diff options
212 files changed, 2829 insertions, 1086 deletions
diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 44e0fe5839..30425404eb 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -7,34 +7,34 @@ env: jobs: ci-config: - runs-on: ubuntu-latest - outputs: - enabled: ${{ steps.check-ref.outputs.enabled }} - steps: - - name: try to clone ci-config branch - continue-on-error: true - run: | - git -c protocol.version=2 clone \ - --no-tags \ - --single-branch \ - -b ci-config \ - --depth 1 \ - --no-checkout \ - --filter=blob:none \ - https://github.com/${{ github.repository }} \ - config-repo && - cd config-repo && - git checkout HEAD -- ci/config - - id: check-ref - name: check whether CI is enabled for ref - run: | - enabled=yes - if test -x config-repo/ci/config/allow-ref && - ! config-repo/ci/config/allow-ref '${{ github.ref }}' - then - enabled=no - fi - echo "::set-output name=enabled::$enabled" + runs-on: ubuntu-latest + outputs: + enabled: ${{ steps.check-ref.outputs.enabled }} + steps: + - name: try to clone ci-config branch + continue-on-error: true + run: | + git -c protocol.version=2 clone \ + --no-tags \ + --single-branch \ + -b ci-config \ + --depth 1 \ + --no-checkout \ + --filter=blob:none \ + https://github.com/${{ github.repository }} \ + config-repo && + cd config-repo && + git checkout HEAD -- ci/config + - id: check-ref + name: check whether CI is enabled for ref + run: | + enabled=yes + if test -x config-repo/ci/config/allow-ref && + ! config-repo/ci/config/allow-ref '${{ github.ref }}' + then + enabled=no + fi + echo "::set-output name=enabled::$enabled" windows-build: needs: ci-config diff --git a/Documentation/MyFirstContribution.txt b/Documentation/MyFirstContribution.txt index d85c9b5143..4f85a089ef 100644 --- a/Documentation/MyFirstContribution.txt +++ b/Documentation/MyFirstContribution.txt @@ -319,14 +319,14 @@ function body: ... git_config(git_default_config, NULL); - if (git_config_get_string_const("user.name", &cfg_name) > 0) + if (git_config_get_string_tmp("user.name", &cfg_name) > 0) printf(_("No name is found in config\n")); else printf(_("Your name: %s\n"), cfg_name); ---- `git_config()` will grab the configuration from config files known to Git and -apply standard precedence rules. `git_config_get_string_const()` will look up +apply standard precedence rules. `git_config_get_string_tmp()` will look up a specific key ("user.name") and give you the value. There are a number of single-key lookup functions like this one; you can see them all (and more info about how to use `git_config()`) in `Documentation/technical/api-config.txt`. diff --git a/Documentation/RelNotes/2.29.0.txt b/Documentation/RelNotes/2.29.0.txt index 483ebb4f8d..a020dadab7 100644 --- a/Documentation/RelNotes/2.29.0.txt +++ b/Documentation/RelNotes/2.29.0.txt @@ -19,6 +19,42 @@ UI, Workflows & Features configurable to selectively allow or reject object filtering specification used for partial cloning. + * Stop when "sendmail.*" configuration variables are defined, which + could be a mistaken attempt to define "sendemail.*" variables. + + * The existing backends for "git mergetool" based on variants of vim + have been refactored and then support for "nvim" has been added. + + * "git bisect" learns the "--first-parent" option to find the first + breakage along the first-parent chain. + + * "git log --first-parent -p" showed patches only for single-parent + commits on the first-parent chain; the "--first-parent" option has + been made to imply "-m". Use "--no-diff-merges" to restore the + previous behaviour to omit patches for merge commits. + + * The commit labels used to explain each side of conflicted hunks + placed by the sequencer machinery have been made more readable by + humans. + + * The "--batch-size" option of "git multi-pack-index repack" command + is now used to specify that very small packfiles are collected into + one until the total size roughly exceeds it. + + * The recent addition of SHA-256 support is marked as experimental in + the documentation. + + * "git fetch" learned --no-write-fetch-head option to avoid writing + the FETCH_HEAD file. + + * Command line completion (in contrib/) usually omits redundant, + deprecated and/or dangerous options from its output; it learned to + optionally include all of them. + + * The output from the "diff" family of the commands had abbreviated + object names of blobs involved in the patch, but its length was not + affected by the --abbrev option. Now it is. + Performance, Internal Implementation, Development Support etc. @@ -52,7 +88,9 @@ Performance, Internal Implementation, Development Support etc. to a certain degree. It has been renamed to "strvec" to reduce the barrier to adoption. - * The final leg of SHA-256 transition. + * The final leg of SHA-256 transition plus doc updates. Note that + there is no inter-operability between SHA-1 and SHA-256 + repositories yet. * CMake support to build with MSVC for Windows bypassing the Makefile. @@ -60,6 +98,29 @@ Performance, Internal Implementation, Development Support etc. easier to mark object existence checks that do and don't want to trigger lazy fetches, and a few such checks are converted using it. + * A no-op replacement function implemented as a C preprocessor macro + does not perform as good a job as one implemented as a "static + inline" function in catching errors in parameters; replace the + former with the latter in <git-compat-util.h> header. + + * Test framework update. + (merge d572f52a64 es/test-cmp-typocatcher later to maint). + + * Updates to "git merge" tests, in preparation for a new merge + strategy backend. + + * midx and commit-graph files now use the byte defined in their file + format specification for identifying the hash function used for + object names. + + * The FETCH_HEAD is now always read from the filesystem regardless of + the ref backend in use, as its format is much richer than the + normal refs, and written directly by "git fetch" as a plain file.. + + * A handful of places in in-tree code still relied on being able to + execute the git subcommands, especially built-ins, in "git-foo" + form, which have been corrected. + Fixes since v2.28 ----------------- @@ -110,6 +171,98 @@ Fixes since v2.28 it is. (merge 11bc12ae1e rp/blame-first-parent-doc later to maint). + * The logic to find the ref transaction hook script attempted to + cache the path to the found hook without realizing that it needed + to keep a copied value, as the API it used returned a transitory + buffer space. This has been corrected. + (merge 09b2aa30c9 ps/ref-transaction-hook later to maint). + + * Recent versions of "git diff-files" shows a diff between the index + and the working tree for "intent-to-add" paths as a "new file" + patch; "git apply --cached" should be able to take "git diff-files" + and should act as an equivalent to "git add" for the path, but the + command failed to do so for such a path. + (merge 4c025c667e rp/apply-cached-with-i-t-a later to maint). + + * "git diff [<tree-ish>] $path" for a $path that is marked with i-t-a + bit was not showing the mode bits from the working tree. + (merge cb0dd22b82 rp/ita-diff-modefix later to maint). + + * Ring buffer with size 4 used for bin-hex translation resulted in a + wrong object name in the sequencer's todo output, which has been + corrected. + (merge 5da69c0dac ak/sequencer-fix-find-uniq-abbrev later to maint). + + * When given more than one target line ranges, "git blame -La,b + -Lc,d" was over-eager to coalesce groups of original lines and + showed incorrect results, which has been corrected. + (merge c2ebaa27d6 jk/blame-coalesce-fix later to maint). + + * The regexp to identify the function boundary for FORTRAN programs + has been updated. + (merge 75c3b6b2e8 pb/userdiff-fortran-update later to maint). + + * A few end-user facing messages have been updated to be + hash-algorithm agnostic. + (merge 4279000d3e jc/object-names-are-not-sha-1 later to maint). + + * "unlink" emulation on MinGW has been optimized. + (merge 680e0b4524 jh/mingw-unlink later to maint). + + * The purpose of "git init --separate-git-dir" is to initialize a + new project with the repository separate from the working tree, + or, in the case of an existing project, to move the repository + (the .git/ directory) out of the working tree. It does not make + sense to use --separate-git-dir with a bare repository for which + there is no working tree, so disallow its use with bare + repositories. + (merge ccf236a23a es/init-no-separate-git-dir-in-bare later to maint). + + * "ls-files -o" mishandled the top-level directory of another git + working tree that hangs in the current git working tree. + (merge ab282aa548 en/dir-nonbare-embedded later to maint). + + * Fix some incorrect UNLEAK() annotations. + (merge 3e19816dc0 jk/unleak-fixes later to maint). + + * Use more buffered I/O where we used to call many small write(2)s. + (merge a698d67b08 rs/more-buffered-io later to maint). + + * The patch-id computation did not ignore the "incomplete last line" + marker like whitespaces. + (merge 82a62015a7 rs/patch-id-with-incomplete-line later to maint). + + * Updates into a lazy/partial clone with a submodule did not work + well with transfer.fsckobjects set. + + * The parser for "git for-each-ref --format=..." was too loose when + parsing the "%(trailers...)" atom, and forgot that "trailers" and + "trailers:<modifiers>" are the only two allowed forms, which has + been corrected. + (merge 2c22e102f8 hv/ref-filter-trailers-atom-parsing-fix later to maint). + + * Long ago, we decided to use 3 threads by default when running the + index-pack task in parallel, which has been adjusted a bit upwards. + (merge fbff95b67f jk/index-pack-w-more-threads later to maint). + + * "git restore/checkout --no-overlay" with wildcarded pathspec + mistakenly removed matching paths in subdirectories, which has been + corrected. + (merge bfda204ade rs/checkout-no-overlay-pathspec-fix later to maint). + + * The description of --cached/--index options in "git apply --help" + has been updated. + (merge d064702be3 rp/apply-cached-doc later to maint). + + * Feeding "$ZERO_OID" to "git log --ignore-missing --stdin", and + running "git log --ignore-missing $ZERO_OID" fell back to start + digging from HEAD; it has been corrected to become a no-op, like + "git log --tags=no-tag-matches-this-pattern" does. + (merge 04a0e98515 jk/rev-input-given-fix later to maint). + + * Various callers of run_command API has been modernized. + (merge afbdba391e jc/run-command-use-embedded-args later to maint). + * Other code cleanup, docfix, build fix, etc. (merge 84544f2ea3 sk/typofixes later to maint). (merge b17f411ab5 ar/help-guides-doc later to maint). @@ -124,3 +277,11 @@ Fixes since v2.28 (merge de20baf2c9 ny/notes-doc-sample-update later to maint). (merge f649aaaf82 so/rev-parser-errormessage-fix later to maint). (merge 6103d58b7f bc/sha-256-cvs-svn-updates later to maint). + (merge ac900fddb7 ma/stop-progress-null-fix later to maint). + (merge e767963ab6 rs/upload-pack-sigchain-fix later to maint). + (merge a831908599 rs/preserve-merges-unused-code-removal later to maint). + (merge 6dfefe70a9 jb/commit-graph-doc-fix later to maint). + (merge 847b37271e pb/set-url-docfix later to maint). + (merge 748f733d54 mt/checkout-entry-dead-code-removal later to maint). + (merge ce820cbd58 dl/subtree-docs later to maint). + (merge 55fe225dde jk/leakfix later to maint). diff --git a/Documentation/config/sendemail.txt b/Documentation/config/sendemail.txt index 0006faf800..cbc5af42fd 100644 --- a/Documentation/config/sendemail.txt +++ b/Documentation/config/sendemail.txt @@ -61,3 +61,8 @@ sendemail.smtpBatchSize:: sendemail.smtpReloginDelay:: Seconds wait before reconnecting to smtp server. See also the `--relogin-delay` option of linkgit:git-send-email[1]. + +sendemail.forbidSendmailVariables:: + To avoid common misconfiguration mistakes, linkgit:git-send-email[1] + will abort with a warning if any configuration options for "sendmail" + exist. Set this variable to bypass the check. diff --git a/Documentation/diff-options.txt b/Documentation/diff-options.txt index 7987d72b02..573fb9bb71 100644 --- a/Documentation/diff-options.txt +++ b/Documentation/diff-options.txt @@ -73,6 +73,11 @@ ifndef::git-format-patch[] Synonym for `-p --raw`. endif::git-format-patch[] +ifdef::git-log[] +-t:: + Show the tree objects in the diff output. +endif::git-log[] + --indent-heuristic:: Enable the heuristic that shifts diff hunk boundaries to make patches easier to read. This is the default. @@ -441,10 +446,11 @@ endif::git-format-patch[] --abbrev[=<n>]:: Instead of showing the full 40-byte hexadecimal object name in diff-raw format output and diff-tree header - lines, show only a partial prefix. This is - independent of the `--full-index` option above, which controls - the diff-patch output format. Non default number of - digits can be specified with `--abbrev=<n>`. + lines, show only a partial prefix. + In diff-patch output format, `--full-index` takes higher + precedence, i.e. if `--full-index` is specified, full blob + names will be shown regardless of `--abbrev`. + Non default number of digits can be specified with `--abbrev=<n>`. -B[<n>][/<m>]:: --break-rewrites[=[<n>][/<m>]]:: diff --git a/Documentation/fetch-options.txt b/Documentation/fetch-options.txt index 6e2a160a47..e8104c082e 100644 --- a/Documentation/fetch-options.txt +++ b/Documentation/fetch-options.txt @@ -64,6 +64,15 @@ documented in linkgit:git-config[1]. --dry-run:: Show what would be done, without making any changes. +ifndef::git-pull[] +--[no-]write-fetch-head:: + Write the list of remote refs fetched in the `FETCH_HEAD` + file directly under `$GIT_DIR`. This is the default. + Passing `--no-write-fetch-head` from the command line tells + Git not to write the file. Under `--dry-run` option, the + file is never written. +endif::git-pull[] + -f:: --force:: When 'git fetch' is used with `<src>:<dst>` refspec it may @@ -186,7 +195,7 @@ ifndef::git-pull[] endif::git-pull[] --set-upstream:: - If the remote is fetched successfully, pull and add upstream + If the remote is fetched successfully, add upstream (tracking) reference, used by argument-less linkgit:git-pull[1] and other commands. For more information, see `branch.<name>.merge` and `branch.<name>.remote` in diff --git a/Documentation/git-apply.txt b/Documentation/git-apply.txt index b9aa39000f..91d9a8601c 100644 --- a/Documentation/git-apply.txt +++ b/Documentation/git-apply.txt @@ -61,18 +61,18 @@ OPTIONS file and detects errors. Turns off "apply". --index:: - When `--check` is in effect, or when applying the patch - (which is the default when none of the options that - disables it is in effect), make sure the patch is - applicable to what the current index file records. If - the file to be patched in the working tree is not - up to date, it is flagged as an error. This flag also - causes the index file to be updated. + Apply the patch to both the index and the working tree (or + merely check that it would apply cleanly to both if `--check` is + in effect). Note that `--index` expects index entries and + working tree copies for relevant paths to be identical (their + contents and metadata such as file mode must match), and will + raise an error if they are not, even if the patch would apply + cleanly to both the index and the working tree in isolation. --cached:: - Apply a patch without touching the working tree. Instead take the - cached data, apply the patch, and store the result in the index - without using the working tree. This implies `--index`. + Apply the patch to just the index, without touching the working + tree. If `--check` is in effect, merely check that it would + apply cleanly to the index entry. --intent-to-add:: When applying the patch only to the working tree, mark new diff --git a/Documentation/git-bisect.txt b/Documentation/git-bisect.txt index 7586c5a843..0e993e4587 100644 --- a/Documentation/git-bisect.txt +++ b/Documentation/git-bisect.txt @@ -17,7 +17,7 @@ The command takes various subcommands, and different options depending on the subcommand: git bisect start [--term-{old,good}=<term> --term-{new,bad}=<term>] - [--no-checkout] [<bad> [<good>...]] [--] [<paths>...] + [--no-checkout] [--first-parent] [<bad> [<good>...]] [--] [<paths>...] git bisect (bad|new|<term-new>) [<rev>] git bisect (good|old|<term-old>) [<rev>...] git bisect terms [--term-good | --term-bad] @@ -365,6 +365,17 @@ does not require a checked out tree. + If the repository is bare, `--no-checkout` is assumed. +--first-parent:: ++ +Follow only the first parent commit upon seeing a merge commit. ++ +In detecting regressions introduced through the merging of a branch, the merge +commit will be identified as introduction of the bug and its ancestors will be +ignored. ++ +This option is particularly useful in avoiding false positives when a merged +branch contained broken or non-buildable commits, but the merge itself was OK. + EXAMPLES -------- diff --git a/Documentation/git-index-pack.txt b/Documentation/git-index-pack.txt index ac74d058e0..af0c26232c 100644 --- a/Documentation/git-index-pack.txt +++ b/Documentation/git-index-pack.txt @@ -100,6 +100,8 @@ OPTIONS value is set or outside a repository. + This option cannot be used with --stdin. ++ +include::object-format-disclaimer.txt[] NOTES ----- diff --git a/Documentation/git-init.txt b/Documentation/git-init.txt index ddfe265da5..f35f70f13d 100644 --- a/Documentation/git-init.txt +++ b/Documentation/git-init.txt @@ -53,6 +53,8 @@ current working directory. Specify the given object format (hash algorithm) for the repository. The valid values are 'sha1' and (if enabled) 'sha256'. 'sha1' is the default. ++ +include::object-format-disclaimer.txt[] --template=<template_directory>:: diff --git a/Documentation/git-log.txt b/Documentation/git-log.txt index 3fd26d5212..2b8ac5ff88 100644 --- a/Documentation/git-log.txt +++ b/Documentation/git-log.txt @@ -114,8 +114,51 @@ include::rev-list-options.txt[] include::pretty-formats.txt[] -COMMON DIFF OPTIONS -------------------- +DIFF FORMATTING +--------------- + +By default, `git log` does not generate any diff output. The options +below can be used to show the changes made by each commit. + +Note that unless one of `-c`, `--cc`, or `-m` is given, merge commits +will never show a diff, even if a diff format like `--patch` is +selected, nor will they match search options like `-S`. The exception is +when `--first-parent` is in use, in which merges are treated like normal +single-parent commits (this can be overridden by providing a +combined-diff option or with `--no-diff-merges`). + +-c:: + With this option, diff output for a merge commit + shows the differences from each of the parents to the merge result + simultaneously instead of showing pairwise diff between a parent + and the result one at a time. Furthermore, it lists only files + which were modified from all parents. + +--cc:: + This flag implies the `-c` option and further compresses the + patch output by omitting uninteresting hunks whose contents in + the parents have only two variants and the merge result picks + one of them without modification. + +--combined-all-paths:: + This flag causes combined diffs (used for merge commits) to + list the name of the file from all parents. It thus only has + effect when -c or --cc are specified, and is likely only + useful if filename changes are detected (i.e. when either + rename or copy detection have been requested). + +-m:: + This flag makes the merge commits show the full diff like + regular commits; for each merge parent, a separate log entry + and diff is generated. An exception is that only diff against + the first parent is shown when `--first-parent` option is given; + in that case, the output represents the changes the merge + brought _into_ the then-current branch. + +--diff-merges=off:: +--no-diff-merges:: + Disable output of diffs for merge commits (default). Useful to + override `-m`, `-c`, or `--cc`. :git-log: 1 include::diff-options.txt[] diff --git a/Documentation/git-multi-pack-index.txt b/Documentation/git-multi-pack-index.txt index 0c6619493c..eb0caa0439 100644 --- a/Documentation/git-multi-pack-index.txt +++ b/Documentation/git-multi-pack-index.txt @@ -51,11 +51,12 @@ repack:: multi-pack-index, then divide by the total number of objects in the pack and multiply by the pack size. We select packs with expected size below the batch size until the set of packs have - total expected size at least the batch size. If the total size - does not reach the batch size, then do nothing. If a new pack- - file is created, rewrite the multi-pack-index to reference the - new pack-file. A later run of 'git multi-pack-index expire' will - delete the pack-files that were part of this batch. + total expected size at least the batch size, or all pack-files + are considered. If only one pack-file is selected, then do + nothing. If a new pack-file is created, rewrite the + multi-pack-index to reference the new pack-file. A later run of + 'git multi-pack-index expire' will delete the pack-files that + were part of this batch. + If `repack.packKeptObjects` is `false`, then any pack-files with an associated `.keep` file will not be selected for the batch to repack. diff --git a/Documentation/git-rebase.txt b/Documentation/git-rebase.txt index 374d2486f7..e8df8d5214 100644 --- a/Documentation/git-rebase.txt +++ b/Documentation/git-rebase.txt @@ -459,17 +459,38 @@ with `--keep-base` in order to drop those commits from your branch. See also INCOMPATIBLE OPTIONS below. --ignore-whitespace:: + Ignore whitespace differences when trying to reconcile +differences. Currently, each backend implements an approximation of +this behavior: ++ +apply backend: When applying a patch, ignore changes in whitespace in +context lines. Unfortunately, this means that if the "old" lines being +replaced by the patch differ only in whitespace from the existing +file, you will get a merge conflict instead of a successful patch +application. ++ +merge backend: Treat lines with only whitespace changes as unchanged +when merging. Unfortunately, this means that any patch hunks that were +intended to modify whitespace and nothing else will be dropped, even +if the other side had no changes that conflicted. + --whitespace=<option>:: - These flags are passed to the 'git apply' program + This flag is passed to the 'git apply' program (see linkgit:git-apply[1]) that applies the patch. Implies --apply. + See also INCOMPATIBLE OPTIONS below. --committer-date-is-author-date:: + Instead of using the current time as the committer date, use + the author date of the commit being rebased as the committer + date. This option implies `--force-rebase`. + --ignore-date:: - These flags are passed to 'git am' to easily change the dates - of the rebased commits (see linkgit:git-am[1]). +--reset-author-date:: + Instead of using the author date of the original commit, use + the current time as the author date of the rebased commit. This + option implies `--force-rebase`. + See also INCOMPATIBLE OPTIONS below. @@ -607,9 +628,6 @@ INCOMPATIBLE OPTIONS The following options: * --apply - * --committer-date-is-author-date - * --ignore-date - * --ignore-whitespace * --whitespace * -C @@ -636,6 +654,9 @@ In addition, the following pairs of options are incompatible: * --preserve-merges and --signoff * --preserve-merges and --rebase-merges * --preserve-merges and --empty= + * --preserve-merges and --ignore-whitespace + * --preserve-merges and --committer-date-is-author-date + * --preserve-merges and --ignore-date * --keep-base and --onto * --keep-base and --root * --fork-point and --root diff --git a/Documentation/git-show-index.txt b/Documentation/git-show-index.txt index 39b1d8eaa1..e49318a5a0 100644 --- a/Documentation/git-show-index.txt +++ b/Documentation/git-show-index.txt @@ -44,6 +44,8 @@ OPTIONS valid values are 'sha1' and (if enabled) 'sha256'. The default is the algorithm for the current repository (set by `extensions.objectFormat`), or 'sha1' if no value is set or outside a repository.. ++ +include::object-format-disclaimer.txt[] GIT --- diff --git a/Documentation/git-update-ref.txt b/Documentation/git-update-ref.txt index 3e737c2360..d401234b03 100644 --- a/Documentation/git-update-ref.txt +++ b/Documentation/git-update-ref.txt @@ -148,12 +148,13 @@ still see a subset of the modifications. LOGGING UPDATES --------------- -If config parameter "core.logAllRefUpdates" is true and the ref is one under -"refs/heads/", "refs/remotes/", "refs/notes/", or the symbolic ref HEAD; or -the file "$GIT_DIR/logs/<ref>" exists then `git update-ref` will append -a line to the log file "$GIT_DIR/logs/<ref>" (dereferencing all -symbolic refs before creating the log name) describing the change -in ref value. Log lines are formatted as: +If config parameter "core.logAllRefUpdates" is true and the ref is one +under "refs/heads/", "refs/remotes/", "refs/notes/", or a pseudoref +like HEAD or ORIG_HEAD; or the file "$GIT_DIR/logs/<ref>" exists then +`git update-ref` will append a line to the log file +"$GIT_DIR/logs/<ref>" (dereferencing all symbolic refs before creating +the log name) describing the change in ref value. Log lines are +formatted as: oldsha1 SP newsha1 SP committer LF diff --git a/Documentation/git.txt b/Documentation/git.txt index 81349a84e7..2fc92586b5 100644 --- a/Documentation/git.txt +++ b/Documentation/git.txt @@ -504,7 +504,8 @@ double-quotes and respecting backslash escapes. E.g., the value If this variable is set, the default hash algorithm for new repositories will be set to this value. This value is currently ignored when cloning; the setting of the remote repository - is used instead. The default is "sha1". + is used instead. The default is "sha1". THIS VARIABLE IS + EXPERIMENTAL! See `--object-format` in linkgit:git-init[1]. Git Commits ~~~~~~~~~~~ diff --git a/Documentation/object-format-disclaimer.txt b/Documentation/object-format-disclaimer.txt new file mode 100644 index 0000000000..4cb106f0d1 --- /dev/null +++ b/Documentation/object-format-disclaimer.txt @@ -0,0 +1,6 @@ +THIS OPTION IS EXPERIMENTAL! SHA-256 support is experimental and still +in an early stage. A SHA-256 repository will in general not be able to +share work with "regular" SHA-1 repositories. It should be assumed +that, e.g., Git internal file formats in relation to SHA-256 +repositories may change in backwards-incompatible ways. Only use +`--object-format=sha256` for testing purposes. diff --git a/Documentation/rev-list-options.txt b/Documentation/rev-list-options.txt index b01b2b6773..002379056a 100644 --- a/Documentation/rev-list-options.txt +++ b/Documentation/rev-list-options.txt @@ -128,8 +128,7 @@ parents) and `--max-parents=-1` (negative numbers denote no upper limit). because merges into a topic branch tend to be only about adjusting to updated upstream from time to time, and this option allows you to ignore the individual commits - brought in to your history by such a merge. Cannot be - combined with --bisect. + brought in to your history by such a merge. --not:: Reverses the meaning of the '{caret}' prefix (or lack thereof) @@ -207,7 +206,7 @@ ifndef::git-rev-list[] Pretend as if the bad bisection ref `refs/bisect/bad` was listed and as if it was followed by `--not` and the good bisection refs `refs/bisect/good-*` on the command - line. Cannot be combined with --first-parent. + line. endif::git-rev-list[] --stdin:: @@ -743,7 +742,7 @@ outputs 'midpoint', the output of the two commands would be of roughly the same length. Finding the change which introduces a regression is thus reduced to a binary search: repeatedly generate and test new 'midpoint's until the commit chain is of length -one. Cannot be combined with --first-parent. +one. --bisect-vars:: This calculates the same as `--bisect`, except that refs in @@ -1117,48 +1116,3 @@ ifdef::git-rev-list[] by a tab. endif::git-rev-list[] endif::git-shortlog[] - -ifndef::git-shortlog[] -ifndef::git-rev-list[] -Diff Formatting -~~~~~~~~~~~~~~~ - -Listed below are options that control the formatting of diff output. -Some of them are specific to linkgit:git-rev-list[1], however other diff -options may be given. See linkgit:git-diff-files[1] for more options. - --c:: - With this option, diff output for a merge commit - shows the differences from each of the parents to the merge result - simultaneously instead of showing pairwise diff between a parent - and the result one at a time. Furthermore, it lists only files - which were modified from all parents. - ---cc:: - This flag implies the `-c` option and further compresses the - patch output by omitting uninteresting hunks whose contents in - the parents have only two variants and the merge result picks - one of them without modification. - ---combined-all-paths:: - This flag causes combined diffs (used for merge commits) to - list the name of the file from all parents. It thus only has - effect when -c or --cc are specified, and is likely only - useful if filename changes are detected (i.e. when either - rename or copy detection have been requested). - --m:: - This flag makes the merge commits show the full diff like - regular commits; for each merge parent, a separate log entry - and diff is generated. An exception is that only diff against - the first parent is shown when `--first-parent` option is given; - in that case, the output represents the changes the merge - brought _into_ the then-current branch. - --r:: - Show recursive diffs. - --t:: - Show the tree objects in the diff output. This implies `-r`. -endif::git-rev-list[] -endif::git-shortlog[] diff --git a/Documentation/technical/commit-graph-format.txt b/Documentation/technical/commit-graph-format.txt index 440541045d..6ddbceba15 100644 --- a/Documentation/technical/commit-graph-format.txt +++ b/Documentation/technical/commit-graph-format.txt @@ -42,8 +42,13 @@ HEADER: 1-byte version number: Currently, the only valid version is 1. - 1-byte Hash Version (1 = SHA-1) - We infer the hash length (H) from this value. + 1-byte Hash Version + We infer the hash length (H) from this value: + 1 => SHA-1 + 2 => SHA-256 + If the hash type does not match the repository's hash algorithm, the + commit-graph file should be ignored with a warning presented to the + user. 1-byte number (C) of "chunks" diff --git a/Documentation/technical/commit-graph.txt b/Documentation/technical/commit-graph.txt index 808fa30b99..f14a7659aa 100644 --- a/Documentation/technical/commit-graph.txt +++ b/Documentation/technical/commit-graph.txt @@ -210,12 +210,12 @@ file. +---------------------+ | | +-----------------------+ +---------------------+ - | graph-{hash2} |->| | + | graph-{hash2} |->| | +-----------------------+ +---------------------+ | | | +-----------------------+ +---------------------+ | | | | - | graph-{hash1} |->| | + | graph-{hash1} |->| | | | | | +-----------------------+ +---------------------+ | tmp_graphXXX @@ -223,7 +223,7 @@ file. | | | | | | - | graph-{hash0} | + | graph-{hash0} | | | | | | | diff --git a/Documentation/technical/hash-function-transition.txt b/Documentation/technical/hash-function-transition.txt index 5b2db3be1e..6fd20ebbc2 100644 --- a/Documentation/technical/hash-function-transition.txt +++ b/Documentation/technical/hash-function-transition.txt @@ -650,7 +650,6 @@ Some initial steps can be implemented independently of one another: The first user-visible change is the introduction of the objectFormat extension (without compatObjectFormat). This requires: -- implementing the loose-object-idx - teaching fsck about this mode of operation - using the hash function API (vtable) when computing object names - signing objects and verifying signatures @@ -658,6 +657,7 @@ extension (without compatObjectFormat). This requires: repository Next comes introduction of compatObjectFormat: +- implementing the loose-object-idx - translating object names between object formats - translating object content between object formats - generating and verifying signatures in the compat format diff --git a/Documentation/technical/http-protocol.txt b/Documentation/technical/http-protocol.txt index 51a79e63de..96d89ea9b2 100644 --- a/Documentation/technical/http-protocol.txt +++ b/Documentation/technical/http-protocol.txt @@ -401,8 +401,9 @@ at all in the request stream: The stream is terminated by a pkt-line flush (`0000`). A single "want" or "have" command MUST have one hex formatted -SHA-1 as its value. Multiple SHA-1s MUST be sent by sending -multiple commands. +object name as its value. Multiple object names MUST be sent by sending +multiple commands. Object names MUST be given using the object format +negotiated through the `object-format` capability (default SHA-1). The `have` list is created by popping the first 32 commits from `c_pending`. Less can be supplied if `c_pending` empties. diff --git a/Documentation/technical/index-format.txt b/Documentation/technical/index-format.txt index faa25c5c52..f9a3644711 100644 --- a/Documentation/technical/index-format.txt +++ b/Documentation/technical/index-format.txt @@ -3,8 +3,11 @@ Git index format == The Git index file has the following format - All binary numbers are in network byte order. Version 2 is described - here unless stated otherwise. + All binary numbers are in network byte order. + In a repository using the traditional SHA-1, checksums and object IDs + (object names) mentioned below are all computed using SHA-1. Similarly, + in SHA-256 repositories, these values are computed using SHA-256. + Version 2 is described here unless stated otherwise. - A 12-byte header consisting of @@ -32,8 +35,7 @@ Git index format Extension data - - 160-bit SHA-1 over the content of the index file before this - checksum. + - Hash checksum over the content of the index file before this checksum. == Index entry @@ -80,7 +82,7 @@ Git index format 32-bit file size This is the on-disk size from stat(2), truncated to 32-bit. - 160-bit SHA-1 for the represented object + Object name for the represented object A 16-bit 'flags' field split into (high to low bits) @@ -160,8 +162,8 @@ Git index format - A newline (ASCII 10); and - - 160-bit object name for the object that would result from writing - this span of index as a tree. + - Object name for the object that would result from writing this span + of index as a tree. An entry can be in an invalidated state and is represented by having a negative number in the entry_count field. In this case, there is no @@ -198,7 +200,7 @@ Git index format stage 1 to 3 (a missing stage is represented by "0" in this field); and - - At most three 160-bit object names of the entry in stages from 1 to 3 + - At most three object names of the entry in stages from 1 to 3 (nothing is written for a missing stage). === Split index @@ -211,8 +213,8 @@ Git index format The extension consists of: - - 160-bit SHA-1 of the shared index file. The shared index file path - is $GIT_DIR/sharedindex.<SHA-1>. If all 160 bits are zero, the + - Hash of the shared index file. The shared index file path + is $GIT_DIR/sharedindex.<hash>. If all bits are zero, the index does not require a shared index file. - An ewah-encoded delete bitmap, each bit represents an entry in the @@ -253,10 +255,10 @@ Git index format - 32-bit dir_flags (see struct dir_struct) - - 160-bit SHA-1 of $GIT_DIR/info/exclude. Null SHA-1 means the file + - Hash of $GIT_DIR/info/exclude. A null hash means the file does not exist. - - 160-bit SHA-1 of core.excludesfile. Null SHA-1 means the file does + - Hash of core.excludesfile. A null hash means the file does not exist. - NUL-terminated string of per-dir exclude file name. This usually @@ -285,13 +287,13 @@ The remaining data of each directory block is grouped by type: - An ewah bitmap, the n-th bit records "check-only" bit of read_directory_recursive() for the n-th directory. - - An ewah bitmap, the n-th bit indicates whether SHA-1 and stat data + - An ewah bitmap, the n-th bit indicates whether hash and stat data is valid for the n-th directory and exists in the next data. - An array of stat data. The n-th data corresponds with the n-th "one" bit in the previous ewah bitmap. - - An array of SHA-1. The n-th SHA-1 corresponds with the n-th "one" bit + - An array of hashes. The n-th hash corresponds with the n-th "one" bit in the previous ewah bitmap. - One NUL. @@ -330,12 +332,12 @@ The remaining data of each directory block is grouped by type: - 32-bit offset to the end of the index entries - - 160-bit SHA-1 over the extension types and their sizes (but not + - Hash over the extension types and their sizes (but not their contents). E.g. if we have "TREE" extension that is N-bytes long, "REUC" extension that is M-bytes long, followed by "EOIE", then the hash would be: - SHA-1("TREE" + <binary representation of N> + + Hash("TREE" + <binary representation of N> + "REUC" + <binary representation of M>) == Index Entry Offset Table diff --git a/Documentation/technical/pack-format.txt b/Documentation/technical/pack-format.txt index d3a142c652..f96b2e605f 100644 --- a/Documentation/technical/pack-format.txt +++ b/Documentation/technical/pack-format.txt @@ -1,6 +1,12 @@ Git pack format =============== +== Checksums and object IDs + +In a repository using the traditional SHA-1, pack checksums, index checksums, +and object IDs (object names) mentioned below are all computed using SHA-1. +Similarly, in SHA-256 repositories, these values are computed using SHA-256. + == pack-*.pack files have the following format: - A header appears at the beginning and consists of the following: @@ -26,7 +32,7 @@ Git pack format (deltified representation) n-byte type and length (3-bit type, (n-1)*7+4-bit length) - 20-byte base object name if OBJ_REF_DELTA or a negative relative + base object name if OBJ_REF_DELTA or a negative relative offset from the delta object's position in the pack if this is an OBJ_OFS_DELTA object compressed delta data @@ -34,7 +40,7 @@ Git pack format Observation: length of each object is encoded in a variable length format and is not constrained to 32-bit or anything. - - The trailer records 20-byte SHA-1 checksum of all of the above. + - The trailer records a pack checksum of all of the above. === Object types @@ -58,8 +64,8 @@ ofs-delta and ref-delta, which is only valid in a pack file. Both ofs-delta and ref-delta store the "delta" to be applied to another object (called 'base object') to reconstruct the object. The -difference between them is, ref-delta directly encodes 20-byte base -object name. If the base object is in the same pack, ofs-delta encodes +difference between them is, ref-delta directly encodes base object +name. If the base object is in the same pack, ofs-delta encodes the offset of the base object in the pack instead. The base object could also be deltified if it's in the same pack. @@ -143,14 +149,14 @@ This is the instruction reserved for future expansion. object is stored in the packfile as the offset from the beginning. - 20-byte object name. + one object name of the appropriate size. - The file is concluded with a trailer: - A copy of the 20-byte SHA-1 checksum at the end of - corresponding packfile. + A copy of the pack checksum at the end of the corresponding + packfile. - 20-byte SHA-1-checksum of all of the above. + Index checksum of all of the above. Pack Idx file: @@ -198,7 +204,7 @@ Pack file entry: <+ If it is not DELTA, then deflated bytes (the size above is the size before compression). If it is REF_DELTA, then - 20-byte base object name SHA-1 (the size above is the + base object name (the size above is the size of the delta data that follows). delta data, deflated. If it is OFS_DELTA, then @@ -227,9 +233,9 @@ Pack file entry: <+ - A 256-entry fan-out table just like v1. - - A table of sorted 20-byte SHA-1 object names. These are - packed together without offset values to reduce the cache - footprint of the binary search for a specific object name. + - A table of sorted object names. These are packed together + without offset values to reduce the cache footprint of the + binary search for a specific object name. - A table of 4-byte CRC32 values of the packed object data. This is new in v2 so compressed data can be copied directly @@ -248,10 +254,10 @@ Pack file entry: <+ - The same trailer as a v1 pack file: - A copy of the 20-byte SHA-1 checksum at the end of + A copy of the pack checksum at the end of corresponding packfile. - 20-byte SHA-1-checksum of all of the above. + Index checksum of all of the above. == multi-pack-index (MIDX) files have the following format: @@ -273,7 +279,12 @@ HEADER: Git only writes or recognizes version 1. 1-byte Object Id Version - Git only writes or recognizes version 1 (SHA1). + We infer the length of object IDs (OIDs) from this value: + 1 => SHA-1 + 2 => SHA-256 + If the hash type does not match the repository's hash algorithm, + the multi-pack-index file should be ignored with a warning + presented to the user. 1-byte number of "chunks" @@ -329,4 +340,4 @@ CHUNK DATA: TRAILER: - 20-byte SHA1-checksum of the above contents. + Index checksum of the above contents. diff --git a/Documentation/technical/protocol-capabilities.txt b/Documentation/technical/protocol-capabilities.txt index 36ccd14f97..124d716807 100644 --- a/Documentation/technical/protocol-capabilities.txt +++ b/Documentation/technical/protocol-capabilities.txt @@ -324,15 +324,19 @@ allow-tip-sha1-in-want ---------------------- If the upload-pack server advertises this capability, fetch-pack may -send "want" lines with SHA-1s that exist at the server but are not -advertised by upload-pack. +send "want" lines with object names that exist at the server but are not +advertised by upload-pack. For historical reasons, the name of this +capability contains "sha1". Object names are always given using the +object format negotiated through the 'object-format' capability. allow-reachable-sha1-in-want ---------------------------- If the upload-pack server advertises this capability, fetch-pack may -send "want" lines with SHA-1s that exist at the server but are not -advertised by upload-pack. +send "want" lines with object names that exist at the server but are not +advertised by upload-pack. For historical reasons, the name of this +capability contains "sha1". Object names are always given using the +object format negotiated through the 'object-format' capability. push-cert=<nonce> ----------------- diff --git a/Documentation/technical/shallow.txt b/Documentation/technical/shallow.txt index 01dedfe9ff..f3738baa0f 100644 --- a/Documentation/technical/shallow.txt +++ b/Documentation/technical/shallow.txt @@ -13,7 +13,7 @@ pretend as if they are root commits (e.g. "git log" traversal stops after showing them; "git fsck" does not complain saying the commits listed on their "parent" lines do not exist). -Each line contains exactly one SHA-1. When read, a commit_graft +Each line contains exactly one object name. When read, a commit_graft will be constructed, which has nr_parent < 0 to make it easier to discern from user provided grafts. @@ -30,8 +30,8 @@ struct gitdiff_data { static void git_apply_config(void) { - git_config_get_string_const("apply.whitespace", &apply_default_whitespace); - git_config_get_string_const("apply.ignorewhitespace", &apply_default_ignorewhitespace); + git_config_get_string("apply.whitespace", &apply_default_whitespace); + git_config_get_string("apply.ignorewhitespace", &apply_default_ignorewhitespace); git_config(git_xmerge_config, NULL); } @@ -3740,6 +3740,7 @@ static int check_preimage(struct apply_state *state, #define EXISTS_IN_INDEX 1 #define EXISTS_IN_WORKTREE 2 +#define EXISTS_IN_INDEX_AS_ITA 3 static int check_to_create(struct apply_state *state, const char *new_name, @@ -3747,10 +3748,23 @@ static int check_to_create(struct apply_state *state, { struct stat nst; - if (state->check_index && - index_name_pos(state->repo->index, new_name, strlen(new_name)) >= 0 && - !ok_if_exists) - return EXISTS_IN_INDEX; + if (state->check_index && (!ok_if_exists || !state->cached)) { + int pos; + + pos = index_name_pos(state->repo->index, new_name, strlen(new_name)); + if (pos >= 0) { + struct cache_entry *ce = state->repo->index->cache[pos]; + + /* allow ITA, as they do not yet exist in the index */ + if (!ok_if_exists && !(ce->ce_flags & CE_INTENT_TO_ADD)) + return EXISTS_IN_INDEX; + + /* ITA entries can never match working tree files */ + if (!state->cached && (ce->ce_flags & CE_INTENT_TO_ADD)) + return EXISTS_IN_INDEX_AS_ITA; + } + } + if (state->cached) return 0; @@ -3935,6 +3949,9 @@ static int check_patch(struct apply_state *state, struct patch *patch) case EXISTS_IN_INDEX: return error(_("%s: already exists in index"), new_name); break; + case EXISTS_IN_INDEX_AS_ITA: + return error(_("%s: does not match index"), new_name); + break; case EXISTS_IN_WORKTREE: return error(_("%s: already exists in working directory"), new_name); @@ -15,6 +15,7 @@ #include "commit-slab.h" #include "commit-reach.h" #include "object-store.h" +#include "dir.h" static struct oid_array good_revs; static struct oid_array skipped_revs; @@ -88,15 +89,16 @@ static inline void weight_set(struct commit_list *elem, int weight) **commit_weight_at(&commit_weight, elem->item) = weight; } -static int count_interesting_parents(struct commit *commit) +static int count_interesting_parents(struct commit *commit, unsigned bisect_flags) { struct commit_list *p; int count; for (count = 0, p = commit->parents; p; p = p->next) { - if (p->item->object.flags & UNINTERESTING) - continue; - count++; + if (!(p->item->object.flags & UNINTERESTING)) + count++; + if (bisect_flags & FIND_BISECTION_FIRST_PARENT_ONLY) + break; } return count; } @@ -135,7 +137,7 @@ static void show_list(const char *debug, int counted, int nr, for (p = list; p; p = p->next) { struct commit_list *pp; struct commit *commit = p->item; - unsigned flags = commit->object.flags; + unsigned commit_flags = commit->object.flags; enum object_type type; unsigned long size; char *buf = read_object_file(&commit->object.oid, &type, @@ -144,9 +146,9 @@ static void show_list(const char *debug, int counted, int nr, int subject_len; fprintf(stderr, "%c%c%c ", - (flags & TREESAME) ? ' ' : 'T', - (flags & UNINTERESTING) ? 'U' : ' ', - (flags & COUNTED) ? 'C' : ' '); + (commit_flags & TREESAME) ? ' ' : 'T', + (commit_flags & UNINTERESTING) ? 'U' : ' ', + (commit_flags & COUNTED) ? 'C' : ' '); if (*commit_weight_at(&commit_weight, p->item)) fprintf(stderr, "%3d", weight(p)); else @@ -171,9 +173,9 @@ static struct commit_list *best_bisection(struct commit_list *list, int nr) best = list; for (p = list; p; p = p->next) { int distance; - unsigned flags = p->item->object.flags; + unsigned commit_flags = p->item->object.flags; - if (flags & TREESAME) + if (commit_flags & TREESAME) continue; distance = weight(p); if (nr - distance < distance) @@ -212,9 +214,9 @@ static struct commit_list *best_bisection_sorted(struct commit_list *list, int n for (p = list, cnt = 0; p; p = p->next) { int distance; - unsigned flags = p->item->object.flags; + unsigned commit_flags = p->item->object.flags; - if (flags & TREESAME) + if (commit_flags & TREESAME) continue; distance = weight(p); if (nr - distance < distance) @@ -259,7 +261,7 @@ static struct commit_list *best_bisection_sorted(struct commit_list *list, int n */ static struct commit_list *do_find_bisection(struct commit_list *list, int nr, int *weights, - int find_all) + unsigned bisect_flags) { int n, counted; struct commit_list *p; @@ -268,12 +270,12 @@ static struct commit_list *do_find_bisection(struct commit_list *list, for (n = 0, p = list; p; p = p->next) { struct commit *commit = p->item; - unsigned flags = commit->object.flags; + unsigned commit_flags = commit->object.flags; *commit_weight_at(&commit_weight, p->item) = &weights[n++]; - switch (count_interesting_parents(commit)) { + switch (count_interesting_parents(commit, bisect_flags)) { case 0: - if (!(flags & TREESAME)) { + if (!(commit_flags & TREESAME)) { weight_set(p, 1); counted++; show_list("bisection 2 count one", @@ -314,11 +316,13 @@ static struct commit_list *do_find_bisection(struct commit_list *list, continue; if (weight(p) != -2) continue; + if (bisect_flags & FIND_BISECTION_FIRST_PARENT_ONLY) + BUG("shouldn't be calling count-distance in fp mode"); weight_set(p, count_distance(p)); clear_distance(list); /* Does it happen to be at exactly half-way? */ - if (!find_all && halfway(p, nr)) + if (!(bisect_flags & FIND_BISECTION_ALL) && halfway(p, nr)) return p; counted++; } @@ -328,11 +332,14 @@ static struct commit_list *do_find_bisection(struct commit_list *list, while (counted < nr) { for (p = list; p; p = p->next) { struct commit_list *q; - unsigned flags = p->item->object.flags; + unsigned commit_flags = p->item->object.flags; if (0 <= weight(p)) continue; - for (q = p->item->parents; q; q = q->next) { + + for (q = p->item->parents; + q; + q = bisect_flags & FIND_BISECTION_FIRST_PARENT_ONLY ? NULL : q->next) { if (q->item->object.flags & UNINTERESTING) continue; if (0 <= weight(q)) @@ -346,7 +353,7 @@ static struct commit_list *do_find_bisection(struct commit_list *list, * add one for p itself if p is to be counted, * otherwise inherit it from q directly. */ - if (!(flags & TREESAME)) { + if (!(commit_flags & TREESAME)) { weight_set(p, weight(q)+1); counted++; show_list("bisection 2 count one", @@ -356,21 +363,21 @@ static struct commit_list *do_find_bisection(struct commit_list *list, weight_set(p, weight(q)); /* Does it happen to be at exactly half-way? */ - if (!find_all && halfway(p, nr)) + if (!(bisect_flags & FIND_BISECTION_ALL) && halfway(p, nr)) return p; } } show_list("bisection 2 counted all", counted, nr, list); - if (!find_all) + if (!(bisect_flags & FIND_BISECTION_ALL)) return best_bisection(list, nr); else return best_bisection_sorted(list, nr); } void find_bisection(struct commit_list **commit_list, int *reaches, - int *all, int find_all) + int *all, unsigned bisect_flags) { int nr, on_list; struct commit_list *list, *p, *best, *next, *last; @@ -386,16 +393,16 @@ void find_bisection(struct commit_list **commit_list, int *reaches, for (nr = on_list = 0, last = NULL, p = *commit_list; p; p = next) { - unsigned flags = p->item->object.flags; + unsigned commit_flags = p->item->object.flags; next = p->next; - if (flags & UNINTERESTING) { + if (commit_flags & UNINTERESTING) { free(p); continue; } p->next = last; last = p; - if (!(flags & TREESAME)) + if (!(commit_flags & TREESAME)) nr++; on_list++; } @@ -406,9 +413,9 @@ void find_bisection(struct commit_list **commit_list, int *reaches, weights = xcalloc(on_list, sizeof(*weights)); /* Do the real work of finding bisection commit. */ - best = do_find_bisection(list, nr, weights, find_all); + best = do_find_bisection(list, nr, weights, bisect_flags); if (best) { - if (!find_all) { + if (!(bisect_flags & FIND_BISECTION_ALL)) { list->item = best->item; free_commit_list(list->next); best = list; @@ -454,6 +461,7 @@ static GIT_PATH_FUNC(git_path_bisect_run, "BISECT_RUN") static GIT_PATH_FUNC(git_path_bisect_start, "BISECT_START") static GIT_PATH_FUNC(git_path_bisect_log, "BISECT_LOG") static GIT_PATH_FUNC(git_path_bisect_terms, "BISECT_TERMS") +static GIT_PATH_FUNC(git_path_bisect_first_parent, "BISECT_FIRST_PARENT") static GIT_PATH_FUNC(git_path_head_name, "head-name") static void read_bisect_paths(struct strvec *array) @@ -983,7 +991,7 @@ void read_bisect_terms(const char **read_bad, const char **read_good) * If no_checkout is non-zero, the bisection process does not * checkout the trial commit but instead simply updates BISECT_HEAD. */ -enum bisect_error bisect_next_all(struct repository *r, const char *prefix, int no_checkout) +enum bisect_error bisect_next_all(struct repository *r, const char *prefix) { struct rev_info revs; struct commit_list *tried; @@ -991,21 +999,31 @@ enum bisect_error bisect_next_all(struct repository *r, const char *prefix, int enum bisect_error res = BISECT_OK; struct object_id *bisect_rev; char *steps_msg; + int no_checkout = ref_exists("BISECT_HEAD"); + unsigned bisect_flags = 0; read_bisect_terms(&term_bad, &term_good); if (read_bisect_refs()) die(_("reading bisect refs failed")); + if (file_exists(git_path_bisect_first_parent())) + bisect_flags |= FIND_BISECTION_FIRST_PARENT_ONLY; + + if (skipped_revs.nr) + bisect_flags |= FIND_BISECTION_ALL; + res = check_good_are_ancestors_of_bad(r, prefix, no_checkout); if (res) return res; bisect_rev_setup(r, &revs, prefix, "%s", "^%s", 1); + + revs.first_parent_only = !!(bisect_flags & FIND_BISECTION_FIRST_PARENT_ONLY); revs.limited = 1; bisect_common(&revs); - find_bisection(&revs.commits, &reaches, &all, !!skipped_revs.nr); + find_bisection(&revs.commits, &reaches, &all, bisect_flags); revs.commits = managed_skipped(revs.commits, &tried); if (!revs.commits) { @@ -1133,6 +1151,7 @@ int bisect_clean_state(void) unlink_or_warn(git_path_bisect_names()); unlink_or_warn(git_path_bisect_run()); unlink_or_warn(git_path_bisect_terms()); + unlink_or_warn(git_path_bisect_first_parent()); /* Cleanup head-name if it got left by an old version of git-bisect */ unlink_or_warn(git_path_head_name()); /* @@ -12,7 +12,7 @@ struct repository; * best commit, as chosen by `find_all`. */ void find_bisection(struct commit_list **list, int *reaches, int *all, - int find_all); + unsigned bisect_flags); struct commit_list *filter_skipped(struct commit_list *list, struct commit_list **tried, @@ -23,6 +23,9 @@ struct commit_list *filter_skipped(struct commit_list *list, #define BISECT_SHOW_ALL (1<<0) #define REV_LIST_QUIET (1<<1) +#define FIND_BISECTION_ALL (1u<<0) +#define FIND_BISECTION_FIRST_PARENT_ONLY (1u<<1) + struct rev_list_info { struct rev_info *revs; int flags; @@ -58,9 +61,7 @@ enum bisect_error { BISECT_INTERNAL_SUCCESS_MERGE_BASE = -11 }; -enum bisect_error bisect_next_all(struct repository *r, - const char *prefix, - int no_checkout); +enum bisect_error bisect_next_all(struct repository *r, const char *prefix); int estimate_bisect_steps(int all); @@ -1184,6 +1184,7 @@ void blame_coalesce(struct blame_scoreboard *sb) for (ent = sb->ent; ent && (next = ent->next); ent = next) { if (ent->suspect == next->suspect && ent->s_lno + ent->num_lines == next->s_lno && + ent->lno + ent->num_lines == next->lno && ent->ignored == next->ignored && ent->unblamable == next->unblamable) { ent->num_lines += next->num_lines; diff --git a/bugreport.c b/bugreport.c index 09579e268d..7ca0fba1b8 100644 --- a/bugreport.c +++ b/bugreport.c @@ -175,10 +175,8 @@ int cmd_main(int argc, const char **argv) /* fopen doesn't offer us an O_EXCL alternative, except with glibc. */ report = open(report_path.buf, O_CREAT | O_EXCL | O_WRONLY, 0666); - if (report < 0) { - UNLEAK(report_path); + if (report < 0) die(_("couldn't create a new file at '%s'"), report_path.buf); - } if (write_in_full(report, buffer.buf, buffer.len) < 0) die_errno(_("unable to write to %s"), report_path.buf); diff --git a/builtin/add.c b/builtin/add.c index ab39a60a0d..b36a99eb7c 100644 --- a/builtin/add.c +++ b/builtin/add.c @@ -534,11 +534,11 @@ int cmd_add(int argc, const char **argv, const char *prefix) die_in_unpopulated_submodule(&the_index, prefix); die_path_inside_submodule(&the_index, &pathspec); + dir_init(&dir); if (add_new_files) { int baselen; /* Set up the default git porcelain excludes */ - memset(&dir, 0, sizeof(dir)); if (!ignored_too) { dir.flags |= DIR_COLLECT_IGNORED; setup_standard_excludes(&dir); @@ -611,7 +611,7 @@ finish: COMMIT_LOCK | SKIP_IF_UNCHANGED)) die(_("Unable to write new index file")); + dir_clear(&dir); UNLEAK(pathspec); - UNLEAK(dir); return exit_status; } diff --git a/builtin/am.c b/builtin/am.c index dd4e6c2d9b..b5c63ddf1d 100644 --- a/builtin/am.c +++ b/builtin/am.c @@ -98,6 +98,8 @@ struct am_state { char *author_name; char *author_email; char *author_date; + char *committer_name; + char *committer_email; char *msg; size_t msg_len; @@ -130,6 +132,8 @@ struct am_state { */ static void am_state_init(struct am_state *state) { + const char *committer; + struct ident_split id; int gpgsign; memset(state, 0, sizeof(*state)); @@ -150,6 +154,14 @@ static void am_state_init(struct am_state *state) if (!git_config_get_bool("commit.gpgsign", &gpgsign)) state->sign_commit = gpgsign ? "" : NULL; + + committer = git_committer_info(IDENT_STRICT); + if (split_ident_line(&id, committer, strlen(committer)) < 0) + die(_("invalid committer: %s"), committer); + state->committer_name = + xmemdupz(id.name_begin, id.name_end - id.name_begin); + state->committer_email = + xmemdupz(id.mail_begin, id.mail_end - id.mail_end); } /** @@ -161,6 +173,8 @@ static void am_state_release(struct am_state *state) free(state->author_name); free(state->author_email); free(state->author_date); + free(state->committer_name); + free(state->committer_email); free(state->msg); strvec_clear(&state->git_apply_opts); } @@ -1556,7 +1570,7 @@ static void do_commit(const struct am_state *state) struct object_id tree, parent, commit; const struct object_id *old_oid; struct commit_list *parents = NULL; - const char *reflog_msg, *author; + const char *reflog_msg, *author, *committer = NULL; struct strbuf sb = STRBUF_INIT; if (run_hook_le(NULL, "pre-applypatch", NULL)) @@ -1580,11 +1594,15 @@ static void do_commit(const struct am_state *state) IDENT_STRICT); if (state->committer_date_is_author_date) - setenv("GIT_COMMITTER_DATE", - state->ignore_date ? "" : state->author_date, 1); - - if (commit_tree(state->msg, state->msg_len, &tree, parents, &commit, - author, state->sign_commit)) + committer = fmt_ident(state->committer_name, + state->author_email, WANT_COMMITTER_IDENT, + state->ignore_date ? NULL + : state->author_date, + IDENT_STRICT); + + if (commit_tree_extended(state->msg, state->msg_len, &tree, parents, + &commit, author, committer, state->sign_commit, + NULL)) die(_("failed to write commit object")); reflog_msg = getenv("GIT_REFLOG_ACTION"); diff --git a/builtin/bisect--helper.c b/builtin/bisect--helper.c index d19300687f..cdda279b23 100644 --- a/builtin/bisect--helper.c +++ b/builtin/bisect--helper.c @@ -16,9 +16,10 @@ static GIT_PATH_FUNC(git_path_bisect_start, "BISECT_START") static GIT_PATH_FUNC(git_path_bisect_log, "BISECT_LOG") static GIT_PATH_FUNC(git_path_head_name, "head-name") static GIT_PATH_FUNC(git_path_bisect_names, "BISECT_NAMES") +static GIT_PATH_FUNC(git_path_bisect_first_parent, "BISECT_FIRST_PARENT") static const char * const git_bisect_helper_usage[] = { - N_("git bisect--helper --next-all [--no-checkout]"), + N_("git bisect--helper --next-all"), N_("git bisect--helper --write-terms <bad_term> <good_term>"), N_("git bisect--helper --bisect-clean-state"), N_("git bisect--helper --bisect-reset [<commit>]"), @@ -27,7 +28,7 @@ static const char * const git_bisect_helper_usage[] = { N_("git bisect--helper --bisect-next-check <good_term> <bad_term> [<term>]"), N_("git bisect--helper --bisect-terms [--term-good | --term-old | --term-bad | --term-new]"), N_("git bisect--helper --bisect-start [--term-{old,good}=<term> --term-{new,bad}=<term>]" - "[--no-checkout] [<bad> [<good>...]] [--] [<paths>...]"), + " [--no-checkout] [--first-parent] [<bad> [<good>...]] [--] [<paths>...]"), NULL }; @@ -420,9 +421,10 @@ finish: return res; } -static int bisect_start(struct bisect_terms *terms, int no_checkout, - const char **argv, int argc) +static int bisect_start(struct bisect_terms *terms, const char **argv, int argc) { + int no_checkout = 0; + int first_parent_only = 0; int i, has_double_dash = 0, must_write_terms = 0, bad_seen = 0; int flags, pathspec_pos, res = 0; struct string_list revs = STRING_LIST_INIT_DUP; @@ -452,6 +454,8 @@ static int bisect_start(struct bisect_terms *terms, int no_checkout, break; } else if (!strcmp(arg, "--no-checkout")) { no_checkout = 1; + } else if (!strcmp(arg, "--first-parent")) { + first_parent_only = 1; } else if (!strcmp(arg, "--term-good") || !strcmp(arg, "--term-old")) { i++; @@ -576,6 +580,9 @@ static int bisect_start(struct bisect_terms *terms, int no_checkout, */ write_file(git_path_bisect_start(), "%s\n", start_head.buf); + if (first_parent_only) + write_file(git_path_bisect_first_parent(), "\n"); + if (no_checkout) { if (get_oid(start_head.buf, &oid) < 0) { res = error(_("invalid ref: '%s'"), start_head.buf); @@ -631,7 +638,7 @@ int cmd_bisect__helper(int argc, const char **argv, const char *prefix) BISECT_TERMS, BISECT_START } cmdmode = 0; - int no_checkout = 0, res = 0, nolog = 0; + int res = 0, nolog = 0; struct option options[] = { OPT_CMDMODE(0, "next-all", &cmdmode, N_("perform 'git bisect next'"), NEXT_ALL), @@ -653,8 +660,6 @@ int cmd_bisect__helper(int argc, const char **argv, const char *prefix) N_("print out the bisect terms"), BISECT_TERMS), OPT_CMDMODE(0, "bisect-start", &cmdmode, N_("start the bisect session"), BISECT_START), - OPT_BOOL(0, "no-checkout", &no_checkout, - N_("update BISECT_HEAD instead of checking out the current commit")), OPT_BOOL(0, "no-log", &nolog, N_("no log for BISECT_WRITE")), OPT_END() @@ -670,7 +675,7 @@ int cmd_bisect__helper(int argc, const char **argv, const char *prefix) switch (cmdmode) { case NEXT_ALL: - res = bisect_next_all(the_repository, prefix, no_checkout); + res = bisect_next_all(the_repository, prefix); break; case WRITE_TERMS: if (argc != 2) @@ -712,7 +717,7 @@ int cmd_bisect__helper(int argc, const char **argv, const char *prefix) break; case BISECT_START: set_terms(&terms, "bad", "good"); - res = bisect_start(&terms, no_checkout, argv, argc); + res = bisect_start(&terms, argv, argc); break; default: return error("BUG: unknown subcommand '%d'", cmdmode); diff --git a/builtin/blame.c b/builtin/blame.c index 94ef57c1cc..eb513fbe60 100644 --- a/builtin/blame.c +++ b/builtin/blame.c @@ -842,7 +842,7 @@ int cmd_blame(int argc, const char **argv, const char *prefix) const char *contents_from = NULL; const struct option options[] = { OPT_BOOL(0, "incremental", &incremental, N_("Show blame entries as we find them, incrementally")), - OPT_BOOL('b', NULL, &blank_boundary, N_("Show blank SHA-1 for boundary commits (Default: off)")), + OPT_BOOL('b', NULL, &blank_boundary, N_("Do not show object names of boundary commits (Default: off)")), OPT_BOOL(0, "root", &show_root, N_("Do not treat root commits as boundaries (Default: off)")), OPT_BOOL(0, "show-stats", &show_stats, N_("Show work cost statistics")), OPT_BOOL(0, "progress", &show_progress, N_("Force progress reporting")), diff --git a/builtin/check-ignore.c b/builtin/check-ignore.c index ea5d0ae3a6..3c652748d5 100644 --- a/builtin/check-ignore.c +++ b/builtin/check-ignore.c @@ -180,7 +180,7 @@ int cmd_check_ignore(int argc, const char **argv, const char *prefix) if (!no_index && read_cache() < 0) die(_("index file corrupt")); - memset(&dir, 0, sizeof(dir)); + dir_init(&dir); setup_standard_excludes(&dir); if (stdin_paths) { @@ -190,7 +190,7 @@ int cmd_check_ignore(int argc, const char **argv, const char *prefix) maybe_flush_or_die(stdout, "ignore to stdout"); } - clear_directory(&dir); + dir_clear(&dir); return !num_ignored; } diff --git a/builtin/checkout.c b/builtin/checkout.c index 2837195491..6ec82097a2 100644 --- a/builtin/checkout.c +++ b/builtin/checkout.c @@ -1120,8 +1120,10 @@ static void setup_new_branch_info_and_source_tree( if (!check_refname_format(new_branch_info->path, 0) && !read_ref(new_branch_info->path, &branch_rev)) oidcpy(rev, &branch_rev); - else + else { + free((char *)new_branch_info->path); new_branch_info->path = NULL; /* not an existing branch */ + } new_branch_info->commit = lookup_commit_reference_gently(the_repository, rev, 1); if (!new_branch_info->commit) { @@ -1707,6 +1709,8 @@ static int checkout_main(int argc, const char **argv, const char *prefix, die(_("--pathspec-file-nul requires --pathspec-from-file")); } + opts->pathspec.recursive = 1; + if (opts->pathspec.nr) { if (1 < !!opts->writeout_stage + !!opts->force + !!opts->merge) die(_("git checkout: --ours/--theirs, --force and --merge are incompatible when\n" diff --git a/builtin/clean.c b/builtin/clean.c index 5a9c29a558..e53ea52d89 100644 --- a/builtin/clean.c +++ b/builtin/clean.c @@ -667,7 +667,7 @@ static int filter_by_patterns_cmd(void) if (!confirm.len) break; - memset(&dir, 0, sizeof(dir)); + dir_init(&dir); pl = add_pattern_list(&dir, EXC_CMDL, "manual exclude"); ignore_list = strbuf_split_max(&confirm, ' ', 0); @@ -698,7 +698,7 @@ static int filter_by_patterns_cmd(void) } strbuf_list_free(ignore_list); - clear_directory(&dir); + dir_clear(&dir); } strbuf_release(&confirm); @@ -923,7 +923,7 @@ int cmd_clean(int argc, const char **argv, const char *prefix) argc = parse_options(argc, argv, prefix, options, builtin_clean_usage, 0); - memset(&dir, 0, sizeof(dir)); + dir_init(&dir); if (!interactive && !dry_run && !force) { if (config_set) die(_("clean.requireForce set to true and neither -i, -n, nor -f given; " @@ -1021,11 +1021,7 @@ int cmd_clean(int argc, const char **argv, const char *prefix) string_list_append(&del_list, rel); } - for (i = 0; i < dir.nr; i++) - free(dir.entries[i]); - - for (i = 0; i < dir.ignored_nr; i++) - free(dir.ignored[i]); + dir_clear(&dir); if (interactive && del_list.nr > 0) interactive_main_loop(); diff --git a/builtin/commit.c b/builtin/commit.c index 69ac78d5e5..5d91b13a5c 100644 --- a/builtin/commit.c +++ b/builtin/commit.c @@ -847,21 +847,19 @@ static int prepare_to_commit(const char *index_file, const char *prefix, if (cleanup_mode == COMMIT_MSG_CLEANUP_SCISSORS && !merge_contains_scissors) wt_status_add_cut_line(s->fp); - status_printf_ln(s, GIT_COLOR_NORMAL, - whence == FROM_MERGE - ? _("\n" - "It looks like you may be committing a merge.\n" - "If this is not correct, please remove the file\n" - " %s\n" - "and try again.\n") - : _("\n" - "It looks like you may be committing a cherry-pick.\n" - "If this is not correct, please remove the file\n" - " %s\n" - "and try again.\n"), + status_printf_ln( + s, GIT_COLOR_NORMAL, whence == FROM_MERGE ? - git_path_merge_head(the_repository) : - git_path_cherry_pick_head(the_repository)); + _("\n" + "It looks like you may be committing a merge.\n" + "If this is not correct, please run\n" + " git update-ref -d MERGE_HEAD\n" + "and try again.\n") : + _("\n" + "It looks like you may be committing a cherry-pick.\n" + "If this is not correct, please run\n" + " git update-ref -d CHERRY_PICK_HEAD\n" + "and try again.\n")); } fprintf(s->fp, "\n"); @@ -1674,8 +1672,8 @@ int cmd_commit(int argc, const char **argv, const char *prefix) } if (commit_tree_extended(sb.buf, sb.len, &active_cache_tree->oid, - parents, &oid, author_ident.buf, sign_commit, - extra)) { + parents, &oid, author_ident.buf, NULL, + sign_commit, extra)) { rollback_index_files(); die(_("failed to write commit object")); } diff --git a/builtin/fetch.c b/builtin/fetch.c index c49f0e9752..0f23dd4b8c 100644 --- a/builtin/fetch.c +++ b/builtin/fetch.c @@ -56,6 +56,7 @@ static int prune_tags = -1; /* unspecified */ #define PRUNE_TAGS_BY_DEFAULT 0 /* do we prune tags by default? */ static int all, append, dry_run, force, keep, multiple, update_head_ok; +static int write_fetch_head = 1; static int verbosity, deepen_relative, set_upstream; static int progress = -1; static int enable_auto_gc = 1; @@ -162,6 +163,8 @@ static struct option builtin_fetch_options[] = { PARSE_OPT_OPTARG, option_fetch_parse_recurse_submodules), OPT_BOOL(0, "dry-run", &dry_run, N_("dry run")), + OPT_BOOL(0, "write-fetch-head", &write_fetch_head, + N_("write fetched references to the FETCH_HEAD file")), OPT_BOOL('k', "keep", &keep, N_("keep downloaded pack")), OPT_BOOL('u', "update-head-ok", &update_head_ok, N_("allow updating of HEAD ref")), @@ -645,7 +648,7 @@ static void prepare_format_display(struct ref *ref_map) struct ref *rm; const char *format = "full"; - git_config_get_string_const("fetch.output", &format); + git_config_get_string_tmp("fetch.output", &format); if (!strcasecmp(format, "full")) compact_format = 0; else if (!strcasecmp(format, "compact")) @@ -893,7 +896,9 @@ static int store_updated_refs(const char *raw_url, const char *remote_name, const char *what, *kind; struct ref *rm; char *url; - const char *filename = dry_run ? "/dev/null" : git_path_fetch_head(the_repository); + const char *filename = (!write_fetch_head + ? "/dev/null" + : git_path_fetch_head(the_repository)); int want_status; int summary_width = transport_summary_width(ref_map); @@ -1327,7 +1332,7 @@ static int do_fetch(struct transport *transport, } /* if not appending, truncate FETCH_HEAD */ - if (!append && !dry_run) { + if (!append && write_fetch_head) { retcode = truncate_fetch_head(); if (retcode) goto cleanup; @@ -1594,7 +1599,7 @@ static int fetch_multiple(struct string_list *list, int max_children) int i, result = 0; struct strvec argv = STRVEC_INIT; - if (!append && !dry_run) { + if (!append && write_fetch_head) { int errcode = truncate_fetch_head(); if (errcode) return errcode; @@ -1795,6 +1800,10 @@ int cmd_fetch(int argc, const char **argv, const char *prefix) if (depth || deepen_since || deepen_not.nr) deepen = 1; + /* FETCH_HEAD never gets updated in --dry-run mode */ + if (dry_run) + write_fetch_head = 0; + if (all) { if (argc == 1) die(_("fetch --all does not take a repository argument")); diff --git a/builtin/grep.c b/builtin/grep.c index cee9db3477..f58979bc3f 100644 --- a/builtin/grep.c +++ b/builtin/grep.c @@ -693,7 +693,7 @@ static int grep_directory(struct grep_opt *opt, const struct pathspec *pathspec, struct dir_struct dir; int i, hit = 0; - memset(&dir, 0, sizeof(dir)); + dir_init(&dir); if (!use_index) dir.flags |= DIR_NO_GITLINKS; if (exc_std) @@ -705,6 +705,7 @@ static int grep_directory(struct grep_opt *opt, const struct pathspec *pathspec, if (hit && opt->status_only) break; } + dir_clear(&dir); return hit; } diff --git a/builtin/index-pack.c b/builtin/index-pack.c index f865666db9..9721bf1ffe 100644 --- a/builtin/index-pack.c +++ b/builtin/index-pack.c @@ -1798,9 +1798,22 @@ int cmd_index_pack(int argc, const char **argv, const char *prefix) if (HAVE_THREADS && !nr_threads) { nr_threads = online_cpus(); - /* An experiment showed that more threads does not mean faster */ - if (nr_threads > 3) - nr_threads = 3; + /* + * Experiments show that going above 20 threads doesn't help, + * no matter how many cores you have. Below that, we tend to + * max at half the number of online_cpus(), presumably because + * half of those are hyperthreads rather than full cores. We'll + * never reduce the level below "3", though, to match a + * historical value that nobody complained about. + */ + if (nr_threads < 4) + ; /* too few cores to consider capping */ + else if (nr_threads < 6) + nr_threads = 3; /* historic cap */ + else if (nr_threads < 40) + nr_threads /= 2; + else + nr_threads = 20; /* hard cap */ } curr_pack = open_pack_file(pack_name); diff --git a/builtin/init-db.c b/builtin/init-db.c index f70076d38e..bbc9bc78f9 100644 --- a/builtin/init-db.c +++ b/builtin/init-db.c @@ -563,6 +563,9 @@ int cmd_init_db(int argc, const char **argv, const char *prefix) argc = parse_options(argc, argv, prefix, init_db_options, init_db_usage, 0); + if (real_git_dir && is_bare_repository_cfg == 1) + die(_("--separate-git-dir and --bare are mutually exclusive")); + if (real_git_dir && !is_absolute_path(real_git_dir)) real_git_dir = real_pathdup(real_git_dir, 1); @@ -658,6 +661,8 @@ int cmd_init_db(int argc, const char **argv, const char *prefix) get_git_work_tree()); } else { + if (real_git_dir) + die(_("--separate-git-dir incompatible with bare repository")); if (work_tree) set_git_work_tree(work_tree); } diff --git a/builtin/log.c b/builtin/log.c index 33528fefa9..b58f8da09e 100644 --- a/builtin/log.c +++ b/builtin/log.c @@ -599,8 +599,8 @@ static int show_tree_object(const struct object_id *oid, static void show_setup_revisions_tweak(struct rev_info *rev, struct setup_revision_opt *opt) { - if (rev->ignore_merges) { - /* There was no "-m" on the command line */ + if (rev->ignore_merges < 0) { + /* There was no "-m" variant on the command line */ rev->ignore_merges = 0; if (!rev->first_parent_only && !rev->combine_merges) { /* No "--first-parent", "-c", or "--cc" */ @@ -732,8 +732,7 @@ static void log_setup_revisions_tweak(struct rev_info *rev, if (!rev->diffopt.output_format && rev->combine_merges) rev->diffopt.output_format = DIFF_FORMAT_PATCH; - /* Turn -m on when --cc/-c was given */ - if (rev->combine_merges) + if (rev->first_parent_only && rev->ignore_merges < 0) rev->ignore_merges = 0; } diff --git a/builtin/ls-files.c b/builtin/ls-files.c index 30a4c10334..c8eae899b8 100644 --- a/builtin/ls-files.c +++ b/builtin/ls-files.c @@ -584,7 +584,7 @@ int cmd_ls_files(int argc, const char **argv, const char *cmd_prefix) if (argc == 2 && !strcmp(argv[1], "-h")) usage_with_options(ls_files_usage, builtin_ls_files_options); - memset(&dir, 0, sizeof(dir)); + dir_init(&dir); prefix = cmd_prefix; if (prefix) prefix_len = strlen(prefix); @@ -688,6 +688,6 @@ int cmd_ls_files(int argc, const char **argv, const char *cmd_prefix) return bad ? 1 : 0; } - UNLEAK(dir); + dir_clear(&dir); return 0; } diff --git a/builtin/ls-remote.c b/builtin/ls-remote.c index ea91679f33..092917eca2 100644 --- a/builtin/ls-remote.c +++ b/builtin/ls-remote.c @@ -83,6 +83,8 @@ int cmd_ls_remote(int argc, const char **argv, const char *prefix) PARSE_OPT_STOP_AT_NON_OPTION); dest = argv[0]; + UNLEAK(sorting); + if (argc > 1) { int i; pattern = xcalloc(argc, sizeof(const char *)); @@ -107,7 +109,6 @@ int cmd_ls_remote(int argc, const char **argv, const char *prefix) if (get_url) { printf("%s\n", *remote->url); - UNLEAK(sorting); return 0; } @@ -122,10 +123,8 @@ int cmd_ls_remote(int argc, const char **argv, const char *prefix) int hash_algo = hash_algo_by_ptr(transport_get_hash_algo(transport)); repo_set_hash_algo(the_repository, hash_algo); } - if (transport_disconnect(transport)) { - UNLEAK(sorting); + if (transport_disconnect(transport)) return 1; - } if (!dest && !quiet) fprintf(stderr, "From %s\n", *remote->url); @@ -150,7 +149,6 @@ int cmd_ls_remote(int argc, const char **argv, const char *prefix) status = 0; /* we found something */ } - UNLEAK(sorting); ref_array_clear(&ref_array); return status; } diff --git a/builtin/merge.c b/builtin/merge.c index 74829a838e..b9a89ba858 100644 --- a/builtin/merge.c +++ b/builtin/merge.c @@ -1348,7 +1348,7 @@ int cmd_merge(int argc, const char **argv, const char *prefix) else die(_("You have not concluded your merge (MERGE_HEAD exists).")); } - if (file_exists(git_path_cherry_pick_head(the_repository))) { + if (ref_exists("CHERRY_PICK_HEAD")) { if (advice_resolve_conflict) die(_("You have not concluded your cherry-pick (CHERRY_PICK_HEAD exists).\n" "Please, commit your changes before you merge.")); diff --git a/builtin/name-rev.c b/builtin/name-rev.c index a9dcd25e46..725dd04519 100644 --- a/builtin/name-rev.c +++ b/builtin/name-rev.c @@ -521,7 +521,7 @@ int cmd_name_rev(int argc, const char **argv, const char *prefix) int all = 0, transform_stdin = 0, allow_undefined = 1, always = 0, peel_tag = 0; struct name_ref_data data = { 0, 0, STRING_LIST_INIT_NODUP, STRING_LIST_INIT_NODUP }; struct option opts[] = { - OPT_BOOL(0, "name-only", &data.name_only, N_("print only names (no SHA-1)")), + OPT_BOOL(0, "name-only", &data.name_only, N_("print only ref-based names (no object names)")), OPT_BOOL(0, "tags", &data.tags_only, N_("only use tags to name the commits")), OPT_STRING_LIST(0, "refs", &data.ref_filters, N_("pattern"), N_("only use refs matching <pattern>")), diff --git a/builtin/pack-objects.c b/builtin/pack-objects.c index a8692d27f1..5617c01b5a 100644 --- a/builtin/pack-objects.c +++ b/builtin/pack-objects.c @@ -3357,7 +3357,7 @@ static void get_object_list(int ac, const char **av) if (starts_with(line, "--shallow ")) { struct object_id oid; if (get_oid_hex(line + 10, &oid)) - die("not an SHA-1 '%s'", line + 10); + die("not an object name '%s'", line + 10); register_shallow(the_repository, &oid); use_bitmap_index = 0; continue; diff --git a/builtin/rebase.c b/builtin/rebase.c index dadb52fa92..c4ff2039ef 100644 --- a/builtin/rebase.c +++ b/builtin/rebase.c @@ -92,6 +92,8 @@ struct rebase_options { int autosquash; char *gpg_sign_opt; int autostash; + int committer_date_is_author_date; + int ignore_date; char *cmd; int allow_empty_message; int rebase_merges, rebase_cousins; @@ -130,8 +132,12 @@ static struct replay_opts get_replay_opts(const struct rebase_options *opts) replay.quiet = !(opts->flags & REBASE_NO_QUIET); replay.verbose = opts->flags & REBASE_VERBOSE; replay.reschedule_failed_exec = opts->reschedule_failed_exec; + replay.committer_date_is_author_date = + opts->committer_date_is_author_date; + replay.ignore_date = opts->ignore_date; replay.gpg_sign = xstrdup_or_null(opts->gpg_sign_opt); replay.strategy = opts->strategy; + if (opts->strategy_opts) parse_strategy_opts(&replay, opts->strategy_opts); @@ -1289,6 +1295,7 @@ int cmd_rebase(int argc, const char **argv, const char *prefix) struct strbuf revisions = STRBUF_INIT; struct strbuf buf = STRBUF_INIT; struct object_id merge_base; + int ignore_whitespace = 0; enum action action = ACTION_NONE; const char *gpg_sign = NULL; struct string_list exec = STRING_LIST_INIT_NODUP; @@ -1318,16 +1325,17 @@ int cmd_rebase(int argc, const char **argv, const char *prefix) PARSE_OPT_NOARG, NULL, REBASE_DIFFSTAT }, OPT_BOOL(0, "signoff", &options.signoff, N_("add a Signed-off-by: line to each commit")), - OPT_PASSTHRU_ARGV(0, "ignore-whitespace", &options.git_am_opts, - NULL, N_("passed to 'git am'"), - PARSE_OPT_NOARG), - OPT_PASSTHRU_ARGV(0, "committer-date-is-author-date", - &options.git_am_opts, NULL, - N_("passed to 'git am'"), PARSE_OPT_NOARG), - OPT_PASSTHRU_ARGV(0, "ignore-date", &options.git_am_opts, NULL, - N_("passed to 'git am'"), PARSE_OPT_NOARG), + OPT_BOOL(0, "committer-date-is-author-date", + &options.committer_date_is_author_date, + N_("make committer date match author date")), + OPT_BOOL(0, "reset-author-date", &options.ignore_date, + N_("ignore author date and use current date")), + OPT_HIDDEN_BOOL(0, "ignore-date", &options.ignore_date, + N_("synonym of --reset-author-date")), OPT_PASSTHRU_ARGV('C', NULL, &options.git_am_opts, N_("n"), N_("passed to 'git apply'"), 0), + OPT_BOOL(0, "ignore-whitespace", &ignore_whitespace, + N_("ignore changes in whitespace")), OPT_PASSTHRU_ARGV(0, "whitespace", &options.git_am_opts, N_("action"), N_("passed to 'git apply'"), 0), OPT_BIT('f', "force-rebase", &options.flags, @@ -1624,12 +1632,12 @@ int cmd_rebase(int argc, const char **argv, const char *prefix) options.autosquash) { allow_preemptive_ff = 0; } + if (options.committer_date_is_author_date || options.ignore_date) + options.flags |= REBASE_FORCE; for (i = 0; i < options.git_am_opts.nr; i++) { const char *option = options.git_am_opts.v[i], *p; - if (!strcmp(option, "--committer-date-is-author-date") || - !strcmp(option, "--ignore-date") || - !strcmp(option, "--whitespace=fix") || + if (!strcmp(option, "--whitespace=fix") || !strcmp(option, "--whitespace=strip")) allow_preemptive_ff = 0; else if (skip_prefix(option, "-C", &p)) { @@ -1682,6 +1690,23 @@ int cmd_rebase(int argc, const char **argv, const char *prefix) imply_merge(&options, "--rebase-merges"); } + if (options.type == REBASE_APPLY) { + if (ignore_whitespace) + strvec_push(&options.git_am_opts, + "--ignore-whitespace"); + if (options.committer_date_is_author_date) + strvec_push(&options.git_am_opts, + "--committer-date-is-author-date"); + if (options.ignore_date) + strvec_push(&options.git_am_opts, "--ignore-date"); + } else { + /* REBASE_MERGE and PRESERVE_MERGES */ + if (ignore_whitespace) { + string_list_append(&strategy_options, + "ignore-space-change"); + } + } + if (strategy_options.nr) { int i; diff --git a/builtin/remote.c b/builtin/remote.c index c8240e9fcd..542f56e387 100644 --- a/builtin/remote.c +++ b/builtin/remote.c @@ -478,6 +478,7 @@ static int get_head_names(const struct ref *remote_refs, struct ref_states *stat struct ref *fetch_map = NULL, **fetch_map_tail = &fetch_map; struct refspec_item refspec; + memset(&refspec, 0, sizeof(refspec)); refspec.force = 0; refspec.pattern = 1; refspec.src = refspec.dst = "refs/heads/*"; diff --git a/builtin/rev-list.c b/builtin/rev-list.c index f520111eda..25c6c3b38d 100644 --- a/builtin/rev-list.c +++ b/builtin/rev-list.c @@ -637,8 +637,15 @@ int cmd_rev_list(int argc, const char **argv, const char *prefix) if (bisect_list) { int reaches, all; + unsigned bisect_flags = 0; - find_bisection(&revs.commits, &reaches, &all, bisect_find_all); + if (bisect_find_all) + bisect_flags |= FIND_BISECTION_ALL; + + if (revs.first_parent_only) + bisect_flags |= FIND_BISECTION_FIRST_PARENT_ONLY; + + find_bisection(&revs.commits, &reaches, &all, bisect_flags); if (bisect_show_vars) return show_bisect_vars(&info, reaches, all); diff --git a/builtin/stash.c b/builtin/stash.c index 10d87630cd..4bdfaf8397 100644 --- a/builtin/stash.c +++ b/builtin/stash.c @@ -864,7 +864,7 @@ static int get_untracked_files(const struct pathspec *ps, int include_untracked, int found = 0; struct dir_struct dir; - memset(&dir, 0, sizeof(dir)); + dir_init(&dir); if (include_untracked != INCLUDE_ALL_FILES) setup_standard_excludes(&dir); @@ -875,12 +875,9 @@ static int get_untracked_files(const struct pathspec *ps, int include_untracked, strbuf_addstr(untracked_files, ent->name); /* NUL-terminate: will be fed to update-index -z */ strbuf_addch(untracked_files, '\0'); - free(ent); } - free(dir.entries); - free(dir.ignored); - clear_directory(&dir); + dir_clear(&dir); return found; } diff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c index df135abbf1..a59d8e4bda 100644 --- a/builtin/submodule--helper.c +++ b/builtin/submodule--helper.c @@ -1511,7 +1511,7 @@ static void determine_submodule_update_strategy(struct repository *r, if (parse_submodule_update_strategy(update, out) < 0) die(_("Invalid update mode '%s' for submodule path '%s'"), update, path); - } else if (!repo_config_get_string_const(r, key, &val)) { + } else if (!repo_config_get_string_tmp(r, key, &val)) { if (parse_submodule_update_strategy(val, out) < 0) die(_("Invalid update mode '%s' configured for submodule path '%s'"), val, path); @@ -1667,7 +1667,7 @@ static int prepare_to_clone_next_submodule(const struct cache_entry *ce, } key = xstrfmt("submodule.%s.update", sub->name); - if (!repo_config_get_string_const(the_repository, key, &update_string)) { + if (!repo_config_get_string_tmp(the_repository, key, &update_string)) { update_type = parse_submodule_update_type(update_string); } else { update_type = sub->update_strategy.type; @@ -1690,7 +1690,7 @@ static int prepare_to_clone_next_submodule(const struct cache_entry *ce, strbuf_reset(&sb); strbuf_addf(&sb, "submodule.%s.url", sub->name); - if (repo_config_get_string_const(the_repository, sb.buf, &url)) { + if (repo_config_get_string_tmp(the_repository, sb.buf, &url)) { if (starts_with_dot_slash(sub->url) || starts_with_dot_dot_slash(sub->url)) { url = compute_submodule_clone_url(sub->url); @@ -1747,8 +1747,8 @@ static int prepare_to_clone_next_submodule(const struct cache_entry *ce, "--no-single-branch"); cleanup: - strbuf_reset(&displaypath_sb); - strbuf_reset(&sb); + strbuf_release(&displaypath_sb); + strbuf_release(&sb); if (need_free_url) free((void*)url); @@ -1976,7 +1976,7 @@ static const char *remote_submodule_branch(const char *path) return NULL; key = xstrfmt("submodule.%s.branch", sub->name); - if (repo_config_get_string_const(the_repository, key, &branch)) + if (repo_config_get_string_tmp(the_repository, key, &branch)) branch = sub->branch; free(key); @@ -2101,7 +2101,7 @@ static int ensure_core_worktree(int argc, const char **argv, const char *prefix) { const struct submodule *sub; const char *path; - char *cw; + const char *cw; struct repository subrepo; if (argc != 2) @@ -2116,7 +2116,7 @@ static int ensure_core_worktree(int argc, const char **argv, const char *prefix) if (repo_submodule_init(&subrepo, the_repository, sub)) die(_("could not get a repository handle for submodule '%s'"), path); - if (!repo_config_get_string(&subrepo, "core.worktree", &cw)) { + if (!repo_config_get_string_tmp(&subrepo, "core.worktree", &cw)) { char *cfg_file, *abs_path; const char *rel_path; struct strbuf sb = STRBUF_INIT; @@ -921,8 +921,8 @@ extern int assume_unchanged; extern int prefer_symlink_refs; extern int warn_ambiguous_refs; extern int warn_on_object_refname_ambiguity; -extern const char *apply_default_whitespace; -extern const char *apply_default_ignorewhitespace; +extern char *apply_default_whitespace; +extern char *apply_default_ignorewhitespace; extern const char *git_attributes_file; extern const char *git_hooks_path; extern int zlib_compression_level; diff --git a/checkout.c b/checkout.c index c72e9f9773..6586e30ca5 100644 --- a/checkout.c +++ b/checkout.c @@ -47,7 +47,7 @@ const char *unique_tracking_name(const char *name, struct object_id *oid, { struct tracking_name_data cb_data = TRACKING_NAME_DATA_INIT; const char *default_remote = NULL; - if (!git_config_get_string_const("checkout.defaultremote", &default_remote)) + if (!git_config_get_string_tmp("checkout.defaultremote", &default_remote)) cb_data.default_remote = default_remote; cb_data.src_ref = xstrfmt("refs/heads/%s", name); cb_data.dst_oid = oid; @@ -55,7 +55,6 @@ const char *unique_tracking_name(const char *name, struct object_id *oid, if (dwim_remotes_matched) *dwim_remotes_matched = cb_data.num_matches; free(cb_data.src_ref); - free((char *)default_remote); if (cb_data.num_matches == 1) { free(cb_data.default_dst_ref); free(cb_data.default_dst_oid); diff --git a/commit-graph.c b/commit-graph.c index e51c91dd5b..0ed003e218 100644 --- a/commit-graph.c +++ b/commit-graph.c @@ -179,7 +179,14 @@ static char *get_chain_filename(struct object_directory *odb) static uint8_t oid_version(void) { - return 1; + switch (hash_algo_by_ptr(the_hash_algo)) { + case GIT_HASH_SHA1: + return 1; + case GIT_HASH_SHA256: + return 2; + default: + die(_("invalid hash version")); + } } static struct commit_graph *alloc_commit_graph(void) @@ -1316,8 +1316,8 @@ int commit_tree(const char *msg, size_t msg_len, const struct object_id *tree, int result; append_merge_tag_headers(parents, &tail); - result = commit_tree_extended(msg, msg_len, tree, parents, ret, - author, sign_commit, extra); + result = commit_tree_extended(msg, msg_len, tree, parents, ret, author, + NULL, sign_commit, extra); free_commit_extra_headers(extra); return result; } @@ -1440,7 +1440,8 @@ N_("Warning: commit message did not conform to UTF-8.\n" int commit_tree_extended(const char *msg, size_t msg_len, const struct object_id *tree, struct commit_list *parents, struct object_id *ret, - const char *author, const char *sign_commit, + const char *author, const char *committer, + const char *sign_commit, struct commit_extra_header *extra) { int result; @@ -1473,7 +1474,9 @@ int commit_tree_extended(const char *msg, size_t msg_len, if (!author) author = git_author_info(IDENT_STRICT); strbuf_addf(&buffer, "author %s\n", author); - strbuf_addf(&buffer, "committer %s\n", git_committer_info(IDENT_STRICT)); + if (!committer) + committer = git_committer_info(IDENT_STRICT); + strbuf_addf(&buffer, "committer %s\n", committer); if (!encoding_is_utf8) strbuf_addf(&buffer, "encoding %s\n", git_commit_encoding); @@ -270,10 +270,9 @@ int commit_tree(const char *msg, size_t msg_len, int commit_tree_extended(const char *msg, size_t msg_len, const struct object_id *tree, - struct commit_list *parents, - struct object_id *ret, const char *author, - const char *sign_commit, - struct commit_extra_header *); + struct commit_list *parents, struct object_id *ret, + const char *author, const char *committer, + const char *sign_commit, struct commit_extra_header *); struct commit_extra_header *read_commit_extra_headers(struct commit *, const char **); diff --git a/compat/mingw.c b/compat/mingw.c index 4454b3e67b..a00f331230 100644 --- a/compat/mingw.c +++ b/compat/mingw.c @@ -290,6 +290,9 @@ int mingw_unlink(const char *pathname) if (xutftowcs_path(wpathname, pathname) < 0) return -1; + if (DeleteFileW(wpathname)) + return 0; + /* read-only files cannot be removed */ _wchmod(wpathname, 0666); while ((ret = _wunlink(wpathname)) == -1 && tries < ARRAY_SIZE(delay)) { @@ -2006,18 +2006,27 @@ const struct string_list *git_configset_get_value_multi(struct config_set *cs, c return e ? &e->value_list : NULL; } -int git_configset_get_string_const(struct config_set *cs, const char *key, const char **dest) +int git_configset_get_string(struct config_set *cs, const char *key, char **dest) { const char *value; if (!git_configset_get_value(cs, key, &value)) - return git_config_string(dest, key, value); + return git_config_string((const char **)dest, key, value); else return 1; } -int git_configset_get_string(struct config_set *cs, const char *key, char **dest) +int git_configset_get_string_tmp(struct config_set *cs, const char *key, + const char **dest) { - return git_configset_get_string_const(cs, key, (const char **)dest); + const char *value; + if (!git_configset_get_value(cs, key, &value)) { + if (!value) + return config_error_nonbool(key); + *dest = value; + return 0; + } else { + return 1; + } } int git_configset_get_int(struct config_set *cs, const char *key, int *dest) @@ -2147,22 +2156,26 @@ const struct string_list *repo_config_get_value_multi(struct repository *repo, return git_configset_get_value_multi(repo->config, key); } -int repo_config_get_string_const(struct repository *repo, - const char *key, const char **dest) +int repo_config_get_string(struct repository *repo, + const char *key, char **dest) { int ret; git_config_check_init(repo); - ret = git_configset_get_string_const(repo->config, key, dest); + ret = git_configset_get_string(repo->config, key, dest); if (ret < 0) git_die_config(key, NULL); return ret; } -int repo_config_get_string(struct repository *repo, - const char *key, char **dest) +int repo_config_get_string_tmp(struct repository *repo, + const char *key, const char **dest) { + int ret; git_config_check_init(repo); - return repo_config_get_string_const(repo, key, (const char **)dest); + ret = git_configset_get_string_tmp(repo->config, key, dest); + if (ret < 0) + git_die_config(key, NULL); + return ret; } int repo_config_get_int(struct repository *repo, @@ -2232,14 +2245,14 @@ const struct string_list *git_config_get_value_multi(const char *key) return repo_config_get_value_multi(the_repository, key); } -int git_config_get_string_const(const char *key, const char **dest) +int git_config_get_string(const char *key, char **dest) { - return repo_config_get_string_const(the_repository, key, dest); + return repo_config_get_string(the_repository, key, dest); } -int git_config_get_string(const char *key, char **dest) +int git_config_get_string_tmp(const char *key, const char **dest) { - return repo_config_get_string(the_repository, key, dest); + return repo_config_get_string_tmp(the_repository, key, dest); } int git_config_get_int(const char *key, int *dest) @@ -2274,7 +2287,7 @@ int git_config_get_pathname(const char *key, const char **dest) int git_config_get_expiry(const char *key, const char **output) { - int ret = git_config_get_string_const(key, output); + int ret = git_config_get_string(key, (char **)output); if (ret) return ret; if (strcmp(*output, "now")) { @@ -2287,11 +2300,11 @@ int git_config_get_expiry(const char *key, const char **output) int git_config_get_expiry_in_days(const char *key, timestamp_t *expiry, timestamp_t now) { - char *expiry_string; + const char *expiry_string; intmax_t days; timestamp_t when; - if (git_config_get_string(key, &expiry_string)) + if (git_config_get_string_tmp(key, &expiry_string)) return 1; /* no such thing */ if (git_parse_signed(expiry_string, &days, maximum_signed_value_of_type(int))) { @@ -458,8 +458,8 @@ void git_configset_clear(struct config_set *cs); */ int git_configset_get_value(struct config_set *cs, const char *key, const char **dest); -int git_configset_get_string_const(struct config_set *cs, const char *key, const char **dest); int git_configset_get_string(struct config_set *cs, const char *key, char **dest); +int git_configset_get_string_tmp(struct config_set *cs, const char *key, const char **dest); int git_configset_get_int(struct config_set *cs, const char *key, int *dest); int git_configset_get_ulong(struct config_set *cs, const char *key, unsigned long *dest); int git_configset_get_bool(struct config_set *cs, const char *key, int *dest); @@ -474,10 +474,10 @@ int repo_config_get_value(struct repository *repo, const char *key, const char **value); const struct string_list *repo_config_get_value_multi(struct repository *repo, const char *key); -int repo_config_get_string_const(struct repository *repo, - const char *key, const char **dest); int repo_config_get_string(struct repository *repo, const char *key, char **dest); +int repo_config_get_string_tmp(struct repository *repo, + const char *key, const char **dest); int repo_config_get_int(struct repository *repo, const char *key, int *dest); int repo_config_get_ulong(struct repository *repo, @@ -529,13 +529,14 @@ void git_config_clear(void); * error message and returns -1. When the configuration variable `key` is * not found, returns 1 without touching `dest`. */ -int git_config_get_string_const(const char *key, const char **dest); +int git_config_get_string(const char *key, char **dest); /** - * Similar to `git_config_get_string_const`, except that retrieved value - * copied into the `dest` parameter is a mutable string. + * Similar to `git_config_get_string`, but does not allocate any new + * memory; on success `dest` will point to memory owned by the config + * machinery, which could be invalidated if it is discarded and reloaded. */ -int git_config_get_string(const char *key, char **dest); +int git_config_get_string_tmp(const char *key, const char **dest); /** * Finds and parses the value to an integer for the configuration variable @@ -1052,7 +1052,7 @@ static const char *get_ssh_command(void) if ((ssh = getenv("GIT_SSH_COMMAND"))) return ssh; - if (!git_config_get_string_const("core.sshcommand", &ssh)) + if (!git_config_get_string_tmp("core.sshcommand", &ssh)) return ssh; return NULL; @@ -1071,7 +1071,7 @@ static void override_ssh_variant(enum ssh_variant *ssh_variant) { const char *variant = getenv("GIT_SSH_VARIANT"); - if (!variant && git_config_get_string_const("ssh.variant", &variant)) + if (!variant && git_config_get_string_tmp("ssh.variant", &variant)) return; if (!strcmp(variant, "auto")) diff --git a/connected.c b/connected.c index 21c1ebe9fb..b18299fdf0 100644 --- a/connected.c +++ b/connected.c @@ -22,14 +22,13 @@ int check_connected(oid_iterate_fn fn, void *cb_data, struct check_connected_options *opt) { struct child_process rev_list = CHILD_PROCESS_INIT; + FILE *rev_list_in; struct check_connected_options defaults = CHECK_CONNECTED_INIT; - char commit[GIT_MAX_HEXSZ + 1]; struct object_id oid; int err = 0; struct packed_git *new_pack = NULL; struct transport *transport; size_t base_len; - const unsigned hexsz = the_hash_algo->hexsz; if (!opt) opt = &defaults; @@ -122,7 +121,8 @@ no_promisor_pack_found: sigchain_push(SIGPIPE, SIG_IGN); - commit[hexsz] = '\n'; + rev_list_in = xfdopen(rev_list.in, "w"); + do { /* * If index-pack already checked that: @@ -135,16 +135,17 @@ no_promisor_pack_found: if (new_pack && find_pack_entry_one(oid.hash, new_pack)) continue; - memcpy(commit, oid_to_hex(&oid), hexsz); - if (write_in_full(rev_list.in, commit, hexsz + 1) < 0) { - if (errno != EPIPE && errno != EINVAL) - error_errno(_("failed write to rev-list")); - err = -1; + if (fprintf(rev_list_in, "%s\n", oid_to_hex(&oid)) < 0) break; - } } while (!fn(cb_data, &oid)); - if (close(rev_list.in)) + if (ferror(rev_list_in) || fflush(rev_list_in)) { + if (errno != EPIPE && errno != EINVAL) + error_errno(_("failed write to rev-list")); + err = -1; + } + + if (fclose(rev_list_in)) err = error_errno(_("failed to close rev-list's stdin")); sigchain_pop(SIGPIPE); diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash index 0fdb5da83b..9147fba3d5 100644 --- a/contrib/completion/git-completion.bash +++ b/contrib/completion/git-completion.bash @@ -39,6 +39,11 @@ # When set to "1", do not include "DWIM" suggestions in git-checkout # and git-switch completion (e.g., completing "foo" when "origin/foo" # exists). +# +# GIT_COMPLETION_SHOW_ALL +# +# When set to "1" suggest all options, including options which are +# typically hidden (e.g. '--allow-empty' for 'git commit'). case "$COMP_WORDBREAKS" in *:*) : great ;; @@ -411,10 +416,17 @@ __gitcomp_builtin () local options eval "options=\${$var-}" + local completion_helper + if [ "$GIT_COMPLETION_SHOW_ALL" = "1" ]; then + completion_helper="--git-completion-helper-all" + else + completion_helper="--git-completion-helper" + fi + if [ -z "$options" ]; then # leading and trailing spaces are significant to make # option removal work correctly. - options=" $incl $(__git ${cmd/_/ } --git-completion-helper) " || return + options=" $incl $(__git ${cmd/_/ } $completion_helper) " || return for i in $excl; do options="${options/ $i / }" @@ -1712,8 +1724,8 @@ _git_diff () } __git_mergetools_common="diffuse diffmerge ecmerge emerge kdiff3 meld opendiff - tkdiff vimdiff gvimdiff xxdiff araxis p4merge bc - codecompare smerge + tkdiff vimdiff nvimdiff gvimdiff xxdiff araxis p4merge + bc codecompare smerge " _git_difftool () diff --git a/contrib/subtree/git-subtree.txt b/contrib/subtree/git-subtree.txt index 352deda69d..0db02fe3c0 100644 --- a/contrib/subtree/git-subtree.txt +++ b/contrib/subtree/git-subtree.txt @@ -139,12 +139,12 @@ OPTIONS -m <message>:: --message=<message>:: - This option is only valid for add, merge and pull (unsure). + This option is only valid for add, merge, pull, and split --rejoin. Specify <message> as the commit message for the merge commit. -OPTIONS FOR add, merge, push, pull ----------------------------------- +OPTIONS FOR add, merge, and pull +-------------------------------- --squash:: This option is only valid for add, merge, and pull commands. diff --git a/diff-lib.c b/diff-lib.c index 25fd2dee19..50521e2093 100644 --- a/diff-lib.c +++ b/diff-lib.c @@ -219,7 +219,8 @@ int run_diff_files(struct rev_info *revs, unsigned int option) continue; } else if (revs->diffopt.ita_invisible_in_index && ce_intent_to_add(ce)) { - diff_addremove(&revs->diffopt, '+', ce->ce_mode, + newmode = ce_mode_from_stat(ce, st.st_mode); + diff_addremove(&revs->diffopt, '+', newmode, &null_oid, 0, ce->name, 0); continue; } @@ -4319,7 +4319,10 @@ static void fill_metainfo(struct strbuf *msg, } if (one && two && !oideq(&one->oid, &two->oid)) { const unsigned hexsz = the_hash_algo->hexsz; - int abbrev = o->flags.full_index ? hexsz : DEFAULT_ABBREV; + int abbrev = o->abbrev ? o->abbrev : DEFAULT_ABBREV; + + if (o->flags.full_index) + abbrev = hexsz; if (o->flags.binary) { mmfile_t mf; @@ -6044,6 +6047,8 @@ static void patch_id_consume(void *priv, char *line, unsigned long len) struct patch_id_t *data = priv; int new_len; + if (len > 12 && starts_with(line, "\\ ")) + return; new_len = remove_space(line, len); the_hash_algo->update_fn(data->ctx, line, new_len); @@ -54,6 +54,11 @@ static enum path_treatment read_directory_recursive(struct dir_struct *dir, static int resolve_dtype(int dtype, struct index_state *istate, const char *path, int len); +void dir_init(struct dir_struct *dir) +{ + memset(dir, 0, sizeof(*dir)); +} + int count_slashes(const char *s) { int cnt = 0; @@ -916,6 +921,8 @@ void clear_pattern_list(struct pattern_list *pl) free(pl->patterns[i]); free(pl->patterns); free(pl->filebuf); + hashmap_free_entries(&pl->recursive_hashmap, struct pattern_entry, ent); + hashmap_free_entries(&pl->parent_hashmap, struct pattern_entry, ent); memset(pl, 0, sizeof(*pl)); } @@ -1792,9 +1799,12 @@ static enum path_treatment treat_directory(struct dir_struct *dir, nested_repo = is_nonbare_repository_dir(&sb); strbuf_release(&sb); } - if (nested_repo) - return ((dir->flags & DIR_SKIP_NESTED_GIT) ? path_none : - (excluded ? path_excluded : path_untracked)); + if (nested_repo) { + if ((dir->flags & DIR_SKIP_NESTED_GIT) || + (matches_how == MATCHED_RECURSIVELY_LEADING_PATHSPEC)) + return path_none; + return excluded ? path_excluded : path_untracked; + } if (!(dir->flags & DIR_SHOW_OTHER_DIRECTORIES)) { if (excluded && @@ -3009,10 +3019,10 @@ int remove_path(const char *name) } /* - * Frees memory within dir which was allocated for exclude lists and - * the exclude_stack. Does not free dir itself. + * Frees memory within dir which was allocated, and resets fields for further + * use. Does not free dir itself. */ -void clear_directory(struct dir_struct *dir) +void dir_clear(struct dir_struct *dir) { int i, j; struct exclude_list_group *group; @@ -3030,6 +3040,13 @@ void clear_directory(struct dir_struct *dir) free(group->pl); } + for (i = 0; i < dir->ignored_nr; i++) + free(dir->ignored[i]); + for (i = 0; i < dir->nr; i++) + free(dir->entries[i]); + free(dir->ignored); + free(dir->entries); + stk = dir->exclude_stack; while (stk) { struct exclude_stack *prev = stk->prev; @@ -3037,6 +3054,8 @@ void clear_directory(struct dir_struct *dir) stk = prev; } strbuf_release(&dir->basebuf); + + dir_init(dir); } struct ondisk_untracked_cache { @@ -19,24 +19,23 @@ * CE_SKIP_WORKTREE marked. If you want to exclude files, make sure you have * loaded the index first. * - * - Prepare `struct dir_struct dir` and clear it with `memset(&dir, 0, - * sizeof(dir))`. + * - Prepare `struct dir_struct dir` using `dir_init()` function. * * - To add single exclude pattern, call `add_pattern_list()` and then * `add_pattern()`. * * - To add patterns from a file (e.g. `.git/info/exclude`), call - * `add_patterns_from_file()` , and/or set `dir.exclude_per_dir`. A - * short-hand function `setup_standard_excludes()` can be used to set - * up the standard set of exclude settings. + * `add_patterns_from_file()` , and/or set `dir.exclude_per_dir`. * - * - Set options described in the Data Structure section above. + * - A short-hand function `setup_standard_excludes()` can be used to set + * up the standard set of exclude settings, instead of manually calling + * the add_pattern*() family of functions. * - * - Call `read_directory()`. + * - Call `fill_directory()`. * - * - Use `dir.entries[]`. + * - Use `dir.entries[]` and `dir.ignored[]`. * - * - Call `clear_directory()` when none of the contained elements are no longer in use. + * - Call `dir_clear()` when the contained elements are no longer in use. * */ @@ -362,6 +361,8 @@ int match_pathspec(const struct index_state *istate, int report_path_error(const char *ps_matched, const struct pathspec *pathspec); int within_depth(const char *name, int namelen, int depth, int max_depth); +void dir_init(struct dir_struct *dir); + int fill_directory(struct dir_struct *dir, struct index_state *istate, const struct pathspec *pathspec); @@ -428,7 +429,7 @@ void parse_path_pattern(const char **string, int *patternlen, unsigned *flags, i void add_pattern(const char *string, const char *base, int baselen, struct pattern_list *pl, int srcpos); void clear_pattern_list(struct pattern_list *pl); -void clear_directory(struct dir_struct *dir); +void dir_clear(struct dir_struct *dir); int repo_file_exists(struct repository *repo, const char *path); int file_exists(const char *); @@ -40,7 +40,7 @@ const char *git_sequence_editor(void) const char *editor = getenv("GIT_SEQUENCE_EDITOR"); if (!editor) - git_config_get_string_const("sequence.editor", &editor); + git_config_get_string_tmp("sequence.editor", &editor); if (!editor) editor = git_editor(); @@ -510,8 +510,6 @@ int checkout_entry(struct cache_entry *ce, const struct checkout *state, /* If it is a gitlink, leave it alone! */ if (S_ISGITLINK(ce->ce_mode)) return 0; - if (!state->force) - return error("%s is a directory", path.buf); remove_subtree(&path); } else if (unlink(path.buf)) return error_errno("unable to unlink old '%s'", path.buf); diff --git a/environment.c b/environment.c index 52e0c979ba..bb518c61cd 100644 --- a/environment.c +++ b/environment.c @@ -35,8 +35,8 @@ int repository_format_precious_objects; int repository_format_worktree_config; const char *git_commit_encoding; const char *git_log_output_encoding; -const char *apply_default_whitespace; -const char *apply_default_ignorewhitespace; +char *apply_default_whitespace; +char *apply_default_ignorewhitespace; const char *git_attributes_file; const char *git_hooks_path; int zlib_compression_level = Z_BEST_SPEED; diff --git a/fast-import.c b/fast-import.c index ce47794db6..c45fe7474c 100644 --- a/fast-import.c +++ b/fast-import.c @@ -526,14 +526,6 @@ static unsigned int hc_str(const char *s, size_t len) return r; } -static char *pool_strdup(const char *s) -{ - size_t len = strlen(s) + 1; - char *r = mem_pool_alloc(&fi_mem_pool, len); - memcpy(r, s, len); - return r; -} - static void insert_mark(struct mark_set *s, uintmax_t idnum, struct object_entry *oe) { while ((idnum >> s->shift) >= 1024) { @@ -615,7 +607,7 @@ static struct branch *new_branch(const char *name) die("Branch name doesn't conform to GIT standards: %s", name); b = mem_pool_calloc(&fi_mem_pool, 1, sizeof(struct branch)); - b->name = pool_strdup(name); + b->name = mem_pool_strdup(&fi_mem_pool, name); b->table_next_branch = branch_table[hc]; b->branch_tree.versions[0].mode = S_IFDIR; b->branch_tree.versions[1].mode = S_IFDIR; @@ -2806,7 +2798,7 @@ static void parse_new_tag(const char *arg) t = mem_pool_alloc(&fi_mem_pool, sizeof(struct tag)); memset(t, 0, sizeof(struct tag)); - t->name = pool_strdup(arg); + t->name = mem_pool_strdup(&fi_mem_pool, arg); if (last_tag) last_tag->next_tag = t; else diff --git a/fetch-pack.c b/fetch-pack.c index 0f1a84c061..5eb6ed6165 100644 --- a/fetch-pack.c +++ b/fetch-pack.c @@ -870,13 +870,16 @@ static int get_pack(struct fetch_pack_args *args, * have this responsibility. */ args->check_self_contained_and_connected = 0; - /* - * If we're obtaining the filename of a lockfile, we'll use - * that filename to write a .promisor file with more - * information below. If not, we need index-pack to do it for - * us. - */ - if (!(do_keep && pack_lockfiles) && args->from_promisor) + + if (args->from_promisor) + /* + * write_promisor_file() may be called afterwards but + * we still need index-pack to know that this is a + * promisor pack. For example, if transfer.fsckobjects + * is true, index-pack needs to know that .gitmodules + * is a promisor object (so that it won't complain if + * it is missing). + */ strvec_push(&cmd.args, "--promisor"); } else { diff --git a/git-bisect.sh b/git-bisect.sh index f03fbb18f0..c28e35b0fa 100755 --- a/git-bisect.sh +++ b/git-bisect.sh @@ -4,7 +4,7 @@ USAGE='[help|start|bad|good|new|old|terms|skip|next|reset|visualize|view|replay| LONG_USAGE='git bisect help print this long help message. git bisect start [--term-{old,good}=<term> --term-{new,bad}=<term>] - [--no-checkout] [<bad> [<good>...]] [--] [<pathspec>...] + [--no-checkout] [--first-parent] [<bad> [<good>...]] [--] [<pathspec>...] reset bisect state and start bisection. git bisect (bad|new) [<rev>] mark <rev> a known-bad revision/ @@ -153,7 +153,7 @@ bisect_next() { git bisect--helper --bisect-next-check $TERM_GOOD $TERM_BAD $TERM_GOOD|| exit # Perform all bisection computation, display and checkout - git bisect--helper --next-all $(git rev-parse --verify -q BISECT_HEAD > /dev/null && echo --no-checkout) + git bisect--helper --next-all res=$? # Check if we should exit because bisection is finished diff --git a/git-compat-util.h b/git-compat-util.h index 5637114b8d..7a0fb7a045 100644 --- a/git-compat-util.h +++ b/git-compat-util.h @@ -252,8 +252,10 @@ typedef unsigned long uintptr_t; #ifdef PRECOMPOSE_UNICODE #include "compat/precompose_utf8.h" #else -#define precompose_str(in,i_nfd2nfc) -#define precompose_argv(c,v) +static inline void precompose_argv(int argc, const char **argv) +{ + ; /* nothing */ +} #define probe_utf8_pathname_composition() #endif @@ -270,7 +272,9 @@ struct itimerval { #endif #ifdef NO_SETITIMER -#define setitimer(which,value,ovalue) +static inline int setitimer(int which, const struct itimerval *value, struct itimerval *newvalue) { + ; /* nothing */ +} #endif #ifndef NO_LIBGEN_H @@ -1231,8 +1235,14 @@ int warn_on_fopen_errors(const char *path); #endif #ifndef _POSIX_THREAD_SAFE_FUNCTIONS -#define flockfile(fh) -#define funlockfile(fh) +static inline void flockfile(FILE *fh) +{ + ; /* nothing */ +} +static inline void funlockfile(FILE *fh) +{ + ; /* nothing */ +} #define getc_unlocked(fh) getc(fh) #endif diff --git a/git-mergetool--lib.sh b/git-mergetool--lib.sh index 204a5acd66..2defef28cd 100644 --- a/git-mergetool--lib.sh +++ b/git-mergetool--lib.sh @@ -43,7 +43,14 @@ show_tool_names () { shown_any= ( cd "$MERGE_TOOLS_DIR" && ls ) | { - while read toolname + while read scriptname + do + setup_tool "$scriptname" 2>/dev/null + variants="$variants$(list_tool_variants)\n" + done + variants="$(echo "$variants" | sort | uniq)" + + for toolname in $variants do if setup_tool "$toolname" 2>/dev/null && (eval "$condition" "$toolname") @@ -157,6 +164,10 @@ setup_tool () { echo "$1" } + list_tool_variants () { + echo "$tool" + } + # Most tools' exit codes cannot be trusted, so By default we ignore # their exit code and check the merged file's modification time in # check_unchanged() to determine whether or not the merge was @@ -178,19 +189,26 @@ setup_tool () { false } - - if ! test -f "$MERGE_TOOLS_DIR/$tool" + if test -f "$MERGE_TOOLS_DIR/$tool" + then + . "$MERGE_TOOLS_DIR/$tool" + elif test -f "$MERGE_TOOLS_DIR/${tool%[0-9]}" then + . "$MERGE_TOOLS_DIR/${tool%[0-9]}" + else setup_user_tool return $? fi - # Load the redefined functions - . "$MERGE_TOOLS_DIR/$tool" # Now let the user override the default command for the tool. If # they have not done so then this will return 1 which we ignore. setup_user_tool + if ! list_tool_variants | grep -q "^$tool$" + then + return 1 + fi + if merge_mode && ! can_merge then echo "error: '$tool' can not be used to resolve merges" >&2 @@ -286,11 +304,14 @@ list_merge_tool_candidates () { tools="$tools smerge" fi case "${VISUAL:-$EDITOR}" in + *nvim*) + tools="$tools nvimdiff vimdiff emerge" + ;; *vim*) - tools="$tools vimdiff emerge" + tools="$tools vimdiff nvimdiff emerge" ;; *) - tools="$tools emerge vimdiff" + tools="$tools emerge vimdiff nvimdiff" ;; esac } diff --git a/git-rebase--preserve-merges.sh b/git-rebase--preserve-merges.sh index dec90e9af6..b9c71d2a71 100644 --- a/git-rebase--preserve-merges.sh +++ b/git-rebase--preserve-merges.sh @@ -193,16 +193,6 @@ mark_action_done () { fi } -# Put the last action marked done at the beginning of the todo list -# again. If there has not been an action marked done yet, leave the list of -# items on the todo list unchanged. -reschedule_last_action () { - tail -n 1 "$done" | cat - "$todo" >"$todo".new - sed -e \$d <"$done" >"$done".new - mv -f "$todo".new "$todo" - mv -f "$done".new "$done" -} - append_todo_help () { gettext " Commands: diff --git a/git-send-email.perl b/git-send-email.perl index 36c47bae1d..1f425c0809 100755 --- a/git-send-email.perl +++ b/git-send-email.perl @@ -250,6 +250,7 @@ my $chain_reply_to = 0; my $use_xmailer = 1; my $validate = 1; my $target_xfer_encoding = 'auto'; +my $forbid_sendmail_variables = 1; my %config_bool_settings = ( "thread" => \$thread, @@ -263,6 +264,7 @@ my %config_bool_settings = ( "multiedit" => \$multiedit, "annotate" => \$annotate, "xmailer" => \$use_xmailer, + "forbidsendmailvariables" => \$forbid_sendmail_variables, ); my %config_settings = ( @@ -478,6 +480,12 @@ unless ($rc) { usage(); } +if ($forbid_sendmail_variables && (scalar Git::config_regexp("^sendmail[.]")) != 0) { + die __("fatal: found configuration options for 'sendmail'\n" . + "git-send-email is configured with the sendemail.* options - note the 'e'.\n" . + "Set sendemail.forbidSendmailVariables to false to disable this check.\n"); +} + die __("Cannot run git format-patch from outside a repository\n") if $format_patch and not $repo; @@ -375,7 +375,7 @@ void list_cmds_by_config(struct string_list *list) { const char *cmd_list; - if (git_config_get_string_const("completion.commands", &cmd_list)) + if (git_config_get_string_tmp("completion.commands", &cmd_list)) return; string_list_sort(list); @@ -345,27 +345,45 @@ person_only: return 0; } -static const char *env_hint = -N_("\n" - "*** Please tell me who you are.\n" - "\n" - "Run\n" - "\n" - " git config --global user.email \"you@example.com\"\n" - " git config --global user.name \"Your Name\"\n" - "\n" - "to set your account\'s default identity.\n" - "Omit --global to set the identity only in this repository.\n" - "\n"); + +static void ident_env_hint(enum want_ident whose_ident) +{ + switch (whose_ident) { + case WANT_AUTHOR_IDENT: + fputs(_("Author identity unknown\n"), stderr); + break; + case WANT_COMMITTER_IDENT: + fputs(_("Committer identity unknown\n"), stderr); + break; + default: + break; + } + + fputs(_("\n" + "*** Please tell me who you are.\n" + "\n" + "Run\n" + "\n" + " git config --global user.email \"you@example.com\"\n" + " git config --global user.name \"Your Name\"\n" + "\n" + "to set your account\'s default identity.\n" + "Omit --global to set the identity only in this repository.\n" + "\n"), stderr); +} const char *fmt_ident(const char *name, const char *email, enum want_ident whose_ident, const char *date_str, int flag) { - static struct strbuf ident = STRBUF_INIT; + static int index; + static struct strbuf ident_pool[2] = { STRBUF_INIT, STRBUF_INIT }; int strict = (flag & IDENT_STRICT); int want_date = !(flag & IDENT_NO_DATE); int want_name = !(flag & IDENT_NO_NAME); + struct strbuf *ident = &ident_pool[index]; + index = (index + 1) % ARRAY_SIZE(ident_pool); + if (!email) { if (whose_ident == WANT_AUTHOR_IDENT && git_author_email.len) email = git_author_email.buf; @@ -375,12 +393,12 @@ const char *fmt_ident(const char *name, const char *email, if (!email) { if (strict && ident_use_config_only && !(ident_config_given & IDENT_MAIL_GIVEN)) { - fputs(_(env_hint), stderr); + ident_env_hint(whose_ident); die(_("no email was given and auto-detection is disabled")); } email = ident_default_email(); if (strict && default_email_is_bogus) { - fputs(_(env_hint), stderr); + ident_env_hint(whose_ident); die(_("unable to auto-detect email address (got '%s')"), email); } } @@ -397,13 +415,13 @@ const char *fmt_ident(const char *name, const char *email, if (!name) { if (strict && ident_use_config_only && !(ident_config_given & IDENT_NAME_GIVEN)) { - fputs(_(env_hint), stderr); + ident_env_hint(whose_ident); die(_("no name was given and auto-detection is disabled")); } name = ident_default_name(); using_default = 1; if (strict && default_name_is_bogus) { - fputs(_(env_hint), stderr); + ident_env_hint(whose_ident); die(_("unable to auto-detect name (got '%s')"), name); } } @@ -411,7 +429,7 @@ const char *fmt_ident(const char *name, const char *email, struct passwd *pw; if (strict) { if (using_default) - fputs(_(env_hint), stderr); + ident_env_hint(whose_ident); die(_("empty ident name (for <%s>) not allowed"), email); } pw = xgetpwuid_self(NULL); @@ -421,25 +439,25 @@ const char *fmt_ident(const char *name, const char *email, die(_("name consists only of disallowed characters: %s"), name); } - strbuf_reset(&ident); + strbuf_reset(ident); if (want_name) { - strbuf_addstr_without_crud(&ident, name); - strbuf_addstr(&ident, " <"); + strbuf_addstr_without_crud(ident, name); + strbuf_addstr(ident, " <"); } - strbuf_addstr_without_crud(&ident, email); + strbuf_addstr_without_crud(ident, email); if (want_name) - strbuf_addch(&ident, '>'); + strbuf_addch(ident, '>'); if (want_date) { - strbuf_addch(&ident, ' '); + strbuf_addch(ident, ' '); if (date_str && date_str[0]) { - if (parse_date(date_str, &ident) < 0) + if (parse_date(date_str, ident) < 0) die(_("invalid date format: %s"), date_str); } else - strbuf_addstr(&ident, ident_default_date()); + strbuf_addstr(ident, ident_default_date()); } - return ident.buf; + return ident->buf; } const char *fmt_name(enum want_ident whose_ident) diff --git a/mem-pool.c b/mem-pool.c index a2841a4a9a..8401761dda 100644 --- a/mem-pool.c +++ b/mem-pool.c @@ -12,11 +12,13 @@ * `insert_after`. If `insert_after` is NULL, then insert block at the * head of the linked list. */ -static struct mp_block *mem_pool_alloc_block(struct mem_pool *mem_pool, size_t block_alloc, struct mp_block *insert_after) +static struct mp_block *mem_pool_alloc_block(struct mem_pool *pool, + size_t block_alloc, + struct mp_block *insert_after) { struct mp_block *p; - mem_pool->pool_alloc += sizeof(struct mp_block) + block_alloc; + pool->pool_alloc += sizeof(struct mp_block) + block_alloc; p = xmalloc(st_add(sizeof(struct mp_block), block_alloc)); p->next_free = (char *)p->space; @@ -26,35 +28,27 @@ static struct mp_block *mem_pool_alloc_block(struct mem_pool *mem_pool, size_t b p->next_block = insert_after->next_block; insert_after->next_block = p; } else { - p->next_block = mem_pool->mp_block; - mem_pool->mp_block = p; + p->next_block = pool->mp_block; + pool->mp_block = p; } return p; } -void mem_pool_init(struct mem_pool **mem_pool, size_t initial_size) +void mem_pool_init(struct mem_pool *pool, size_t initial_size) { - struct mem_pool *pool; - - if (*mem_pool) - return; - - pool = xcalloc(1, sizeof(*pool)); - + memset(pool, 0, sizeof(*pool)); pool->block_alloc = BLOCK_GROWTH_SIZE; if (initial_size > 0) mem_pool_alloc_block(pool, initial_size, NULL); - - *mem_pool = pool; } -void mem_pool_discard(struct mem_pool *mem_pool, int invalidate_memory) +void mem_pool_discard(struct mem_pool *pool, int invalidate_memory) { struct mp_block *block, *block_to_free; - block = mem_pool->mp_block; + block = pool->mp_block; while (block) { block_to_free = block; @@ -66,10 +60,11 @@ void mem_pool_discard(struct mem_pool *mem_pool, int invalidate_memory) free(block_to_free); } - free(mem_pool); + pool->mp_block = NULL; + pool->pool_alloc = 0; } -void *mem_pool_alloc(struct mem_pool *mem_pool, size_t len) +void *mem_pool_alloc(struct mem_pool *pool, size_t len) { struct mp_block *p = NULL; void *r; @@ -78,15 +73,15 @@ void *mem_pool_alloc(struct mem_pool *mem_pool, size_t len) if (len & (sizeof(uintmax_t) - 1)) len += sizeof(uintmax_t) - (len & (sizeof(uintmax_t) - 1)); - if (mem_pool->mp_block && - mem_pool->mp_block->end - mem_pool->mp_block->next_free >= len) - p = mem_pool->mp_block; + if (pool->mp_block && + pool->mp_block->end - pool->mp_block->next_free >= len) + p = pool->mp_block; if (!p) { - if (len >= (mem_pool->block_alloc / 2)) - return mem_pool_alloc_block(mem_pool, len, mem_pool->mp_block); + if (len >= (pool->block_alloc / 2)) + return mem_pool_alloc_block(pool, len, pool->mp_block); - p = mem_pool_alloc_block(mem_pool, mem_pool->block_alloc, NULL); + p = mem_pool_alloc_block(pool, pool->block_alloc, NULL); } r = p->next_free; @@ -94,20 +89,38 @@ void *mem_pool_alloc(struct mem_pool *mem_pool, size_t len) return r; } -void *mem_pool_calloc(struct mem_pool *mem_pool, size_t count, size_t size) +void *mem_pool_calloc(struct mem_pool *pool, size_t count, size_t size) { size_t len = st_mult(count, size); - void *r = mem_pool_alloc(mem_pool, len); + void *r = mem_pool_alloc(pool, len); memset(r, 0, len); return r; } -int mem_pool_contains(struct mem_pool *mem_pool, void *mem) +char *mem_pool_strdup(struct mem_pool *pool, const char *str) +{ + size_t len = strlen(str) + 1; + char *ret = mem_pool_alloc(pool, len); + + return memcpy(ret, str, len); +} + +char *mem_pool_strndup(struct mem_pool *pool, const char *str, size_t len) +{ + char *p = memchr(str, '\0', len); + size_t actual_len = (p ? p - str : len); + char *ret = mem_pool_alloc(pool, actual_len+1); + + ret[actual_len] = '\0'; + return memcpy(ret, str, actual_len); +} + +int mem_pool_contains(struct mem_pool *pool, void *mem) { struct mp_block *p; /* Check if memory is allocated in a block */ - for (p = mem_pool->mp_block; p; p = p->next_block) + for (p = pool->mp_block; p; p = p->next_block) if ((mem >= ((void *)p->space)) && (mem < ((void *)p->end))) return 1; diff --git a/mem-pool.h b/mem-pool.h index 999d3c3a52..fe7507f022 100644 --- a/mem-pool.h +++ b/mem-pool.h @@ -24,12 +24,12 @@ struct mem_pool { /* * Initialize mem_pool with specified initial size. */ -void mem_pool_init(struct mem_pool **mem_pool, size_t initial_size); +void mem_pool_init(struct mem_pool *pool, size_t initial_size); /* - * Discard a memory pool and free all the memory it is responsible for. + * Discard all the memory the memory pool is responsible for. */ -void mem_pool_discard(struct mem_pool *mem_pool, int invalidate_memory); +void mem_pool_discard(struct mem_pool *pool, int invalidate_memory); /* * Alloc memory from the mem_pool. @@ -42,6 +42,12 @@ void *mem_pool_alloc(struct mem_pool *pool, size_t len); void *mem_pool_calloc(struct mem_pool *pool, size_t count, size_t size); /* + * Allocate memory from the memory pool and copy str into it. + */ +char *mem_pool_strdup(struct mem_pool *pool, const char *str); +char *mem_pool_strndup(struct mem_pool *pool, const char *str, size_t len); + +/* * Move the memory associated with the 'src' pool to the 'dst' pool. The 'src' * pool will be empty and not contain any memory. It still needs to be free'd * with a call to `mem_pool_discard`. @@ -52,6 +58,6 @@ void mem_pool_combine(struct mem_pool *dst, struct mem_pool *src); * Check if a memory pointed at by 'mem' is part of the range of * memory managed by the specified mem_pool. */ -int mem_pool_contains(struct mem_pool *mem_pool, void *mem); +int mem_pool_contains(struct mem_pool *pool, void *mem); #endif @@ -80,8 +80,8 @@ int checkout_fast_forward(struct repository *r, } memset(&opts, 0, sizeof(opts)); + dir_init(&dir); if (overwrite_ignore) { - memset(&dir, 0, sizeof(dir)); dir.flags |= DIR_SHOW_IGNORED; setup_standard_excludes(&dir); opts.dir = &dir; @@ -102,6 +102,7 @@ int checkout_fast_forward(struct repository *r, clear_unpack_trees_porcelain(&opts); return -1; } + dir_clear(&dir); clear_unpack_trees_porcelain(&opts); if (write_locked_index(r->index, &lock_file, COMMIT_LOCK)) diff --git a/mergetools/bc b/mergetools/bc index 3a69e60faa..a89086ee72 100644 --- a/mergetools/bc +++ b/mergetools/bc @@ -21,3 +21,8 @@ translate_merge_tool_path() { echo bcompare fi } + +list_tool_variants () { + echo bc + echo bc3 +} diff --git a/mergetools/bc3 b/mergetools/bc3 deleted file mode 100644 index 5d8dd48184..0000000000 --- a/mergetools/bc3 +++ /dev/null @@ -1 +0,0 @@ -. "$MERGE_TOOLS_DIR/bc" diff --git a/mergetools/gvimdiff3 b/mergetools/gvimdiff3 deleted file mode 100644 index 04a5bb0ea8..0000000000 --- a/mergetools/gvimdiff3 +++ /dev/null @@ -1 +0,0 @@ -. "$MERGE_TOOLS_DIR/vimdiff" diff --git a/mergetools/gvimdiff2 b/mergetools/nvimdiff index 04a5bb0ea8..04a5bb0ea8 100644 --- a/mergetools/gvimdiff2 +++ b/mergetools/nvimdiff diff --git a/mergetools/vimdiff b/mergetools/vimdiff index 10d86f3e19..abc8ce4ec4 100644 --- a/mergetools/vimdiff +++ b/mergetools/vimdiff @@ -5,7 +5,7 @@ diff_cmd () { merge_cmd () { case "$1" in - gvimdiff|vimdiff) + *vimdiff) if $base_present then "$merge_tool_path" -f -d -c '4wincmd w | wincmd J' \ @@ -15,11 +15,11 @@ merge_cmd () { "$LOCAL" "$MERGED" "$REMOTE" fi ;; - gvimdiff2|vimdiff2) + *vimdiff2) "$merge_tool_path" -f -d -c 'wincmd l' \ "$LOCAL" "$MERGED" "$REMOTE" ;; - gvimdiff3|vimdiff3) + *vimdiff3) if $base_present then "$merge_tool_path" -f -d -c 'hid | hid | hid' \ @@ -34,10 +34,13 @@ merge_cmd () { translate_merge_tool_path() { case "$1" in - gvimdiff|gvimdiff2|gvimdiff3) + nvimdiff*) + echo nvim + ;; + gvimdiff*) echo gvim ;; - vimdiff|vimdiff2|vimdiff3) + vimdiff*) echo vim ;; esac @@ -46,3 +49,11 @@ translate_merge_tool_path() { exit_code_trustable () { true } + +list_tool_variants () { + for prefix in '' g n; do + for suffix in '' 2 3; do + echo "${prefix}vimdiff${suffix}" + done + done +} diff --git a/mergetools/vimdiff2 b/mergetools/vimdiff2 deleted file mode 100644 index 04a5bb0ea8..0000000000 --- a/mergetools/vimdiff2 +++ /dev/null @@ -1 +0,0 @@ -. "$MERGE_TOOLS_DIR/vimdiff" diff --git a/mergetools/vimdiff3 b/mergetools/vimdiff3 deleted file mode 100644 index 04a5bb0ea8..0000000000 --- a/mergetools/vimdiff3 +++ /dev/null @@ -1 +0,0 @@ -. "$MERGE_TOOLS_DIR/vimdiff" @@ -17,7 +17,6 @@ #define MIDX_BYTE_HASH_VERSION 5 #define MIDX_BYTE_NUM_CHUNKS 6 #define MIDX_BYTE_NUM_PACKS 8 -#define MIDX_HASH_VERSION 1 #define MIDX_HEADER_SIZE 12 #define MIDX_MIN_SIZE (MIDX_HEADER_SIZE + the_hash_algo->rawsz) @@ -36,6 +35,18 @@ #define PACK_EXPIRED UINT_MAX +static uint8_t oid_version(void) +{ + switch (hash_algo_by_ptr(the_hash_algo)) { + case GIT_HASH_SHA1: + return 1; + case GIT_HASH_SHA256: + return 2; + default: + die(_("invalid hash version")); + } +} + static char *get_midx_filename(const char *object_dir) { return xstrfmt("%s/pack/multi-pack-index", object_dir); @@ -90,8 +101,11 @@ struct multi_pack_index *load_multi_pack_index(const char *object_dir, int local m->version); hash_version = m->data[MIDX_BYTE_HASH_VERSION]; - if (hash_version != MIDX_HASH_VERSION) - die(_("hash version %u does not match"), hash_version); + if (hash_version != oid_version()) { + error(_("multi-pack-index hash version %u does not match version %u"), + hash_version, oid_version()); + goto cleanup_fail; + } m->hash_len = the_hash_algo->rawsz; m->num_chunks = m->data[MIDX_BYTE_NUM_CHUNKS]; @@ -418,7 +432,7 @@ static size_t write_midx_header(struct hashfile *f, hashwrite_be32(f, MIDX_SIGNATURE); byte_values[0] = MIDX_VERSION; - byte_values[1] = MIDX_HASH_VERSION; + byte_values[1] = oid_version(); byte_values[2] = num_chunks; byte_values[3] = 0; /* unused */ hashwrite(f, byte_values, sizeof(byte_values)); @@ -807,11 +821,9 @@ static int write_midx_internal(const char *object_dir, struct multi_pack_index * int result = 0; midx_name = get_midx_filename(object_dir); - if (safe_create_leading_directories(midx_name)) { - UNLEAK(midx_name); + if (safe_create_leading_directories(midx_name)) die_errno(_("unable to create leading directories of %s"), midx_name); - } if (m) packs.m = m; @@ -1051,10 +1063,8 @@ void clear_midx_file(struct repository *r) r->objects->multi_pack_index = NULL; } - if (remove_path(midx)) { - UNLEAK(midx); + if (remove_path(midx)) die(_("failed to clear multi-pack-index at %s"), midx); - } free(midx); } @@ -1105,8 +1115,17 @@ int verify_midx_file(struct repository *r, const char *object_dir, unsigned flag struct multi_pack_index *m = load_multi_pack_index(object_dir, 1); verify_midx_error = 0; - if (!m) - return 0; + if (!m) { + int result = 0; + struct stat sb; + char *filename = get_midx_filename(object_dir); + if (!stat(filename, &sb)) { + error(_("multi-pack-index file exists, but failed to parse")); + result = 1; + } + free(filename); + return result; + } if (flags & MIDX_PROGRESS) progress = start_progress(_("Looking for referenced packfiles"), @@ -1371,7 +1390,7 @@ static int fill_included_packs_batch(struct repository *r, free(pack_info); - if (total_size < batch_size || packs_to_repack < 2) + if (packs_to_repack < 2) return 1; return 0; @@ -1383,6 +1402,7 @@ int midx_repack(struct repository *r, const char *object_dir, size_t batch_size, uint32_t i; unsigned char *include_pack; struct child_process cmd = CHILD_PROCESS_INIT; + FILE *cmd_in; struct strbuf base_name = STRBUF_INIT; struct multi_pack_index *m = load_multi_pack_index(object_dir, 1); @@ -1435,6 +1455,8 @@ int midx_repack(struct repository *r, const char *object_dir, size_t batch_size, goto cleanup; } + cmd_in = xfdopen(cmd.in, "w"); + for (i = 0; i < m->num_objects; i++) { struct object_id oid; uint32_t pack_int_id = nth_midxed_pack_int_id(m, i); @@ -1443,10 +1465,9 @@ int midx_repack(struct repository *r, const char *object_dir, size_t batch_size, continue; nth_midxed_object_oid(&oid, m, i); - xwrite(cmd.in, oid_to_hex(&oid), the_hash_algo->hexsz); - xwrite(cmd.in, "\n", 1); + fprintf(cmd_in, "%s\n", oid_to_hex(&oid)); } - close(cmd.in); + fclose(cmd_in); if (finish_command(&cmd)) { error(_("could not finish pack-objects")); diff --git a/parse-options.c b/parse-options.c index c57618d537..f0507432ee 100644 --- a/parse-options.c +++ b/parse-options.c @@ -525,7 +525,8 @@ void parse_options_start(struct parse_opt_ctx_t *ctx, parse_options_start_1(ctx, argc, argv, prefix, options, flags); } -static void show_negated_gitcomp(const struct option *opts, int nr_noopts) +static void show_negated_gitcomp(const struct option *opts, int show_all, + int nr_noopts) { int printed_dashdash = 0; @@ -535,7 +536,8 @@ static void show_negated_gitcomp(const struct option *opts, int nr_noopts) if (!opts->long_name) continue; - if (opts->flags & (PARSE_OPT_HIDDEN | PARSE_OPT_NOCOMPLETE)) + if (!show_all && + (opts->flags & (PARSE_OPT_HIDDEN | PARSE_OPT_NOCOMPLETE))) continue; if (opts->flags & PARSE_OPT_NONEG) continue; @@ -572,7 +574,7 @@ static void show_negated_gitcomp(const struct option *opts, int nr_noopts) } } -static int show_gitcomp(const struct option *opts) +static int show_gitcomp(const struct option *opts, int show_all) { const struct option *original_opts = opts; int nr_noopts = 0; @@ -582,7 +584,8 @@ static int show_gitcomp(const struct option *opts) if (!opts->long_name) continue; - if (opts->flags & (PARSE_OPT_HIDDEN | PARSE_OPT_NOCOMPLETE)) + if (!show_all && + (opts->flags & (PARSE_OPT_HIDDEN | PARSE_OPT_NOCOMPLETE))) continue; switch (opts->type) { @@ -610,8 +613,8 @@ static int show_gitcomp(const struct option *opts) nr_noopts++; printf(" --%s%s", opts->long_name, suffix); } - show_negated_gitcomp(original_opts, -1); - show_negated_gitcomp(original_opts, nr_noopts); + show_negated_gitcomp(original_opts, show_all, -1); + show_negated_gitcomp(original_opts, show_all, nr_noopts); fputc('\n', stdout); return PARSE_OPT_COMPLETE; } @@ -723,9 +726,14 @@ int parse_options_step(struct parse_opt_ctx_t *ctx, if (internal_help && ctx->total == 1 && !strcmp(arg + 1, "h")) goto show_usage; - /* lone --git-completion-helper is asked by git-completion.bash */ - if (ctx->total == 1 && !strcmp(arg + 1, "-git-completion-helper")) - return show_gitcomp(options); + /* + * lone --git-completion-helper and --git-completion-helper-all + * are asked by git-completion.bash + */ + if (ctx->total == 1 && !strcmp(arg, "--git-completion-helper")) + return show_gitcomp(options, 0); + if (ctx->total == 1 && !strcmp(arg, "--git-completion-helper-all")) + return show_gitcomp(options, 1); if (arg[1] != '-') { ctx->opt = arg + 1; diff --git a/parse-options.h b/parse-options.h index 46af942093..7030d8f3da 100644 --- a/parse-options.h +++ b/parse-options.h @@ -314,7 +314,7 @@ int parse_opt_passthru_argv(const struct option *, const char *, int); #define OPT__FORCE(var, h, f) OPT_COUNTUP_F('f', "force", (var), (h), (f)) #define OPT__ABBREV(var) \ { OPTION_CALLBACK, 0, "abbrev", (var), N_("n"), \ - N_("use <n> digits to display SHA-1s"), \ + N_("use <n> digits to display object names"), \ PARSE_OPT_OPTARG, &parse_opt_abbrev_cb, 0 } #define OPT__COLOR(var, h) \ OPT_COLOR_FLAG(0, "color", (var), (h)) @@ -1528,8 +1528,6 @@ char *xdg_cache_home(const char *filename) return NULL; } -REPO_GIT_PATH_FUNC(cherry_pick_head, "CHERRY_PICK_HEAD") -REPO_GIT_PATH_FUNC(revert_head, "REVERT_HEAD") REPO_GIT_PATH_FUNC(squash_msg, "SQUASH_MSG") REPO_GIT_PATH_FUNC(merge_msg, "MERGE_MSG") REPO_GIT_PATH_FUNC(merge_rr, "MERGE_RR") @@ -170,8 +170,6 @@ void report_linked_checkout_garbage(void); } struct path_cache { - const char *cherry_pick_head; - const char *revert_head; const char *squash_msg; const char *merge_msg; const char *merge_rr; @@ -182,10 +180,11 @@ struct path_cache { const char *shallow; }; -#define PATH_CACHE_INIT { NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL } +#define PATH_CACHE_INIT \ + { \ + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL \ + } -const char *git_path_cherry_pick_head(struct repository *r); -const char *git_path_revert_head(struct repository *r); const char *git_path_squash_msg(struct repository *r); const char *git_path_merge_msg(struct repository *r); const char *git_path_merge_rr(struct repository *r); diff --git a/perl/Git.pm b/perl/Git.pm index 54c9ed0dde..10df990959 100644 --- a/perl/Git.pm +++ b/perl/Git.pm @@ -723,6 +723,32 @@ sub config_int { return scalar _config_common({'kind' => '--int'}, @_); } +=item config_regexp ( RE ) + +Retrieve the list of configuration key names matching the regular +expression C<RE>. The return value is a list of strings matching +this regex. + +=cut + +sub config_regexp { + my ($self, $regex) = _maybe_self(@_); + try { + my @cmd = ('config', '--name-only', '--get-regexp', $regex); + unshift @cmd, $self if $self; + my @matches = command(@cmd); + return @matches; + } catch Git::Error::Command with { + my $E = shift; + if ($E->value() == 1) { + my @matches = (); + return @matches; + } else { + throw $E; + } + }; +} + # Common subroutine to implement bulk of what the config* family of methods # do. This currently wraps command('config') so it is not so fast. sub _config_common { @@ -6503,7 +6503,7 @@ msgstr "'%s' ne peut pas être résolue comme une branche" #: remote.c:1088 #, c-format msgid "unable to delete '%s': remote ref does not exist" -msgstr "suppression d '%s' impossible : la référence distante n'existe pas" +msgstr "suppression de '%s' impossible : la référence distante n'existe pas" #: remote.c:1100 #, c-format diff --git a/progress.c b/progress.c index 3eda914518..31014e6fca 100644 --- a/progress.c +++ b/progress.c @@ -319,9 +319,12 @@ static void finish_if_sparse(struct progress *progress) void stop_progress(struct progress **p_progress) { + if (!p_progress) + BUG("don't provide NULL to stop_progress"); + finish_if_sparse(*p_progress); - if (p_progress && *p_progress) { + if (*p_progress) { trace2_data_intmax("progress", the_repository, "total_objects", (*p_progress)->total); @@ -338,7 +341,12 @@ void stop_progress(struct progress **p_progress) void stop_progress_msg(struct progress **p_progress, const char *msg) { - struct progress *progress = *p_progress; + struct progress *progress; + + if (!p_progress) + BUG("don't provide NULL to stop_progress_msg"); + + progress = *p_progress; if (!progress) return; *p_progress = NULL; diff --git a/protocol.c b/protocol.c index d1dd3424bb..8d964fc65e 100644 --- a/protocol.c +++ b/protocol.c @@ -21,7 +21,7 @@ enum protocol_version get_protocol_version_config(void) const char *git_test_k = "GIT_TEST_PROTOCOL_VERSION"; const char *git_test_v; - if (!git_config_get_string_const("protocol.version", &value)) { + if (!git_config_get_string_tmp("protocol.version", &value)) { enum protocol_version version = parse_protocol_version(value); if (version == protocol_unknown_version) diff --git a/read-cache.c b/read-cache.c index 8ed1c29b54..fa291cdbee 100644 --- a/read-cache.c +++ b/read-cache.c @@ -89,8 +89,10 @@ static struct mem_pool *find_mem_pool(struct index_state *istate) else pool_ptr = &istate->ce_mem_pool; - if (!*pool_ptr) - mem_pool_init(pool_ptr, 0); + if (!*pool_ptr) { + *pool_ptr = xmalloc(sizeof(**pool_ptr)); + mem_pool_init(*pool_ptr, 0); + } return *pool_ptr; } @@ -2006,11 +2008,12 @@ static unsigned long load_all_cache_entries(struct index_state *istate, { unsigned long consumed; + istate->ce_mem_pool = xmalloc(sizeof(*istate->ce_mem_pool)); if (istate->version == 4) { - mem_pool_init(&istate->ce_mem_pool, + mem_pool_init(istate->ce_mem_pool, estimate_cache_size_from_compressed(istate->cache_nr)); } else { - mem_pool_init(&istate->ce_mem_pool, + mem_pool_init(istate->ce_mem_pool, estimate_cache_size(mmap_size, istate->cache_nr)); } @@ -2070,7 +2073,8 @@ static unsigned long load_cache_entries_threaded(struct index_state *istate, con if (istate->name_hash_initialized) BUG("the name hash isn't thread safe"); - mem_pool_init(&istate->ce_mem_pool, 0); + istate->ce_mem_pool = xmalloc(sizeof(*istate->ce_mem_pool)); + mem_pool_init(istate->ce_mem_pool, 0); /* ensure we have no more threads than we have blocks to process */ if (nr_threads > ieot->nr) @@ -2097,11 +2101,12 @@ static unsigned long load_cache_entries_threaded(struct index_state *istate, con nr = 0; for (j = p->ieot_start; j < p->ieot_start + p->ieot_blocks; j++) nr += p->ieot->entries[j].nr; + istate->ce_mem_pool = xmalloc(sizeof(*istate->ce_mem_pool)); if (istate->version == 4) { - mem_pool_init(&p->ce_mem_pool, + mem_pool_init(p->ce_mem_pool, estimate_cache_size_from_compressed(nr)); } else { - mem_pool_init(&p->ce_mem_pool, + mem_pool_init(p->ce_mem_pool, estimate_cache_size(mmap_size, nr)); } @@ -2358,7 +2363,7 @@ int discard_index(struct index_state *istate) if (istate->ce_mem_pool) { mem_pool_discard(istate->ce_mem_pool, should_validate_cache_entries()); - istate->ce_mem_pool = NULL; + FREE_AND_NULL(istate->ce_mem_pool); } return 0; diff --git a/ref-filter.c b/ref-filter.c index ba85869755..8ba0e31915 100644 --- a/ref-filter.c +++ b/ref-filter.c @@ -345,9 +345,11 @@ static int contents_atom_parser(const struct ref_format *format, struct used_ato atom->u.contents.option = C_SIG; else if (!strcmp(arg, "subject")) atom->u.contents.option = C_SUB; - else if (skip_prefix(arg, "trailers", &arg)) { - skip_prefix(arg, ":", &arg); - if (trailers_atom_parser(format, atom, *arg ? arg : NULL, err)) + else if (!strcmp(arg, "trailers")) { + if (trailers_atom_parser(format, atom, NULL, err)) + return -1; + } else if (skip_prefix(arg, "trailers:", &arg)) { + if (trailers_atom_parser(format, atom, arg, err)) return -1; } else if (skip_prefix(arg, "lines=", &arg)) { atom->u.contents.option = C_LINES; @@ -313,7 +313,7 @@ int read_ref(const char *refname, struct object_id *oid) return read_ref_full(refname, RESOLVE_REF_READING, oid, NULL); } -static int refs_ref_exists(struct ref_store *refs, const char *refname) +int refs_ref_exists(struct ref_store *refs, const char *refname) { return !!refs_resolve_ref_unsafe(refs, refname, RESOLVE_REF_READING, NULL, NULL); } @@ -708,10 +708,9 @@ int dwim_log(const char *str, int len, struct object_id *oid, char **log) static int is_per_worktree_ref(const char *refname) { - return !strcmp(refname, "HEAD") || - starts_with(refname, "refs/worktree/") || - starts_with(refname, "refs/bisect/") || - starts_with(refname, "refs/rewritten/"); + return starts_with(refname, "refs/worktree/") || + starts_with(refname, "refs/bisect/") || + starts_with(refname, "refs/rewritten/"); } static int is_pseudoref_syntax(const char *refname) @@ -771,102 +770,6 @@ long get_files_ref_lock_timeout_ms(void) return timeout_ms; } -static int write_pseudoref(const char *pseudoref, const struct object_id *oid, - const struct object_id *old_oid, struct strbuf *err) -{ - const char *filename; - int fd; - struct lock_file lock = LOCK_INIT; - struct strbuf buf = STRBUF_INIT; - int ret = -1; - - if (!oid) - return 0; - - strbuf_addf(&buf, "%s\n", oid_to_hex(oid)); - - filename = git_path("%s", pseudoref); - fd = hold_lock_file_for_update_timeout(&lock, filename, 0, - get_files_ref_lock_timeout_ms()); - if (fd < 0) { - strbuf_addf(err, _("could not open '%s' for writing: %s"), - filename, strerror(errno)); - goto done; - } - - if (old_oid) { - struct object_id actual_old_oid; - - if (read_ref(pseudoref, &actual_old_oid)) { - if (!is_null_oid(old_oid)) { - strbuf_addf(err, _("could not read ref '%s'"), - pseudoref); - rollback_lock_file(&lock); - goto done; - } - } else if (is_null_oid(old_oid)) { - strbuf_addf(err, _("ref '%s' already exists"), - pseudoref); - rollback_lock_file(&lock); - goto done; - } else if (!oideq(&actual_old_oid, old_oid)) { - strbuf_addf(err, _("unexpected object ID when writing '%s'"), - pseudoref); - rollback_lock_file(&lock); - goto done; - } - } - - if (write_in_full(fd, buf.buf, buf.len) < 0) { - strbuf_addf(err, _("could not write to '%s'"), filename); - rollback_lock_file(&lock); - goto done; - } - - commit_lock_file(&lock); - ret = 0; -done: - strbuf_release(&buf); - return ret; -} - -static int delete_pseudoref(const char *pseudoref, const struct object_id *old_oid) -{ - const char *filename; - - filename = git_path("%s", pseudoref); - - if (old_oid && !is_null_oid(old_oid)) { - struct lock_file lock = LOCK_INIT; - int fd; - struct object_id actual_old_oid; - - fd = hold_lock_file_for_update_timeout( - &lock, filename, 0, - get_files_ref_lock_timeout_ms()); - if (fd < 0) { - error_errno(_("could not open '%s' for writing"), - filename); - return -1; - } - if (read_ref(pseudoref, &actual_old_oid)) - die(_("could not read ref '%s'"), pseudoref); - if (!oideq(&actual_old_oid, old_oid)) { - error(_("unexpected object ID when deleting '%s'"), - pseudoref); - rollback_lock_file(&lock); - return -1; - } - - unlink(filename); - rollback_lock_file(&lock); - } else { - unlink(filename); - } - - return 0; -} - int refs_delete_ref(struct ref_store *refs, const char *msg, const char *refname, const struct object_id *old_oid, @@ -875,11 +778,6 @@ int refs_delete_ref(struct ref_store *refs, const char *msg, struct ref_transaction *transaction; struct strbuf err = STRBUF_INIT; - if (ref_type(refname) == REF_TYPE_PSEUDOREF) { - assert(refs == get_main_ref_store(the_repository)); - return delete_pseudoref(refname, old_oid); - } - transaction = ref_store_transaction_begin(refs, &err); if (!transaction || ref_transaction_delete(transaction, refname, old_oid, @@ -1210,18 +1108,13 @@ int refs_update_ref(struct ref_store *refs, const char *msg, struct strbuf err = STRBUF_INIT; int ret = 0; - if (ref_type(refname) == REF_TYPE_PSEUDOREF) { - assert(refs == get_main_ref_store(the_repository)); - ret = write_pseudoref(refname, new_oid, old_oid, &err); - } else { - t = ref_store_transaction_begin(refs, &err); - if (!t || - ref_transaction_update(t, refname, new_oid, old_oid, - flags, msg, &err) || - ref_transaction_commit(t, &err)) { - ret = 1; - ref_transaction_free(t); - } + t = ref_store_transaction_begin(refs, &err); + if (!t || + ref_transaction_update(t, refname, new_oid, old_oid, flags, msg, + &err) || + ref_transaction_commit(t, &err)) { + ret = 1; + ref_transaction_free(t); } if (ret) { const char *str = _("update_ref failed for ref '%s': %s"); @@ -1634,11 +1527,37 @@ int for_each_rawref(each_ref_fn fn, void *cb_data) return refs_for_each_rawref(get_main_ref_store(the_repository), fn, cb_data); } +static int refs_read_special_head(struct ref_store *ref_store, + const char *refname, struct object_id *oid, + struct strbuf *referent, unsigned int *type) +{ + struct strbuf full_path = STRBUF_INIT; + struct strbuf content = STRBUF_INIT; + int result = -1; + strbuf_addf(&full_path, "%s/%s", ref_store->gitdir, refname); + + if (strbuf_read_file(&content, full_path.buf, 0) < 0) + goto done; + + result = parse_loose_ref_contents(content.buf, oid, referent, type); + +done: + strbuf_release(&full_path); + strbuf_release(&content); + return result; +} + int refs_read_raw_ref(struct ref_store *ref_store, const char *refname, struct object_id *oid, struct strbuf *referent, unsigned int *type) { - return ref_store->be->read_raw_ref(ref_store, refname, oid, referent, type); + if (!strcmp(refname, "FETCH_HEAD") || !strcmp(refname, "MERGE_HEAD")) { + return refs_read_special_head(ref_store, refname, oid, referent, + type); + } + + return ref_store->be->read_raw_ref(ref_store, refname, oid, referent, + type); } /* This function needs to return a meaningful errno on failure */ @@ -2031,24 +1950,17 @@ int ref_update_reject_duplicates(struct string_list *refnames, return 0; } -static const char hook_not_found; -static const char *hook; - static int run_transaction_hook(struct ref_transaction *transaction, const char *state) { struct child_process proc = CHILD_PROCESS_INIT; struct strbuf buf = STRBUF_INIT; + const char *hook; int ret = 0, i; - if (hook == &hook_not_found) - return ret; + hook = find_hook("reference-transaction"); if (!hook) - hook = find_hook("reference-transaction"); - if (!hook) { - hook = &hook_not_found; return ret; - } strvec_pushl(&proc.args, hook, state, NULL); proc.in = -1; @@ -105,6 +105,8 @@ int refs_verify_refname_available(struct ref_store *refs, const struct string_list *skip, struct strbuf *err); +int refs_ref_exists(struct ref_store *refs, const char *refname); + int ref_exists(const char *refname); int should_autocreate_reflog(const char *refname); diff --git a/refs/files-backend.c b/refs/files-backend.c index 985631f33e..dd712e47f4 100644 --- a/refs/files-backend.c +++ b/refs/files-backend.c @@ -67,7 +67,6 @@ struct files_ref_store { struct ref_store base; unsigned int store_flags; - char *gitdir; char *gitcommondir; struct ref_cache *loose; @@ -94,18 +93,17 @@ static struct ref_store *files_ref_store_create(const char *gitdir, struct ref_store *ref_store = (struct ref_store *)refs; struct strbuf sb = STRBUF_INIT; + ref_store->gitdir = xstrdup(gitdir); base_ref_store_init(ref_store, &refs_be_files); refs->store_flags = flags; - refs->gitdir = xstrdup(gitdir); get_common_dir_noenv(&sb, gitdir); refs->gitcommondir = strbuf_detach(&sb, NULL); strbuf_addf(&sb, "%s/packed-refs", refs->gitcommondir); refs->packed_ref_store = packed_ref_store_create(sb.buf, flags); strbuf_release(&sb); - chdir_notify_reparent("files-backend $GIT_DIR", - &refs->gitdir); + chdir_notify_reparent("files-backend $GIT_DIR", &refs->base.gitdir); chdir_notify_reparent("files-backend $GIT_COMMONDIR", &refs->gitcommondir); @@ -176,7 +174,7 @@ static void files_reflog_path(struct files_ref_store *refs, switch (ref_type(refname)) { case REF_TYPE_PER_WORKTREE: case REF_TYPE_PSEUDOREF: - strbuf_addf(sb, "%s/logs/%s", refs->gitdir, refname); + strbuf_addf(sb, "%s/logs/%s", refs->base.gitdir, refname); break; case REF_TYPE_OTHER_PSEUDOREF: case REF_TYPE_MAIN_PSEUDOREF: @@ -198,7 +196,7 @@ static void files_ref_path(struct files_ref_store *refs, switch (ref_type(refname)) { case REF_TYPE_PER_WORKTREE: case REF_TYPE_PSEUDOREF: - strbuf_addf(sb, "%s/%s", refs->gitdir, refname); + strbuf_addf(sb, "%s/%s", refs->base.gitdir, refname); break; case REF_TYPE_MAIN_PSEUDOREF: if (!skip_prefix(refname, "main-worktree/", &refname)) @@ -360,7 +358,6 @@ static int files_read_raw_ref(struct ref_store *ref_store, struct strbuf sb_path = STRBUF_INIT; const char *path; const char *buf; - const char *p; struct stat st; int fd; int ret = -1; @@ -465,6 +462,21 @@ stat_ref: close(fd); strbuf_rtrim(&sb_contents); buf = sb_contents.buf; + + ret = parse_loose_ref_contents(buf, oid, referent, type); + +out: + save_errno = errno; + strbuf_release(&sb_path); + strbuf_release(&sb_contents); + errno = save_errno; + return ret; +} + +int parse_loose_ref_contents(const char *buf, struct object_id *oid, + struct strbuf *referent, unsigned int *type) +{ + const char *p; if (skip_prefix(buf, "ref:", &buf)) { while (isspace(*buf)) buf++; @@ -472,29 +484,19 @@ stat_ref: strbuf_reset(referent); strbuf_addstr(referent, buf); *type |= REF_ISSYMREF; - ret = 0; - goto out; + return 0; } /* - * Please note that FETCH_HEAD has additional - * data after the sha. + * FETCH_HEAD has additional data after the sha. */ if (parse_oid_hex(buf, oid, &p) || (*p != '\0' && !isspace(*p))) { *type |= REF_ISBROKEN; errno = EINVAL; - goto out; + return -1; } - - ret = 0; - -out: - save_errno = errno; - strbuf_release(&sb_path); - strbuf_release(&sb_contents); - errno = save_errno; - return ret; + return 0; } static void unlock_ref(struct ref_lock *lock) @@ -2199,12 +2201,11 @@ static struct ref_iterator *files_reflog_iterator_begin(struct ref_store *ref_st files_downcast(ref_store, REF_STORE_READ, "reflog_iterator_begin"); - if (!strcmp(refs->gitdir, refs->gitcommondir)) { + if (!strcmp(refs->base.gitdir, refs->gitcommondir)) { return reflog_iterator_begin(ref_store, refs->gitcommondir); } else { return merge_ref_iterator_begin( - 0, - reflog_iterator_begin(ref_store, refs->gitdir), + 0, reflog_iterator_begin(ref_store, refs->base.gitdir), reflog_iterator_begin(ref_store, refs->gitcommondir), reflog_iterator_select, refs); } diff --git a/refs/packed-backend.c b/refs/packed-backend.c index 4458a0f69c..b912f2505f 100644 --- a/refs/packed-backend.c +++ b/refs/packed-backend.c @@ -200,6 +200,7 @@ struct ref_store *packed_ref_store_create(const char *path, struct ref_store *ref_store = (struct ref_store *)refs; base_ref_store_init(ref_store, &refs_be_packed); + ref_store->gitdir = xstrdup(path); refs->store_flags = store_flags; refs->path = xstrdup(path); diff --git a/refs/refs-internal.h b/refs/refs-internal.h index 357359a0be..527b0a6e2e 100644 --- a/refs/refs-internal.h +++ b/refs/refs-internal.h @@ -667,14 +667,23 @@ extern struct ref_storage_be refs_be_packed; /* * A representation of the reference store for the main repository or * a submodule. The ref_store instances for submodules are kept in a - * linked list. + * hash map; see get_submodule_ref_store() for more info. */ struct ref_store { /* The backend describing this ref_store's storage scheme: */ const struct ref_storage_be *be; + + /* The gitdir that this ref_store applies to: */ + char *gitdir; }; /* + * Parse contents of a loose ref file. + */ +int parse_loose_ref_contents(const char *buf, struct object_id *oid, + struct strbuf *referent, unsigned int *type); + +/* * Fill in the generic part of refs and add it to our collection of * reference stores. */ @@ -4,6 +4,19 @@ #define TAG_REFSPEC "refs/tags/*:refs/tags/*" extern const struct refspec_item *tag_refspec; +/** + * A struct refspec_item holds the parsed interpretation of a refspec. If it will + * force updates (starts with a '+'), force is true. If it is a pattern + * (sides end with '*') pattern is true. src and dest are the two sides + * (including '*' characters if present); if there is only one side, it is src, + * and dst is NULL; if sides exist but are empty (i.e., the refspec either + * starts or ends with ':'), the corresponding side is "". + * + * remote_find_tracking(), given a remote and a struct refspec_item with either src + * or dst filled out, will fill out the other such that the result is in the + * "fetch" specification for the remote (note that this evaluates patterns and + * returns a single result). + */ struct refspec_item { unsigned force : 1; unsigned pattern : 1; @@ -21,20 +34,8 @@ struct refspec_item { #define REFSPEC_INIT_PUSH { .fetch = REFSPEC_PUSH } /** - * A struct refspec holds the parsed interpretation of a refspec. If it will - * force updates (starts with a '+'), force is true. If it is a pattern - * (sides end with '*') pattern is true. src and dest are the two sides - * (including '*' characters if present); if there is only one side, it is src, - * and dst is NULL; if sides exist but are empty (i.e., the refspec either - * starts or ends with ':'), the corresponding side is "". - * - * An array of strings can be parsed into an array of struct refspecs using + * An array of strings can be parsed into a struct refspec using * parse_fetch_refspec() or parse_push_refspec(). - * - * remote_find_tracking(), given a remote and a struct refspec with either src - * or dst filled out, will fill out the other such that the result is in the - * "fetch" specification for the remote (note that this evaluates patterns and - * returns a single result). */ struct refspec { struct refspec_item *items; diff --git a/revision.c b/revision.c index 3dcf689341..08c2ad23af 100644 --- a/revision.c +++ b/revision.c @@ -1815,7 +1815,7 @@ void repo_init_revisions(struct repository *r, revs->repo = r; revs->abbrev = DEFAULT_ABBREV; - revs->ignore_merges = 1; + revs->ignore_merges = -1; revs->simplify_history = 1; revs->pruning.repo = r; revs->pruning.flags.recursive = 1; @@ -2017,7 +2017,7 @@ static int handle_dotdot(const char *arg, return ret; } -int handle_revision_arg(const char *arg_, struct rev_info *revs, int flags, unsigned revarg_opt) +static int handle_revision_arg_1(const char *arg_, struct rev_info *revs, int flags, unsigned revarg_opt) { struct object_context oc; char *mark; @@ -2092,6 +2092,14 @@ int handle_revision_arg(const char *arg_, struct rev_info *revs, int flags, unsi return 0; } +int handle_revision_arg(const char *arg, struct rev_info *revs, int flags, unsigned revarg_opt) +{ + int ret = handle_revision_arg_1(arg, revs, flags, revarg_opt); + if (!ret) + revs->rev_input_given = 1; + return ret; +} + static void read_pathspec_from_stdin(struct strbuf *sb, struct strvec *prune) { @@ -2345,6 +2353,15 @@ static int handle_revision_opt(struct rev_info *revs, int argc, const char **arg revs->diffopt.flags.tree_in_recursive = 1; } else if (!strcmp(arg, "-m")) { revs->ignore_merges = 0; + } else if ((argcount = parse_long_opt("diff-merges", argv, &optarg))) { + if (!strcmp(optarg, "off")) { + revs->ignore_merges = 1; + } else { + die(_("unknown value for --diff-merges: %s"), optarg); + } + return argcount; + } else if (!strcmp(arg, "--no-diff-merges")) { + revs->ignore_merges = 1; } else if (!strcmp(arg, "-c")) { revs->diff = 1; revs->dense_combined_merges = 0; @@ -2694,7 +2711,7 @@ static void NORETURN diagnose_missing_default(const char *def) */ int setup_revisions(int argc, const char **argv, struct rev_info *revs, struct setup_revision_opt *opt) { - int i, flags, left, seen_dashdash, got_rev_arg = 0, revarg_opt; + int i, flags, left, seen_dashdash, revarg_opt; struct strvec prune_data = STRVEC_INIT; const char *submodule = NULL; int seen_end_of_options = 0; @@ -2783,8 +2800,6 @@ int setup_revisions(int argc, const char **argv, struct rev_info *revs, struct s strvec_pushv(&prune_data, argv + i); break; } - else - got_rev_arg = 1; } if (prune_data.nr) { @@ -2813,7 +2828,7 @@ int setup_revisions(int argc, const char **argv, struct rev_info *revs, struct s opt->tweak(revs, opt); if (revs->show_merge) prepare_show_merge(revs); - if (revs->def && !revs->pending.nr && !revs->rev_input_given && !got_rev_arg) { + if (revs->def && !revs->pending.nr && !revs->rev_input_given) { struct object_id oid; struct object *object; struct object_context oc; @@ -2854,8 +2869,10 @@ int setup_revisions(int argc, const char **argv, struct rev_info *revs, struct s copy_pathspec(&revs->diffopt.pathspec, &revs->prune_data); } - if (revs->combine_merges) + if (revs->combine_merges && revs->ignore_merges < 0) revs->ignore_merges = 0; + if (revs->ignore_merges < 0) + revs->ignore_merges = 1; if (revs->combined_all_paths && !revs->combine_merges) die("--combined-all-paths makes no sense without -c or --cc"); @@ -2889,9 +2906,6 @@ int setup_revisions(int argc, const char **argv, struct rev_info *revs, struct s if (!revs->reflog_info && revs->grep_filter.use_reflog_filter) die("cannot use --grep-reflog without --walk-reflogs"); - if (revs->first_parent_only && revs->bisect) - die(_("--first-parent is incompatible with --bisect")); - if (revs->line_level_traverse && (revs->diffopt.output_format & ~(DIFF_FORMAT_PATCH | DIFF_FORMAT_NO_OUTPUT))) die(_("-L does not yet support diff formats besides -p and -s")); diff --git a/revision.h b/revision.h index 889216c2d8..c1e5bcf139 100644 --- a/revision.h +++ b/revision.h @@ -190,11 +190,11 @@ struct rev_info { show_root_diff:1, no_commit_id:1, verbose_header:1, - ignore_merges:1, combine_merges:1, combined_all_paths:1, dense_combined_merges:1, always_show_header:1; + int ignore_merges:2; /* Format info */ int show_notes; diff --git a/sequencer.c b/sequencer.c index cc3f8fa88e..e8676e965f 100644 --- a/sequencer.c +++ b/sequencer.c @@ -150,6 +150,8 @@ static GIT_PATH_FUNC(rebase_path_refs_to_delete, "rebase-merge/refs-to-delete") * command-line. */ static GIT_PATH_FUNC(rebase_path_gpg_sign_opt, "rebase-merge/gpg_sign_opt") +static GIT_PATH_FUNC(rebase_path_cdate_is_adate, "rebase-merge/cdate_is_adate") +static GIT_PATH_FUNC(rebase_path_ignore_date, "rebase-merge/ignore_date") static GIT_PATH_FUNC(rebase_path_orig_head, "rebase-merge/orig-head") static GIT_PATH_FUNC(rebase_path_verbose, "rebase-merge/verbose") static GIT_PATH_FUNC(rebase_path_quiet, "rebase-merge/quiet") @@ -303,6 +305,8 @@ int sequencer_remove_state(struct replay_opts *opts) } } + free(opts->committer_name); + free(opts->committer_email); free(opts->gpg_sign); free(opts->strategy); for (i = 0; i < opts->xopts_nr; i++) @@ -355,7 +359,7 @@ static int get_message(struct commit *commit, struct commit_message *out) subject_len = find_commit_subject(out->message, &subject); out->subject = xmemdupz(subject, subject_len); - out->label = xstrfmt("%s... %s", abbrev, out->subject); + out->label = xstrfmt("%s (%s)", abbrev, out->subject); out->parent_label = xstrfmt("parent of %s", out->label); return 0; @@ -381,7 +385,8 @@ static void print_advice(struct repository *r, int show_hint, * (typically rebase --interactive) wants to take care * of the commit itself so remove CHERRY_PICK_HEAD */ - unlink(git_path_cherry_pick_head(r)); + refs_delete_ref(get_main_ref_store(r), "", "CHERRY_PICK_HEAD", + NULL, 0); return; } @@ -863,6 +868,22 @@ static char *get_author(const char *message) return NULL; } +static const char *author_date_from_env_array(const struct strvec *env) +{ + int i; + const char *date; + + for (i = 0; i < env->nr; i++) + if (skip_prefix(env->v[i], + "GIT_AUTHOR_DATE=", &date)) + return date; + /* + * If GIT_AUTHOR_DATE is missing we should have already errored out when + * reading the script + */ + BUG("GIT_AUTHOR_DATE missing from author script"); +} + static const char staged_changes_advice[] = N_("you have staged changes in your working tree\n" "If these changes are meant to be squashed into the previous commit, run:\n" @@ -929,6 +950,14 @@ static int run_git_commit(struct repository *r, gpg_opt, gpg_opt); } + if (opts->committer_date_is_author_date) + strvec_pushf(&cmd.env_array, "GIT_COMMITTER_DATE=%s", + opts->ignore_date ? + "" : + author_date_from_env_array(&cmd.env_array)); + if (opts->ignore_date) + strvec_push(&cmd.env_array, "GIT_AUTHOR_DATE="); + strvec_push(&cmd.args, "commit"); if (!(flags & VERIFY_MSG)) @@ -1308,6 +1337,7 @@ static int try_to_commit(struct repository *r, struct strbuf err = STRBUF_INIT; struct strbuf commit_msg = STRBUF_INIT; char *amend_author = NULL; + const char *committer = NULL; const char *hook_commit = NULL; enum commit_msg_cleanup_mode cleanup; int res = 0; @@ -1399,10 +1429,57 @@ static int try_to_commit(struct repository *r, goto out; } - reset_ident_date(); + if (opts->committer_date_is_author_date) { + struct ident_split id; + struct strbuf date = STRBUF_INIT; + + if (!opts->ignore_date) { + if (split_ident_line(&id, author, (int)strlen(author)) < 0) { + res = error(_("invalid author identity '%s'"), + author); + goto out; + } + if (!id.date_begin) { + res = error(_( + "corrupt author: missing date information")); + goto out; + } + strbuf_addf(&date, "@%.*s %.*s", + (int)(id.date_end - id.date_begin), + id.date_begin, + (int)(id.tz_end - id.tz_begin), + id.tz_begin); + } else { + reset_ident_date(); + } + committer = fmt_ident(opts->committer_name, + opts->committer_email, + WANT_COMMITTER_IDENT, + opts->ignore_date ? NULL : date.buf, + IDENT_STRICT); + strbuf_release(&date); + } else { + reset_ident_date(); + } + + if (opts->ignore_date) { + struct ident_split id; + char *name, *email; + + if (split_ident_line(&id, author, strlen(author)) < 0) { + error(_("invalid author identity '%s'"), author); + goto out; + } + name = xmemdupz(id.name_begin, id.name_end - id.name_begin); + email = xmemdupz(id.mail_begin, id.mail_end - id.mail_begin); + author = fmt_ident(name, email, WANT_AUTHOR_IDENT, NULL, + IDENT_STRICT); + free(name); + free(email); + } - if (commit_tree_extended(msg->buf, msg->len, &tree, parents, - oid, author, opts->gpg_sign, extra)) { + if (commit_tree_extended(msg->buf, msg->len, &tree, parents, oid, + author, committer, opts->gpg_sign, extra)) { res = error(_("failed to write commit object")); goto out; } @@ -1455,7 +1532,8 @@ static int do_commit(struct repository *r, author, opts, flags, &oid); strbuf_release(&sb); if (!res) { - unlink(git_path_cherry_pick_head(r)); + refs_delete_ref(get_main_ref_store(r), "", + "CHERRY_PICK_HEAD", NULL, 0); unlink(git_path_merge_msg(r)); if (!is_rebase_i(opts)) print_commit_summary(r, NULL, &oid, @@ -1966,7 +2044,8 @@ static int do_pick_commit(struct repository *r, flags |= ALLOW_EMPTY; } else if (allow == 2) { drop_commit = 1; - unlink(git_path_cherry_pick_head(r)); + refs_delete_ref(get_main_ref_store(r), "", "CHERRY_PICK_HEAD", + NULL, 0); unlink(git_path_merge_msg(r)); fprintf(stderr, _("dropping %s %s -- patch contents already upstream\n"), @@ -2305,15 +2384,19 @@ void sequencer_post_commit_cleanup(struct repository *r, int verbose) struct replay_opts opts = REPLAY_OPTS_INIT; int need_cleanup = 0; - if (file_exists(git_path_cherry_pick_head(r))) { - if (!unlink(git_path_cherry_pick_head(r)) && verbose) + if (refs_ref_exists(get_main_ref_store(r), "CHERRY_PICK_HEAD")) { + if (!refs_delete_ref(get_main_ref_store(r), "", + "CHERRY_PICK_HEAD", NULL, 0) && + verbose) warning(_("cancelling a cherry picking in progress")); opts.action = REPLAY_PICK; need_cleanup = 1; } - if (file_exists(git_path_revert_head(r))) { - if (!unlink(git_path_revert_head(r)) && verbose) + if (refs_ref_exists(get_main_ref_store(r), "REVERT_HEAD")) { + if (!refs_delete_ref(get_main_ref_store(r), "", "REVERT_HEAD", + NULL, 0) && + verbose) warning(_("cancelling a revert in progress")); opts.action = REPLAY_REVERT; need_cleanup = 1; @@ -2528,6 +2611,16 @@ static int read_populate_opts(struct replay_opts *opts) opts->signoff = 1; } + if (file_exists(rebase_path_cdate_is_adate())) { + opts->allow_ff = 0; + opts->committer_date_is_author_date = 1; + } + + if (file_exists(rebase_path_ignore_date())) { + opts->allow_ff = 0; + opts->ignore_date = 1; + } + if (file_exists(rebase_path_reschedule_failed_exec())) opts->reschedule_failed_exec = 1; @@ -2623,6 +2716,10 @@ int write_basic_state(struct replay_opts *opts, const char *head_name, write_file(rebase_path_drop_redundant_commits(), "%s", ""); if (opts->keep_redundant_commits) write_file(rebase_path_keep_redundant_commits(), "%s", ""); + if (opts->committer_date_is_author_date) + write_file(rebase_path_cdate_is_adate(), "%s", ""); + if (opts->ignore_date) + write_file(rebase_path_ignore_date(), "%s", ""); if (opts->reschedule_failed_exec) write_file(rebase_path_reschedule_failed_exec(), "%s", ""); @@ -2671,8 +2768,9 @@ static int create_seq_dir(struct repository *r) enum replay_action action; const char *in_progress_error = NULL; const char *in_progress_advice = NULL; - unsigned int advise_skip = file_exists(git_path_revert_head(r)) || - file_exists(git_path_cherry_pick_head(r)); + unsigned int advise_skip = + refs_ref_exists(get_main_ref_store(r), "REVERT_HEAD") || + refs_ref_exists(get_main_ref_store(r), "CHERRY_PICK_HEAD"); if (!sequencer_get_last_command(r, &action)) { switch (action) { @@ -2771,8 +2869,8 @@ static int rollback_single_pick(struct repository *r) { struct object_id head_oid; - if (!file_exists(git_path_cherry_pick_head(r)) && - !file_exists(git_path_revert_head(r))) + if (!refs_ref_exists(get_main_ref_store(r), "CHERRY_PICK_HEAD") && + !refs_ref_exists(get_main_ref_store(r), "REVERT_HEAD")) return error(_("no cherry-pick or revert in progress")); if (read_ref_full("HEAD", 0, &head_oid, NULL)) return error(_("cannot resolve HEAD")); @@ -2866,7 +2964,7 @@ int sequencer_skip(struct repository *r, struct replay_opts *opts) */ switch (opts->action) { case REPLAY_REVERT: - if (!file_exists(git_path_revert_head(r))) { + if (!refs_ref_exists(get_main_ref_store(r), "REVERT_HEAD")) { if (action != REPLAY_REVERT) return error(_("no revert in progress")); if (!rollback_is_safe()) @@ -2874,7 +2972,8 @@ int sequencer_skip(struct repository *r, struct replay_opts *opts) } break; case REPLAY_PICK: - if (!file_exists(git_path_cherry_pick_head(r))) { + if (!refs_ref_exists(get_main_ref_store(r), + "CHERRY_PICK_HEAD")) { if (action != REPLAY_PICK) return error(_("no cherry-pick in progress")); if (!rollback_is_safe()) @@ -3543,6 +3642,14 @@ static int do_merge(struct repository *r, goto leave_merge; } + if (opts->committer_date_is_author_date) + strvec_pushf(&cmd.env_array, "GIT_COMMITTER_DATE=%s", + opts->ignore_date ? + "" : + author_date_from_env_array(&cmd.env_array)); + if (opts->ignore_date) + strvec_push(&cmd.env_array, "GIT_AUTHOR_DATE="); + cmd.git_cmd = 1; strvec_push(&cmd.args, "merge"); strvec_push(&cmd.args, "-s"); @@ -3569,7 +3676,8 @@ static int do_merge(struct repository *r, oid_to_hex(&j->item->object.oid)); strbuf_release(&ref_name); - unlink(git_path_cherry_pick_head(r)); + refs_delete_ref(get_main_ref_store(r), "", "CHERRY_PICK_HEAD", + NULL, 0); rollback_lock_file(&lock); rollback_lock_file(&lock); @@ -3906,7 +4014,9 @@ static int pick_commits(struct repository *r, prev_reflog_action = xstrdup(getenv(GIT_REFLOG_ACTION)); if (opts->allow_ff) assert(!(opts->signoff || opts->no_commit || - opts->record_origin || opts->edit)); + opts->record_origin || opts->edit || + opts->committer_date_is_author_date || + opts->ignore_date)); if (read_and_refresh_cache(r, opts)) return -1; @@ -4201,8 +4311,8 @@ static int continue_single_pick(struct repository *r) { const char *argv[] = { "commit", NULL }; - if (!file_exists(git_path_cherry_pick_head(r)) && - !file_exists(git_path_revert_head(r))) + if (!refs_ref_exists(get_main_ref_store(r), "CHERRY_PICK_HEAD") && + !refs_ref_exists(get_main_ref_store(r), "REVERT_HEAD")) return error(_("no cherry-pick or revert in progress")); return run_command_v_opt(argv, RUN_GIT_CMD); } @@ -4318,9 +4428,10 @@ static int commit_staged_changes(struct repository *r, } if (is_clean) { - const char *cherry_pick_head = git_path_cherry_pick_head(r); - - if (file_exists(cherry_pick_head) && unlink(cherry_pick_head)) + if (refs_ref_exists(get_main_ref_store(r), + "CHERRY_PICK_HEAD") && + refs_delete_ref(get_main_ref_store(r), "", + "CHERRY_PICK_HEAD", NULL, 0)) return error(_("could not remove CHERRY_PICK_HEAD")); if (!final_fixup) return 0; @@ -4347,6 +4458,22 @@ static int commit_staged_changes(struct repository *r, return 0; } +static int init_committer(struct replay_opts *opts) +{ + struct ident_split id; + const char *committer; + + committer = git_committer_info(IDENT_STRICT); + if (split_ident_line(&id, committer, strlen(committer)) < 0) + return error(_("invalid committer '%s'"), committer); + opts->committer_name = + xmemdupz(id.name_begin, id.name_end - id.name_begin); + opts->committer_email = + xmemdupz(id.mail_begin, id.mail_end - id.mail_end); + + return 0; +} + int sequencer_continue(struct repository *r, struct replay_opts *opts) { struct todo_list todo_list = TODO_LIST_INIT; @@ -4358,6 +4485,9 @@ int sequencer_continue(struct repository *r, struct replay_opts *opts) if (read_populate_opts(opts)) return -1; if (is_rebase_i(opts)) { + if (opts->committer_date_is_author_date && init_committer(opts)) + return -1; + if ((res = read_populate_todo(r, &todo_list, opts))) goto release_todo_list; @@ -4379,8 +4509,9 @@ int sequencer_continue(struct repository *r, struct replay_opts *opts) if (!is_rebase_i(opts)) { /* Verify that the conflict has been resolved */ - if (file_exists(git_path_cherry_pick_head(r)) || - file_exists(git_path_revert_head(r))) { + if (refs_ref_exists(get_main_ref_store(r), + "CHERRY_PICK_HEAD") || + refs_ref_exists(get_main_ref_store(r), "REVERT_HEAD")) { res = continue_single_pick(r); if (res) goto release_todo_list; @@ -5178,13 +5309,14 @@ int complete_action(struct repository *r, struct replay_opts *opts, unsigned fla struct string_list *commands, unsigned autosquash, struct todo_list *todo_list) { - const char *shortonto, *todo_file = rebase_path_todo(); + char shortonto[GIT_MAX_HEXSZ + 1]; + const char *todo_file = rebase_path_todo(); struct todo_list new_todo = TODO_LIST_INIT; struct strbuf *buf = &todo_list->buf, buf2 = STRBUF_INIT; struct object_id oid = onto->object.oid; int res; - shortonto = find_unique_abbrev(&oid, DEFAULT_ABBREV); + find_unique_abbrev_r(shortonto, &oid, DEFAULT_ABBREV); if (buf->len == 0) { struct todo_item *item = append_new_todo(todo_list); @@ -5250,6 +5382,9 @@ int complete_action(struct repository *r, struct replay_opts *opts, unsigned fla res = -1; + if (opts->committer_date_is_author_date && init_committer(opts)) + goto cleanup; + if (checkout_onto(r, opts, onto_name, &oid, orig_head)) goto cleanup; @@ -5442,7 +5577,7 @@ int todo_list_rearrange_squash(struct todo_list *todo_list) int sequencer_determine_whence(struct repository *r, enum commit_whence *whence) { - if (file_exists(git_path_cherry_pick_head(r))) { + if (refs_ref_exists(get_main_ref_store(r), "CHERRY_PICK_HEAD")) { struct object_id cherry_pick_head, rebase_head; if (file_exists(git_path_seq_dir())) diff --git a/sequencer.h b/sequencer.h index d31c41f018..b2a501e445 100644 --- a/sequencer.h +++ b/sequencer.h @@ -45,9 +45,13 @@ struct replay_opts { int verbose; int quiet; int reschedule_failed_exec; + int committer_date_is_author_date; + int ignore_date; int mainline; + char *committer_name; + char *committer_email; char *gpg_sign; enum commit_msg_cleanup_mode default_msg_cleanup; int explicit_cleanup; diff --git a/sideband.c b/sideband.c index ef851113c4..0a60662fa6 100644 --- a/sideband.c +++ b/sideband.c @@ -147,7 +147,7 @@ int demultiplex_sideband(const char *me, char *buf, int len, switch (band) { case 3: if (die_on_error) - die("remote error: %s", buf + 1); + die(_("remote error: %s"), buf + 1); strbuf_addf(scratch, "%s%s", scratch->len ? "\n" : "", DISPLAY_PREFIX); maybe_colorize_sideband(scratch, buf + 1, len); diff --git a/split-index.c b/split-index.c index e6154e4ea9..c0e8ad670d 100644 --- a/split-index.c +++ b/split-index.c @@ -79,8 +79,10 @@ void move_cache_to_base_index(struct index_state *istate) if (si->base && si->base->ce_mem_pool) { - if (!istate->ce_mem_pool) - mem_pool_init(&istate->ce_mem_pool, 0); + if (!istate->ce_mem_pool) { + istate->ce_mem_pool = xmalloc(sizeof(struct mem_pool)); + mem_pool_init(istate->ce_mem_pool, 0); + } mem_pool_combine(istate->ce_mem_pool, istate->split_index->base->ce_mem_pool); } diff --git a/submodule.c b/submodule.c index a52b93a87f..d0b70ca536 100644 --- a/submodule.c +++ b/submodule.c @@ -194,7 +194,7 @@ void set_diffopt_flags_from_submodule_config(struct diff_options *diffopt, char *key; key = xstrfmt("submodule.%s.ignore", submodule->name); - if (repo_config_get_string_const(the_repository, key, &ignore)) + if (repo_config_get_string_tmp(the_repository, key, &ignore)) ignore = submodule->ignore; free(key); @@ -1299,7 +1299,7 @@ static int get_fetch_recurse_config(const struct submodule *submodule, int fetch_recurse = submodule->fetch_recurse; key = xstrfmt("submodule.%s.fetchRecurseSubmodules", submodule->name); - if (!repo_config_get_string_const(spf->r, key, &value)) { + if (!repo_config_get_string_tmp(spf->r, key, &value)) { fetch_recurse = parse_fetch_recurse_submodules_arg(key, value); } free(key); @@ -421,6 +421,10 @@ GIT_TEST_DISALLOW_ABBREVIATED_OPTIONS=<boolean>, when true (which is the default when running tests), errors out when an abbreviated option is used. +GIT_TEST_DEFAULT_HASH=<hash-algo> specifies which hash algorithm to +use in the test scripts. Recognized values for <hash-algo> are "sha1" +and "sha256". + Naming Tests ------------ diff --git a/t/helper/test-config.c b/t/helper/test-config.c index 234c722b48..a6e936721f 100644 --- a/t/helper/test-config.c +++ b/t/helper/test-config.c @@ -126,7 +126,7 @@ int cmd__config(int argc, const char **argv) goto exit1; } } else if (argc == 3 && !strcmp(argv[1], "get_string")) { - if (!git_config_get_string_const(argv[2], &v)) { + if (!git_config_get_string_tmp(argv[2], &v)) { printf("%s\n", v); goto exit0; } else { diff --git a/t/helper/test-read-midx.c b/t/helper/test-read-midx.c index 831b586d02..2430880f78 100644 --- a/t/helper/test-read-midx.c +++ b/t/helper/test-read-midx.c @@ -7,14 +7,18 @@ static int read_midx_file(const char *object_dir) { uint32_t i; - struct multi_pack_index *m = load_multi_pack_index(object_dir, 1); + struct multi_pack_index *m; + + setup_git_directory(); + m = load_multi_pack_index(object_dir, 1); if (!m) return 1; - printf("header: %08x %d %d %d\n", + printf("header: %08x %d %d %d %d\n", m->signature, m->version, + m->hash_len, m->num_chunks, m->num_packs); diff --git a/t/perf/README b/t/perf/README index c7b70e2d28..bd649afa97 100644 --- a/t/perf/README +++ b/t/perf/README @@ -84,6 +84,15 @@ You can set the following variables (also in your config.mak): probably be about linux.git size for optimal results. Both default to the git.git you are running from. + GIT_PERF_EXTRA + Boolean to enable additional tests. Most test scripts are + written to detect regressions between two versions of Git, and + the output will compare timings for individual tests between + those versions. Some scripts have additional tests which are not + run by default, that show patterns within a single version of + Git (e.g., performance of index-pack as the number of threads + changes). These can be enabled with GIT_PERF_EXTRA. + You can also pass the options taken by ordinary git tests; the most useful one is: diff --git a/t/perf/p1400-update-ref.sh b/t/perf/p1400-update-ref.sh index d275a81248..ce5ac3ed85 100755 --- a/t/perf/p1400-update-ref.sh +++ b/t/perf/p1400-update-ref.sh @@ -7,11 +7,13 @@ test_description="Tests performance of update-ref" test_perf_fresh_repo test_expect_success "setup" ' + git init --bare target-repo.git && test_commit PRE && test_commit POST && printf "create refs/heads/%d PRE\n" $(test_seq 1000) >create && printf "update refs/heads/%d POST PRE\n" $(test_seq 1000) >update && - printf "delete refs/heads/%d POST\n" $(test_seq 1000) >delete + printf "delete refs/heads/%d POST\n" $(test_seq 1000) >delete && + git update-ref --stdin <create ' test_perf "update-ref" ' @@ -24,9 +26,14 @@ test_perf "update-ref" ' ' test_perf "update-ref --stdin" ' - git update-ref --stdin <create && git update-ref --stdin <update && - git update-ref --stdin <delete + git update-ref --stdin <delete && + git update-ref --stdin <create +' + +test_perf "nonatomic push" ' + git push ./target-repo.git $(test_seq 1000) && + git push --delete ./target-repo.git $(test_seq 1000) ' test_done diff --git a/t/perf/p5302-pack-index.sh b/t/perf/p5302-pack-index.sh index a9b3e112d9..228593d9ad 100755 --- a/t/perf/p5302-pack-index.sh +++ b/t/perf/p5302-pack-index.sh @@ -13,35 +13,36 @@ test_expect_success 'repack' ' export PACK ' -test_perf 'index-pack 0 threads' ' - rm -rf repo.git && - git init --bare repo.git && - GIT_DIR=repo.git git index-pack --threads=1 --stdin < $PACK -' - -test_perf 'index-pack 1 thread ' ' - rm -rf repo.git && - git init --bare repo.git && - GIT_DIR=repo.git GIT_FORCE_THREADS=1 git index-pack --threads=1 --stdin < $PACK +# Rather than counting up and doubling each time, count down from the endpoint, +# halving each time. That ensures that our final test uses as many threads as +# CPUs, even if it isn't a power of 2. +test_expect_success 'set up thread-counting tests' ' + t=$(test-tool online-cpus) && + threads= && + while test $t -gt 0 + do + threads="$t $threads" + t=$((t / 2)) + done ' -test_perf 'index-pack 2 threads' ' +test_perf PERF_EXTRA 'index-pack 0 threads' ' rm -rf repo.git && git init --bare repo.git && - GIT_DIR=repo.git git index-pack --threads=2 --stdin < $PACK -' - -test_perf 'index-pack 4 threads' ' - rm -rf repo.git && - git init --bare repo.git && - GIT_DIR=repo.git git index-pack --threads=4 --stdin < $PACK + GIT_DIR=repo.git git index-pack --threads=1 --stdin < $PACK ' -test_perf 'index-pack 8 threads' ' - rm -rf repo.git && - git init --bare repo.git && - GIT_DIR=repo.git git index-pack --threads=8 --stdin < $PACK -' +for t in $threads +do + THREADS=$t + export THREADS + test_perf PERF_EXTRA "index-pack $t threads" ' + rm -rf repo.git && + git init --bare repo.git && + GIT_DIR=repo.git GIT_FORCE_THREADS=1 \ + git index-pack --threads=$THREADS --stdin <$PACK + ' +done test_perf 'index-pack default number of threads' ' rm -rf repo.git && diff --git a/t/perf/perf-lib.sh b/t/perf/perf-lib.sh index 13e389367a..821581a885 100644 --- a/t/perf/perf-lib.sh +++ b/t/perf/perf-lib.sh @@ -245,3 +245,5 @@ test_at_end_hook_ () { test_export () { export "$@" } + +test_lazy_prereq PERF_EXTRA 'test_bool_env GIT_PERF_EXTRA false' diff --git a/t/t0001-init.sh b/t/t0001-init.sh index d71d4c7238..50222a10c5 100755 --- a/t/t0001-init.sh +++ b/t/t0001-init.sh @@ -316,6 +316,19 @@ test_expect_success 'init with separate gitdir' ' test_path_is_dir realgitdir/refs ' +test_expect_success 'explicit bare & --separate-git-dir incompatible' ' + test_must_fail git init --bare --separate-git-dir goop.git bare.git 2>err && + test_i18ngrep "mutually exclusive" err +' + +test_expect_success 'implicit bare & --separate-git-dir incompatible' ' + test_when_finished "rm -rf bare.git" && + mkdir -p bare.git && + test_must_fail env GIT_DIR=. \ + git -C bare.git init --separate-git-dir goop.git 2>err && + test_i18ngrep "incompatible" err +' + test_lazy_prereq GETCWD_IGNORES_PERMS ' base=GETCWD_TEST_BASE_DIR && mkdir -p $base/dir && diff --git a/t/t0040-parse-options.sh b/t/t0040-parse-options.sh index f8178ee4e3..14cafc138b 100755 --- a/t/t0040-parse-options.sh +++ b/t/t0040-parse-options.sh @@ -44,7 +44,7 @@ Magic arguments --no-ambiguous negative ambiguity Standard options - --abbrev[=<n>] use <n> digits to display SHA-1s + --abbrev[=<n>] use <n> digits to display object names -v, --verbose be verbose -n, --dry-run dry run -q, --quiet be quiet diff --git a/t/t1400-update-ref.sh b/t/t1400-update-ref.sh index d0d36750bc..770e7be363 100755 --- a/t/t1400-update-ref.sh +++ b/t/t1400-update-ref.sh @@ -160,10 +160,10 @@ test_expect_success 'core.logAllRefUpdates=always creates reflog by default' ' git reflog exists $outside ' -test_expect_success 'core.logAllRefUpdates=always creates no reflog for ORIG_HEAD' ' +test_expect_success 'core.logAllRefUpdates=always creates reflog for ORIG_HEAD' ' test_config core.logAllRefUpdates always && git update-ref ORIG_HEAD $A && - test_must_fail git reflog exists ORIG_HEAD + git reflog exists ORIG_HEAD ' test_expect_success '--no-create-reflog overrides core.logAllRefUpdates=always' ' @@ -475,57 +475,57 @@ test_expect_success 'git cat-file blob master@{2005-05-26 23:42}:F (expect OTHER test_expect_success 'given old value for missing pseudoref, do not create' ' test_must_fail git update-ref PSEUDOREF $A $B 2>err && - test_path_is_missing .git/PSEUDOREF && - test_i18ngrep "could not read ref" err + test_must_fail git rev-parse PSEUDOREF && + test_i18ngrep "unable to resolve reference" err ' test_expect_success 'create pseudoref' ' git update-ref PSEUDOREF $A && - test $A = $(cat .git/PSEUDOREF) + test $A = $(git rev-parse PSEUDOREF) ' test_expect_success 'overwrite pseudoref with no old value given' ' git update-ref PSEUDOREF $B && - test $B = $(cat .git/PSEUDOREF) + test $B = $(git rev-parse PSEUDOREF) ' test_expect_success 'overwrite pseudoref with correct old value' ' git update-ref PSEUDOREF $C $B && - test $C = $(cat .git/PSEUDOREF) + test $C = $(git rev-parse PSEUDOREF) ' test_expect_success 'do not overwrite pseudoref with wrong old value' ' test_must_fail git update-ref PSEUDOREF $D $E 2>err && - test $C = $(cat .git/PSEUDOREF) && - test_i18ngrep "unexpected object ID" err + test $C = $(git rev-parse PSEUDOREF) && + test_i18ngrep "cannot lock ref.*expected" err ' test_expect_success 'delete pseudoref' ' git update-ref -d PSEUDOREF && - test_path_is_missing .git/PSEUDOREF + test_must_fail git rev-parse PSEUDOREF ' test_expect_success 'do not delete pseudoref with wrong old value' ' git update-ref PSEUDOREF $A && test_must_fail git update-ref -d PSEUDOREF $B 2>err && - test $A = $(cat .git/PSEUDOREF) && - test_i18ngrep "unexpected object ID" err + test $A = $(git rev-parse PSEUDOREF) && + test_i18ngrep "cannot lock ref.*expected" err ' test_expect_success 'delete pseudoref with correct old value' ' git update-ref -d PSEUDOREF $A && - test_path_is_missing .git/PSEUDOREF + test_must_fail git rev-parse PSEUDOREF ' test_expect_success 'create pseudoref with old OID zero' ' git update-ref PSEUDOREF $A $Z && - test $A = $(cat .git/PSEUDOREF) + test $A = $(git rev-parse PSEUDOREF) ' test_expect_success 'do not overwrite pseudoref with old OID zero' ' test_when_finished git update-ref -d PSEUDOREF && test_must_fail git update-ref PSEUDOREF $B $Z 2>err && - test $A = $(cat .git/PSEUDOREF) && + test $A = $(git rev-parse PSEUDOREF) && test_i18ngrep "already exists" err ' diff --git a/t/t1405-main-ref-store.sh b/t/t1405-main-ref-store.sh index 331899ddc4..74af927fba 100755 --- a/t/t1405-main-ref-store.sh +++ b/t/t1405-main-ref-store.sh @@ -31,7 +31,10 @@ test_expect_success 'create_symref(FOO, refs/heads/master)' ' test_expect_success 'delete_refs(FOO, refs/tags/new-tag)' ' git rev-parse FOO -- && git rev-parse refs/tags/new-tag -- && - $RUN delete-refs 0 nothing FOO refs/tags/new-tag && + m=$(git rev-parse master) && + REF_NO_DEREF=1 && + $RUN delete-refs $REF_NO_DEREF nothing FOO refs/tags/new-tag && + test_must_fail git rev-parse --symbolic-full-name FOO && test_must_fail git rev-parse FOO -- && test_must_fail git rev-parse refs/tags/new-tag -- ' diff --git a/t/t1416-ref-transaction-hooks.sh b/t/t1416-ref-transaction-hooks.sh index da58d867a5..f6e741c6c0 100755 --- a/t/t1416-ref-transaction-hooks.sh +++ b/t/t1416-ref-transaction-hooks.sh @@ -7,6 +7,7 @@ test_description='reference transaction hooks' test_expect_success setup ' mkdir -p .git/hooks && test_commit PRE && + PRE_OID=$(git rev-parse PRE) && test_commit POST && POST_OID=$(git rev-parse POST) ' @@ -106,4 +107,30 @@ test_expect_success 'hook gets all queued updates in aborted state' ' test_cmp expect actual ' +test_expect_success 'interleaving hook calls succeed' ' + test_when_finished "rm -r target-repo.git" && + + git init --bare target-repo.git && + + write_script target-repo.git/hooks/reference-transaction <<-\EOF && + echo $0 "$@" >>actual + EOF + + write_script target-repo.git/hooks/update <<-\EOF && + echo $0 "$@" >>actual + EOF + + cat >expect <<-EOF && + hooks/update refs/tags/PRE $ZERO_OID $PRE_OID + hooks/reference-transaction prepared + hooks/reference-transaction committed + hooks/update refs/tags/POST $ZERO_OID $POST_OID + hooks/reference-transaction prepared + hooks/reference-transaction committed + EOF + + git push ./target-repo.git PRE POST && + test_cmp expect target-repo.git/actual +' + test_done diff --git a/t/t2025-checkout-no-overlay.sh b/t/t2025-checkout-no-overlay.sh index 76330cb5ab..fa9e098706 100755 --- a/t/t2025-checkout-no-overlay.sh +++ b/t/t2025-checkout-no-overlay.sh @@ -44,4 +44,16 @@ test_expect_success '--no-overlay --theirs with D/F conflict deletes file' ' test_path_is_missing file1 ' +test_expect_success 'wildcard pathspec matches file in subdirectory' ' + git reset --hard && + mkdir subdir && + test_commit file3-1 subdir/file3 && + test_commit file3-2 subdir/file3 && + + git checkout --no-overlay file3-1 "*file3" && + echo file3-1 >expect && + test_path_is_file subdir/file3 && + test_cmp expect subdir/file3 +' + test_done diff --git a/t/t2072-restore-pathspec-file.sh b/t/t2072-restore-pathspec-file.sh index 0d47946e8a..b48345bf95 100755 --- a/t/t2072-restore-pathspec-file.sh +++ b/t/t2072-restore-pathspec-file.sh @@ -9,18 +9,21 @@ test_tick test_expect_success setup ' test_commit file0 && + mkdir dir1 && + echo 1 >dir1/file && echo 1 >fileA.t && echo 1 >fileB.t && echo 1 >fileC.t && echo 1 >fileD.t && - git add fileA.t fileB.t fileC.t fileD.t && + git add dir1 fileA.t fileB.t fileC.t fileD.t && git commit -m "files 1" && + echo 2 >dir1/file && echo 2 >fileA.t && echo 2 >fileB.t && echo 2 >fileC.t && echo 2 >fileD.t && - git add fileA.t fileB.t fileC.t fileD.t && + git add dir1 fileA.t fileB.t fileC.t fileD.t && git commit -m "files 2" && git tag checkpoint @@ -31,7 +34,7 @@ restore_checkpoint () { } verify_expect () { - git status --porcelain --untracked-files=no -- fileA.t fileB.t fileC.t fileD.t >actual && + git status --porcelain --untracked-files=no -- dir1 fileA.t fileB.t fileC.t fileD.t >actual && test_cmp expect actual } @@ -161,4 +164,14 @@ test_expect_success 'error conditions' ' test_i18ngrep -e "you must specify path(s) to restore" err ' +test_expect_success 'wildcard pathspec matches file in subdirectory' ' + restore_checkpoint && + + echo "*file" | git restore --pathspec-from-file=- --source=HEAD^1 && + cat >expect <<-\EOF && + M dir1/file + EOF + verify_expect +' + test_done diff --git a/t/t3000-ls-files-others.sh b/t/t3000-ls-files-others.sh index ffdfb16f58..740ce56eab 100755 --- a/t/t3000-ls-files-others.sh +++ b/t/t3000-ls-files-others.sh @@ -152,7 +152,7 @@ test_expect_success 'ls-files -o --directory with mix dir/file pathspecs' ' ) ' -test_expect_success 'ls-files --o --directory with glob filetype match' ' +test_expect_success 'ls-files -o --directory with glob filetype match' ' ( cd nested && @@ -168,7 +168,7 @@ test_expect_success 'ls-files --o --directory with glob filetype match' ' ) ' -test_expect_success 'ls-files --o --directory with mix of tracked states' ' +test_expect_success 'ls-files -o --directory with mix of tracked states' ' ( cd nested && @@ -184,7 +184,7 @@ test_expect_success 'ls-files --o --directory with mix of tracked states' ' ) ' -test_expect_success 'ls-files --o --directory with glob filetype match only' ' +test_expect_success 'ls-files -o --directory with glob filetype match only' ' ( cd nested && @@ -198,7 +198,7 @@ test_expect_success 'ls-files --o --directory with glob filetype match only' ' ) ' -test_expect_success 'ls-files --o --directory to get immediate paths under one dir only' ' +test_expect_success 'ls-files -o --directory to get immediate paths under one dir only' ' ( cd nested && @@ -212,4 +212,20 @@ test_expect_success 'ls-files --o --directory to get immediate paths under one d ) ' +test_expect_success 'ls-files -o avoids listing untracked non-matching gitdir' ' + test_when_finished "rm -rf nested/untracked/deep/empty" && + ( + cd nested && + + git init untracked/deep/empty && + git ls-files --others "untracked/*.c" >actual && + + cat <<-EOF >expect && + untracked/deep/foo.c + EOF + + test_cmp expect actual + ) +' + test_done diff --git a/t/t3404-rebase-interactive.sh b/t/t3404-rebase-interactive.sh index 9744e88760..07a1617351 100755 --- a/t/t3404-rebase-interactive.sh +++ b/t/t3404-rebase-interactive.sh @@ -256,7 +256,7 @@ test_expect_success 'stop on conflicting pick' ' D ======= G - >>>>>>> $commit... G + >>>>>>> $commit (G) EOF git tag new-branch1 && test_must_fail git rebase -i master && @@ -1791,6 +1791,12 @@ test_expect_success 'correct error message for commit --amend after empty pick' test_i18ngrep "middle of a rebase -- cannot amend." err ' +test_expect_success 'todo has correct onto hash' ' + GIT_SEQUENCE_EDITOR=cat git rebase -i no-conflict-branch~4 no-conflict-branch >actual && + onto=$(git rev-parse --short HEAD~4) && + test_i18ngrep "^# Rebase ..* onto $onto" actual +' + # This must be the last test in this file test_expect_success '$EDITOR and friends are unchanged' ' test_editor_unchanged diff --git a/t/t3422-rebase-incompatible-options.sh b/t/t3422-rebase-incompatible-options.sh index 50e7960702..c8234062c6 100755 --- a/t/t3422-rebase-incompatible-options.sh +++ b/t/t3422-rebase-incompatible-options.sh @@ -61,8 +61,6 @@ test_rebase_am_only () { } test_rebase_am_only --whitespace=fix -test_rebase_am_only --ignore-whitespace -test_rebase_am_only --committer-date-is-author-date test_rebase_am_only -C4 test_expect_success REBASE_P '--preserve-merges incompatible with --signoff' ' diff --git a/t/t3436-rebase-more-options.sh b/t/t3436-rebase-more-options.sh new file mode 100755 index 0000000000..996e82787e --- /dev/null +++ b/t/t3436-rebase-more-options.sh @@ -0,0 +1,180 @@ +#!/bin/sh +# +# Copyright (c) 2019 Rohit Ashiwal +# + +test_description='tests to ensure compatibility between am and interactive backends' + +. ./test-lib.sh + +. "$TEST_DIRECTORY"/lib-rebase.sh + +GIT_AUTHOR_DATE="1999-04-02T08:03:20+05:30" +export GIT_AUTHOR_DATE + +# This is a special case in which both am and interactive backends +# provide the same output. It was done intentionally because +# both the backends fall short of optimal behaviour. +test_expect_success 'setup' ' + git checkout -b topic && + test_write_lines "line 1" " line 2" "line 3" >file && + git add file && + git commit -m "add file" && + + test_write_lines "line 1" "new line 2" "line 3" >file && + git commit -am "update file" && + git tag side && + test_commit commit1 foo foo1 && + test_commit commit2 foo foo2 && + test_commit commit3 foo foo3 && + + git checkout --orphan master && + rm foo && + test_write_lines "line 1" " line 2" "line 3" >file && + git commit -am "add file" && + git tag main && + + mkdir test-bin && + write_script test-bin/git-merge-test <<-\EOF + exec git merge-recursive "$@" + EOF +' + +test_expect_success '--ignore-whitespace works with apply backend' ' + test_must_fail git rebase --apply main side && + git rebase --abort && + git rebase --apply --ignore-whitespace main side && + git diff --exit-code side +' + +test_expect_success '--ignore-whitespace works with merge backend' ' + test_must_fail git rebase --merge main side && + git rebase --abort && + git rebase --merge --ignore-whitespace main side && + git diff --exit-code side +' + +test_expect_success '--ignore-whitespace is remembered when continuing' ' + ( + set_fake_editor && + FAKE_LINES="break 1" git rebase -i --ignore-whitespace \ + main side && + git rebase --continue + ) && + git diff --exit-code side +' + +test_ctime_is_atime () { + git log $1 --format=%ai >authortime && + git log $1 --format=%ci >committertime && + test_cmp authortime committertime +} + +test_expect_success '--committer-date-is-author-date works with apply backend' ' + GIT_AUTHOR_DATE="@1234 +0300" git commit --amend --reset-author && + git rebase --apply --committer-date-is-author-date HEAD^ && + test_ctime_is_atime -1 +' + +test_expect_success '--committer-date-is-author-date works with merge backend' ' + GIT_AUTHOR_DATE="@1234 +0300" git commit --amend --reset-author && + git rebase -m --committer-date-is-author-date HEAD^ && + test_ctime_is_atime -1 +' + +test_expect_success '--committer-date-is-author-date works with rebase -r' ' + git checkout side && + GIT_AUTHOR_DATE="@1234 +0300" git merge --no-ff commit3 && + git rebase -r --root --committer-date-is-author-date && + test_ctime_is_atime +' + +test_expect_success '--committer-date-is-author-date works when forking merge' ' + git checkout side && + GIT_AUTHOR_DATE="@1234 +0300" git merge --no-ff commit3 && + PATH="./test-bin:$PATH" git rebase -r --root --strategy=test \ + --committer-date-is-author-date && + test_ctime_is_atime +' + +test_expect_success '--committer-date-is-author-date works when committing conflict resolution' ' + git checkout commit2 && + GIT_AUTHOR_DATE="@1980 +0000" git commit --amend --only --reset-author && + test_must_fail git rebase -m --committer-date-is-author-date \ + --onto HEAD^^ HEAD^ && + echo resolved > foo && + git add foo && + git rebase --continue && + test_ctime_is_atime -1 +' + +# Checking for +0000 in the author date is sufficient since the +# default timezone is UTC but the timezone used while committing is +# +0530. The inverted logic in the grep is necessary to check all the +# author dates in the file. +test_atime_is_ignored () { + git log $1 --format=%ai >authortime && + ! grep -v +0000 authortime +} + +test_expect_success '--reset-author-date works with apply backend' ' + git commit --amend --date="$GIT_AUTHOR_DATE" && + git rebase --apply --reset-author-date HEAD^ && + test_atime_is_ignored -1 +' + +test_expect_success '--reset-author-date works with merge backend' ' + git commit --amend --date="$GIT_AUTHOR_DATE" && + git rebase --reset-author-date -m HEAD^ && + test_atime_is_ignored -1 +' + +test_expect_success '--reset-author-date works after conflict resolution' ' + test_must_fail git rebase --reset-author-date -m \ + --onto commit2^^ commit2^ commit2 && + echo resolved >foo && + git add foo && + git rebase --continue && + test_atime_is_ignored -1 +' + +test_expect_success '--reset-author-date works with rebase -r' ' + git checkout side && + git merge --no-ff commit3 && + git rebase -r --root --reset-author-date && + test_atime_is_ignored +' + +test_expect_success '--reset-author-date with --committer-date-is-author-date works' ' + test_must_fail git rebase -m --committer-date-is-author-date \ + --reset-author-date --onto commit2^^ commit2^ commit3 && + git checkout --theirs foo && + git add foo && + git rebase --continue && + test_ctime_is_atime -2 && + test_atime_is_ignored -2 +' + +test_expect_success '--reset-author-date --committer-date-is-author-date works when forking merge' ' + GIT_SEQUENCE_EDITOR="echo \"merge -C $(git rev-parse HEAD) commit3\">" \ + PATH="./test-bin:$PATH" git rebase -i --strategy=test \ + --reset-author-date \ + --committer-date-is-author-date side side && + test_ctime_is_atime -1 && + test_atime_is_ignored -1 + ' + +test_expect_success '--ignore-date is an alias for --reset-author-date' ' + git commit --amend --date="$GIT_AUTHOR_DATE" && + git rebase --apply --ignore-date HEAD^ && + git commit --allow-empty -m empty --date="$GIT_AUTHOR_DATE" && + git rebase -m --ignore-date HEAD^ && + test_atime_is_ignored -2 +' + +# This must be the last test in this file +test_expect_success '$EDITOR and friends are unchanged' ' + test_editor_unchanged +' + +test_done diff --git a/t/t3500-cherry.sh b/t/t3500-cherry.sh index f038f34b7c..2b8d9cb38e 100755 --- a/t/t3500-cherry.sh +++ b/t/t3500-cherry.sh @@ -55,4 +55,27 @@ test_expect_success \ expr "$(echo $(git cherry master my-topic-branch) )" : "+ [^ ]* - .*" ' +test_expect_success 'cherry ignores whitespace' ' + git switch --orphan=upstream-with-space && + test_commit initial file && + >expect && + git switch --create=feature-without-space && + + # A spaceless file on the feature branch. Expect a match upstream. + printf space >file && + git add file && + git commit -m"file without space" && + git log --format="- %H" -1 >>expect && + + # A further change. Should not match upstream. + test_commit change file && + git log --format="+ %H" -1 >>expect && + + git switch upstream-with-space && + # Same as the spaceless file, just with spaces and on upstream. + test_commit "file with space" file "s p a c e" file-with-space && + git cherry upstream-with-space feature-without-space >actual && + test_cmp expect actual +' + test_done diff --git a/t/t3507-cherry-pick-conflict.sh b/t/t3507-cherry-pick-conflict.sh index f107622a9e..a21adcf0e4 100755 --- a/t/t3507-cherry-pick-conflict.sh +++ b/t/t3507-cherry-pick-conflict.sh @@ -283,12 +283,12 @@ test_expect_success 'failed cherry-pick describes conflict in work tree' ' a ======= c - >>>>>>> objid picked + >>>>>>> objid (picked) EOF test_must_fail git cherry-pick picked && - sed "s/[a-f0-9]*\.\.\./objid/" foo >actual && + sed "s/[a-f0-9]* (/objid (/" foo >actual && test_cmp expected actual ' @@ -298,16 +298,16 @@ test_expect_success 'diff3 -m style' ' cat <<-EOF >expected && <<<<<<< HEAD a - ||||||| parent of objid picked + ||||||| parent of objid (picked) b ======= c - >>>>>>> objid picked + >>>>>>> objid (picked) EOF test_must_fail git cherry-pick picked && - sed "s/[a-f0-9]*\.\.\./objid/" foo >actual && + sed "s/[a-f0-9]* (/objid (/" foo >actual && test_cmp expected actual ' @@ -319,7 +319,7 @@ test_expect_success 'revert also handles conflicts sanely' ' a ======= b - >>>>>>> parent of objid picked + >>>>>>> parent of objid (picked) EOF { git checkout picked -- foo && @@ -345,7 +345,7 @@ test_expect_success 'revert also handles conflicts sanely' ' test_must_fail git update-index --refresh -q && test_must_fail git diff-index --exit-code HEAD && test_cmp expected-stages actual-stages && - sed "s/[a-f0-9]*\.\.\./objid/" foo >actual && + sed "s/[a-f0-9]* (/objid (/" foo >actual && test_cmp expected actual ' @@ -429,16 +429,16 @@ test_expect_success 'revert conflict, diff3 -m style' ' cat <<-EOF >expected && <<<<<<< HEAD a - ||||||| objid picked + ||||||| objid (picked) c ======= b - >>>>>>> parent of objid picked + >>>>>>> parent of objid (picked) EOF test_must_fail git revert picked && - sed "s/[a-f0-9]*\.\.\./objid/" foo >actual && + sed "s/[a-f0-9]* (/objid (/" foo >actual && test_cmp expected actual ' diff --git a/t/t4013-diff-various.sh b/t/t4013-diff-various.sh index 3f60f7d96c..5c7b0122b4 100755 --- a/t/t4013-diff-various.sh +++ b/t/t4013-diff-various.sh @@ -130,27 +130,45 @@ test_expect_success setup ' EOF process_diffs () { - _x04="[0-9a-f][0-9a-f][0-9a-f][0-9a-f]" && - _x07="$_x05[0-9a-f][0-9a-f]" && - sed -e "s/$OID_REGEX/$ZERO_OID/g" \ - -e "s/From $_x40 /From $ZERO_OID /" \ - -e "s/from $_x40)/from $ZERO_OID)/" \ - -e "s/commit $_x40\$/commit $ZERO_OID/" \ - -e "s/commit $_x40 (/commit $ZERO_OID (/" \ - -e "s/$_x40 $_x40 $_x40/$ZERO_OID $ZERO_OID $ZERO_OID/" \ - -e "s/$_x40 $_x40 /$ZERO_OID $ZERO_OID /" \ - -e "s/^$_x40 $_x40$/$ZERO_OID $ZERO_OID/" \ - -e "s/^$_x40 /$ZERO_OID /" \ - -e "s/^$_x40$/$ZERO_OID/" \ - -e "s/$_x07\.\.$_x07/fffffff..fffffff/g" \ - -e "s/$_x07,$_x07\.\.$_x07/fffffff,fffffff..fffffff/g" \ - -e "s/$_x07 $_x07 $_x07/fffffff fffffff fffffff/g" \ - -e "s/$_x07 $_x07 /fffffff fffffff /g" \ - -e "s/Merge: $_x07 $_x07/Merge: fffffff fffffff/g" \ - -e "s/$_x07\.\.\./fffffff.../g" \ - -e "s/ $_x04\.\.\./ ffff.../g" \ - -e "s/ $_x04/ ffff/g" \ - "$1" + perl -e ' + my $oid_length = length($ARGV[0]); + my $x40 = "[0-9a-f]{40}"; + my $xab = "[0-9a-f]{4,16}"; + my $orx = "[0-9a-f]" x $oid_length; + + sub munge_oid { + my ($oid) = @_; + my $x; + + return "" unless length $oid; + + if ($oid =~ /^(100644|100755|120000)$/) { + return $oid; + } + + if ($oid =~ /^0*$/) { + $x = "0"; + } else { + $x = "f"; + } + + if (length($oid) == 40) { + return $x x $oid_length; + } else { + return $x x length($oid); + } + } + + while (<STDIN>) { + s/($orx)/munge_oid($1)/ge; + s/From ($x40)( |\))/"From " . munge_oid($1) . $2/ge; + s/commit ($x40)($| \(from )($x40?)/"commit " . munge_oid($1) . $2 . munge_oid($3)/ge; + s/\b($x40)( |\.\.|$)/munge_oid($1) . $2/ge; + s/^($x40)($| )/munge_oid($1) . $2/e; + s/($xab)(\.\.|,| |\.\.\.|$)/munge_oid($1) . $2/ge; + print; + } + ' "$ZERO_OID" <"$1" } V=$(git version | sed -e 's/^git version //' -e 's/\./\\./g') @@ -221,6 +239,9 @@ diff-tree --root -r --abbrev=4 initial :noellipses diff-tree --root -r --abbrev=4 initial diff-tree -p initial diff-tree --root -p initial +diff-tree --root -p --abbrev=10 initial +diff-tree --root -p --full-index initial +diff-tree --root -p --full-index --abbrev=10 initial diff-tree --patch-with-stat initial diff-tree --root --patch-with-stat initial diff-tree --patch-with-raw initial @@ -297,6 +318,9 @@ log --root --patch-with-stat --summary master log --root -c --patch-with-stat --summary master # improved by Timo's patch log --root --cc --patch-with-stat --summary master +log --no-diff-merges -p --first-parent master +log --diff-merges=off -p --first-parent master +log --first-parent --diff-merges=off -p master log -p --first-parent master log -m -p --first-parent master log -m -p master diff --git a/t/t4013/diff.diff-tree_--root_-p_--abbrev=10_initial b/t/t4013/diff.diff-tree_--root_-p_--abbrev=10_initial new file mode 100644 index 0000000000..7518a9044e --- /dev/null +++ b/t/t4013/diff.diff-tree_--root_-p_--abbrev=10_initial @@ -0,0 +1,29 @@ +$ git diff-tree --root -p --abbrev=10 initial +444ac553ac7612cc88969031b02b3767fb8a353a +diff --git a/dir/sub b/dir/sub +new file mode 100644 +index 0000000000..35d242ba79 +--- /dev/null ++++ b/dir/sub +@@ -0,0 +1,2 @@ ++A ++B +diff --git a/file0 b/file0 +new file mode 100644 +index 0000000000..01e79c32a8 +--- /dev/null ++++ b/file0 +@@ -0,0 +1,3 @@ ++1 ++2 ++3 +diff --git a/file2 b/file2 +new file mode 100644 +index 0000000000..01e79c32a8 +--- /dev/null ++++ b/file2 +@@ -0,0 +1,3 @@ ++1 ++2 ++3 +$ diff --git a/t/t4013/diff.diff-tree_--root_-p_--full-index_--abbrev=10_initial b/t/t4013/diff.diff-tree_--root_-p_--full-index_--abbrev=10_initial new file mode 100644 index 0000000000..69f913fbe5 --- /dev/null +++ b/t/t4013/diff.diff-tree_--root_-p_--full-index_--abbrev=10_initial @@ -0,0 +1,29 @@ +$ git diff-tree --root -p --full-index --abbrev=10 initial +444ac553ac7612cc88969031b02b3767fb8a353a +diff --git a/dir/sub b/dir/sub +new file mode 100644 +index 0000000000000000000000000000000000000000..35d242ba79ae89ac695e26b3d4c27a8e6f028f9e +--- /dev/null ++++ b/dir/sub +@@ -0,0 +1,2 @@ ++A ++B +diff --git a/file0 b/file0 +new file mode 100644 +index 0000000000000000000000000000000000000000..01e79c32a8c99c557f0757da7cb6d65b3414466d +--- /dev/null ++++ b/file0 +@@ -0,0 +1,3 @@ ++1 ++2 ++3 +diff --git a/file2 b/file2 +new file mode 100644 +index 0000000000000000000000000000000000000000..01e79c32a8c99c557f0757da7cb6d65b3414466d +--- /dev/null ++++ b/file2 +@@ -0,0 +1,3 @@ ++1 ++2 ++3 +$ diff --git a/t/t4013/diff.diff-tree_--root_-p_--full-index_initial b/t/t4013/diff.diff-tree_--root_-p_--full-index_initial new file mode 100644 index 0000000000..1b0b6717fa --- /dev/null +++ b/t/t4013/diff.diff-tree_--root_-p_--full-index_initial @@ -0,0 +1,29 @@ +$ git diff-tree --root -p --full-index initial +444ac553ac7612cc88969031b02b3767fb8a353a +diff --git a/dir/sub b/dir/sub +new file mode 100644 +index 0000000000000000000000000000000000000000..35d242ba79ae89ac695e26b3d4c27a8e6f028f9e +--- /dev/null ++++ b/dir/sub +@@ -0,0 +1,2 @@ ++A ++B +diff --git a/file0 b/file0 +new file mode 100644 +index 0000000000000000000000000000000000000000..01e79c32a8c99c557f0757da7cb6d65b3414466d +--- /dev/null ++++ b/file0 +@@ -0,0 +1,3 @@ ++1 ++2 ++3 +diff --git a/file2 b/file2 +new file mode 100644 +index 0000000000000000000000000000000000000000..01e79c32a8c99c557f0757da7cb6d65b3414466d +--- /dev/null ++++ b/file2 +@@ -0,0 +1,3 @@ ++1 ++2 ++3 +$ diff --git a/t/t4013/diff.log_--diff-merges=off_-p_--first-parent_master b/t/t4013/diff.log_--diff-merges=off_-p_--first-parent_master new file mode 100644 index 0000000000..194e893c94 --- /dev/null +++ b/t/t4013/diff.log_--diff-merges=off_-p_--first-parent_master @@ -0,0 +1,78 @@ +$ git log --diff-merges=off -p --first-parent master +commit 59d314ad6f356dd08601a4cd5e530381da3e3c64 +Merge: 9a6d494 c7a2ab9 +Author: A U Thor <author@example.com> +Date: Mon Jun 26 00:04:00 2006 +0000 + + Merge branch 'side' + +commit 9a6d4949b6b76956d9d5e26f2791ec2ceff5fdc0 +Author: A U Thor <author@example.com> +Date: Mon Jun 26 00:02:00 2006 +0000 + + Third + +diff --git a/dir/sub b/dir/sub +index 8422d40..cead32e 100644 +--- a/dir/sub ++++ b/dir/sub +@@ -2,3 +2,5 @@ A + B + C + D ++E ++F +diff --git a/file1 b/file1 +new file mode 100644 +index 0000000..b1e6722 +--- /dev/null ++++ b/file1 +@@ -0,0 +1,3 @@ ++A ++B ++C + +commit 1bde4ae5f36c8d9abe3a0fce0c6aab3c4a12fe44 +Author: A U Thor <author@example.com> +Date: Mon Jun 26 00:01:00 2006 +0000 + + Second + + This is the second commit. + +diff --git a/dir/sub b/dir/sub +index 35d242b..8422d40 100644 +--- a/dir/sub ++++ b/dir/sub +@@ -1,2 +1,4 @@ + A + B ++C ++D +diff --git a/file0 b/file0 +index 01e79c3..b414108 100644 +--- a/file0 ++++ b/file0 +@@ -1,3 +1,6 @@ + 1 + 2 + 3 ++4 ++5 ++6 +diff --git a/file2 b/file2 +deleted file mode 100644 +index 01e79c3..0000000 +--- a/file2 ++++ /dev/null +@@ -1,3 +0,0 @@ +-1 +-2 +-3 + +commit 444ac553ac7612cc88969031b02b3767fb8a353a +Author: A U Thor <author@example.com> +Date: Mon Jun 26 00:00:00 2006 +0000 + + Initial +$ diff --git a/t/t4013/diff.log_--first-parent_--diff-merges=off_-p_master b/t/t4013/diff.log_--first-parent_--diff-merges=off_-p_master new file mode 100644 index 0000000000..5d7461a167 --- /dev/null +++ b/t/t4013/diff.log_--first-parent_--diff-merges=off_-p_master @@ -0,0 +1,78 @@ +$ git log --first-parent --diff-merges=off -p master +commit 80e25ffa65bcdbe82ef654b4d06dbbde7945c37f +Merge: 9a6d494 c7a2ab9 +Author: A U Thor <author@example.com> +Date: Mon Jun 26 00:04:00 2006 +0000 + + Merge branch 'side' + +commit 9a6d4949b6b76956d9d5e26f2791ec2ceff5fdc0 +Author: A U Thor <author@example.com> +Date: Mon Jun 26 00:02:00 2006 +0000 + + Third + +diff --git a/dir/sub b/dir/sub +index 8422d40..cead32e 100644 +--- a/dir/sub ++++ b/dir/sub +@@ -2,3 +2,5 @@ A + B + C + D ++E ++F +diff --git a/file1 b/file1 +new file mode 100644 +index 0000000..b1e6722 +--- /dev/null ++++ b/file1 +@@ -0,0 +1,3 @@ ++A ++B ++C + +commit 1bde4ae5f36c8d9abe3a0fce0c6aab3c4a12fe44 +Author: A U Thor <author@example.com> +Date: Mon Jun 26 00:01:00 2006 +0000 + + Second + + This is the second commit. + +diff --git a/dir/sub b/dir/sub +index 35d242b..8422d40 100644 +--- a/dir/sub ++++ b/dir/sub +@@ -1,2 +1,4 @@ + A + B ++C ++D +diff --git a/file0 b/file0 +index 01e79c3..b414108 100644 +--- a/file0 ++++ b/file0 +@@ -1,3 +1,6 @@ + 1 + 2 + 3 ++4 ++5 ++6 +diff --git a/file2 b/file2 +deleted file mode 100644 +index 01e79c3..0000000 +--- a/file2 ++++ /dev/null +@@ -1,3 +0,0 @@ +-1 +-2 +-3 + +commit 444ac553ac7612cc88969031b02b3767fb8a353a +Author: A U Thor <author@example.com> +Date: Mon Jun 26 00:00:00 2006 +0000 + + Initial +$ diff --git a/t/t4013/diff.log_--no-diff-merges_-p_--first-parent_master b/t/t4013/diff.log_--no-diff-merges_-p_--first-parent_master new file mode 100644 index 0000000000..597002232e --- /dev/null +++ b/t/t4013/diff.log_--no-diff-merges_-p_--first-parent_master @@ -0,0 +1,78 @@ +$ git log --no-diff-merges -p --first-parent master +commit 59d314ad6f356dd08601a4cd5e530381da3e3c64 +Merge: 9a6d494 c7a2ab9 +Author: A U Thor <author@example.com> +Date: Mon Jun 26 00:04:00 2006 +0000 + + Merge branch 'side' + +commit 9a6d4949b6b76956d9d5e26f2791ec2ceff5fdc0 +Author: A U Thor <author@example.com> +Date: Mon Jun 26 00:02:00 2006 +0000 + + Third + +diff --git a/dir/sub b/dir/sub +index 8422d40..cead32e 100644 +--- a/dir/sub ++++ b/dir/sub +@@ -2,3 +2,5 @@ A + B + C + D ++E ++F +diff --git a/file1 b/file1 +new file mode 100644 +index 0000000..b1e6722 +--- /dev/null ++++ b/file1 +@@ -0,0 +1,3 @@ ++A ++B ++C + +commit 1bde4ae5f36c8d9abe3a0fce0c6aab3c4a12fe44 +Author: A U Thor <author@example.com> +Date: Mon Jun 26 00:01:00 2006 +0000 + + Second + + This is the second commit. + +diff --git a/dir/sub b/dir/sub +index 35d242b..8422d40 100644 +--- a/dir/sub ++++ b/dir/sub +@@ -1,2 +1,4 @@ + A + B ++C ++D +diff --git a/file0 b/file0 +index 01e79c3..b414108 100644 +--- a/file0 ++++ b/file0 +@@ -1,3 +1,6 @@ + 1 + 2 + 3 ++4 ++5 ++6 +diff --git a/file2 b/file2 +deleted file mode 100644 +index 01e79c3..0000000 +--- a/file2 ++++ /dev/null +@@ -1,3 +0,0 @@ +-1 +-2 +-3 + +commit 444ac553ac7612cc88969031b02b3767fb8a353a +Author: A U Thor <author@example.com> +Date: Mon Jun 26 00:00:00 2006 +0000 + + Initial +$ diff --git a/t/t4013/diff.log_-p_--first-parent_master b/t/t4013/diff.log_-p_--first-parent_master index 3fc896d424..28840ebea1 100644 --- a/t/t4013/diff.log_-p_--first-parent_master +++ b/t/t4013/diff.log_-p_--first-parent_master @@ -6,6 +6,28 @@ Date: Mon Jun 26 00:04:00 2006 +0000 Merge branch 'side' +diff --git a/dir/sub b/dir/sub +index cead32e..992913c 100644 +--- a/dir/sub ++++ b/dir/sub +@@ -4,3 +4,5 @@ C + D + E + F ++1 ++2 +diff --git a/file0 b/file0 +index b414108..10a8a9f 100644 +--- a/file0 ++++ b/file0 +@@ -4,3 +4,6 @@ + 4 + 5 + 6 ++A ++B ++C + commit 9a6d4949b6b76956d9d5e26f2791ec2ceff5fdc0 Author: A U Thor <author@example.com> Date: Mon Jun 26 00:02:00 2006 +0000 diff --git a/t/t4018/fortran-block-data b/t/t4018/fortran-block-data new file mode 100644 index 0000000000..63d4e21d0a --- /dev/null +++ b/t/t4018/fortran-block-data @@ -0,0 +1,5 @@ + BLOCK DATA RIGHT + + COMMON /B/ C, ChangeMe + DATA C, ChangeMe / 2.0, 6.0 / + END diff --git a/t/t4018/fortran-comment b/t/t4018/fortran-comment new file mode 100644 index 0000000000..7b10d17658 --- /dev/null +++ b/t/t4018/fortran-comment @@ -0,0 +1,13 @@ + module a + + contains + + ! subroutine wrong + subroutine RIGHT + ! subroutine wrong + + real ChangeMe + + end subroutine RIGHT + + end module a diff --git a/t/t4018/fortran-comment-keyword b/t/t4018/fortran-comment-keyword new file mode 100644 index 0000000000..e9206a5379 --- /dev/null +++ b/t/t4018/fortran-comment-keyword @@ -0,0 +1,14 @@ + module a + + contains + + subroutine RIGHT (funcA, funcB) + + real funcA ! grid function a + real funcB ! grid function b + + real ChangeMe + + end subroutine RIGHT + + end module a diff --git a/t/t4018/fortran-comment-legacy b/t/t4018/fortran-comment-legacy new file mode 100644 index 0000000000..53cd062c1e --- /dev/null +++ b/t/t4018/fortran-comment-legacy @@ -0,0 +1,13 @@ + module a + + contains + +C subroutine wrong + subroutine RIGHT +C subroutine wrong + + real ChangeMe + + end subroutine RIGHT + + end module a diff --git a/t/t4018/fortran-comment-legacy-star b/t/t4018/fortran-comment-legacy-star new file mode 100644 index 0000000000..2cbcdc3d8a --- /dev/null +++ b/t/t4018/fortran-comment-legacy-star @@ -0,0 +1,13 @@ + module a + + contains + +* subroutine wrong + subroutine RIGHT +* subroutine wrong + + real ChangeMe + + end subroutine RIGHT + + end module a diff --git a/t/t4018/fortran-external-function b/t/t4018/fortran-external-function new file mode 100644 index 0000000000..5a2d85d3aa --- /dev/null +++ b/t/t4018/fortran-external-function @@ -0,0 +1,9 @@ +function RIGHT(a, b) result(c) + +integer, intent(in) :: ChangeMe +integer, intent(in) :: b +integer, intent(out) :: c + +c = a+b + +end function RIGHT diff --git a/t/t4018/fortran-external-subroutine b/t/t4018/fortran-external-subroutine new file mode 100644 index 0000000000..4ce85fea13 --- /dev/null +++ b/t/t4018/fortran-external-subroutine @@ -0,0 +1,5 @@ +subroutine RIGHT + +real ChangeMe + +end subroutine RIGHT diff --git a/t/t4018/fortran-module b/t/t4018/fortran-module new file mode 100644 index 0000000000..c4b737dac3 --- /dev/null +++ b/t/t4018/fortran-module @@ -0,0 +1,5 @@ +module RIGHT + +use ChangeMe + +end module RIGHT diff --git a/t/t4018/fortran-module-procedure b/t/t4018/fortran-module-procedure new file mode 100644 index 0000000000..1ce6d854c2 --- /dev/null +++ b/t/t4018/fortran-module-procedure @@ -0,0 +1,13 @@ + module RIGHT + + implicit none + private + + interface letters ! generic interface + module procedure aaaa, & + bbbb, & + ChangeMe, & + dddd + end interface + +end module RIGHT diff --git a/t/t4018/fortran-program b/t/t4018/fortran-program new file mode 100644 index 0000000000..4616895e4b --- /dev/null +++ b/t/t4018/fortran-program @@ -0,0 +1,5 @@ +program RIGHT + +call ChangeMe + +end program RIGHT diff --git a/t/t4140-apply-ita.sh b/t/t4140-apply-ita.sh new file mode 100755 index 0000000000..c614eaf04c --- /dev/null +++ b/t/t4140-apply-ita.sh @@ -0,0 +1,56 @@ +#!/bin/sh + +test_description='git apply of i-t-a file' + +. ./test-lib.sh + +test_expect_success setup ' + test_write_lines 1 2 3 4 5 >blueprint && + + cat blueprint >test-file && + git add -N test-file && + git diff >creation-patch && + grep "new file mode 100644" creation-patch && + + rm -f test-file && + git diff >deletion-patch && + grep "deleted file mode 100644" deletion-patch +' + +test_expect_success 'apply creation patch to ita path (--cached)' ' + git rm -f test-file && + cat blueprint >test-file && + git add -N test-file && + + git apply --cached creation-patch && + git cat-file blob :test-file >actual && + test_cmp blueprint actual +' + +test_expect_success 'apply creation patch to ita path (--index)' ' + git rm -f test-file && + cat blueprint >test-file && + git add -N test-file && + rm -f test-file && + + test_must_fail git apply --index creation-patch +' + +test_expect_success 'apply deletion patch to ita path (--cached)' ' + git rm -f test-file && + cat blueprint >test-file && + git add -N test-file && + + git apply --cached deletion-patch && + test_must_fail git ls-files --stage --error-unmatch test-file +' + +test_expect_success 'apply deletion patch to ita path (--index)' ' + cat blueprint >test-file && + git add -N test-file && + + test_must_fail git apply --index deletion-patch && + git ls-files --stage --error-unmatch test-file +' + +test_done diff --git a/t/t4202-log.sh b/t/t4202-log.sh index a0930599aa..56d34ed465 100755 --- a/t/t4202-log.sh +++ b/t/t4202-log.sh @@ -1850,6 +1850,16 @@ test_expect_success 'log does not default to HEAD when rev input is given' ' test_must_be_empty actual ' +test_expect_success 'do not default to HEAD with ignored object on cmdline' ' + git log --ignore-missing $ZERO_OID >actual && + test_must_be_empty actual +' + +test_expect_success 'do not default to HEAD with ignored object on stdin' ' + echo $ZERO_OID | git log --ignore-missing --stdin >actual && + test_must_be_empty actual +' + test_expect_success 'set up --source tests' ' git checkout --orphan source-a && test_commit one && diff --git a/t/t4216-log-bloom.sh b/t/t4216-log-bloom.sh index c21cc160f3..4bb9e9dbe2 100755 --- a/t/t4216-log-bloom.sh +++ b/t/t4216-log-bloom.sh @@ -30,12 +30,17 @@ test_expect_success 'setup test - repo, commits, commit graph, log outputs' ' rm file_to_be_deleted && git add . && git commit -m "file removed" && - git commit-graph write --reachable --changed-paths + git commit-graph write --reachable --changed-paths && + + test_oid_cache <<-EOF + oid_version sha1:1 + oid_version sha256:2 + EOF ' graph_read_expect () { NUM_CHUNKS=5 cat >expect <<- EOF - header: 43475048 1 1 $NUM_CHUNKS 0 + header: 43475048 1 $(test_oid oid_version) $NUM_CHUNKS 0 num_commits: $1 chunks: oid_fanout oid_lookup commit_metadata bloom_indexes bloom_data EOF diff --git a/t/t5318-commit-graph.sh b/t/t5318-commit-graph.sh index 044cf8a3de..2ed0c1544d 100755 --- a/t/t5318-commit-graph.sh +++ b/t/t5318-commit-graph.sh @@ -10,7 +10,12 @@ test_expect_success 'setup full repo' ' cd "$TRASH_DIRECTORY/full" && git init && git config core.commitGraph true && - objdir=".git/objects" + objdir=".git/objects" && + + test_oid_cache <<-EOF + oid_version sha1:1 + oid_version sha256:2 + EOF ' test_expect_success POSIXPERM 'tweak umask for modebit tests' ' @@ -77,7 +82,7 @@ graph_read_expect() { NUM_CHUNKS=$((3 + $(echo "$2" | wc -w))) fi cat >expect <<- EOF - header: 43475048 1 1 $NUM_CHUNKS 0 + header: 43475048 1 $(test_oid oid_version) $NUM_CHUNKS 0 num_commits: $1 chunks: oid_fanout oid_lookup commit_metadata$OPTIONAL EOF @@ -412,6 +417,35 @@ test_expect_success 'replace-objects invalidates commit-graph' ' ) ' +test_expect_success 'warn on improper hash version' ' + git init --object-format=sha1 sha1 && + ( + cd sha1 && + test_commit 1 && + git commit-graph write --reachable && + mv .git/objects/info/commit-graph ../cg-sha1 + ) && + git init --object-format=sha256 sha256 && + ( + cd sha256 && + test_commit 1 && + git commit-graph write --reachable && + mv .git/objects/info/commit-graph ../cg-sha256 + ) && + ( + cd sha1 && + mv ../cg-sha256 .git/objects/info/commit-graph && + git log -1 2>err && + test_i18ngrep "commit-graph hash version 2 does not match version 1" err + ) && + ( + cd sha256 && + mv ../cg-sha1 .git/objects/info/commit-graph && + git log -1 2>err && + test_i18ngrep "commit-graph hash version 1 does not match version 2" err + ) +' + # the verify tests below expect the commit-graph to contain # exactly the commits reachable from the commits/8 branch. # If the file changes the set of commits in the list, then the diff --git a/t/t5319-multi-pack-index.sh b/t/t5319-multi-pack-index.sh index 7dfff0f8f4..43b1b5b2af 100755 --- a/t/t5319-multi-pack-index.sh +++ b/t/t5319-multi-pack-index.sh @@ -5,6 +5,8 @@ test_description='multi-pack-indexes' objdir=.git/objects +HASH_LEN=$(test_oid rawsz) + midx_read_expect () { NUM_PACKS=$1 NUM_OBJECTS=$2 @@ -13,7 +15,7 @@ midx_read_expect () { EXTRA_CHUNKS="$5" { cat <<-EOF && - header: 4d494458 1 $NUM_CHUNKS $NUM_PACKS + header: 4d494458 1 $HASH_LEN $NUM_CHUNKS $NUM_PACKS chunks: pack-names oid-fanout oid-lookup object-offsets$EXTRA_CHUNKS num_objects: $NUM_OBJECTS packs: @@ -46,7 +48,7 @@ test_expect_success "don't write midx with no packs" ' test_path_is_missing pack/multi-pack-index ' -test_expect_success "Warn if a midx contains no oid" ' +test_expect_success SHA1 'warn if a midx contains no oid' ' cp "$TEST_DIRECTORY"/t5319/no-objects.midx $objdir/pack/multi-pack-index && test_must_fail git multi-pack-index verify && rm $objdir/pack/multi-pack-index @@ -198,6 +200,40 @@ test_expect_success 'write midx with twelve packs' ' compare_results_with_midx "twelve packs" +test_expect_success 'warn on improper hash version' ' + git init --object-format=sha1 sha1 && + ( + cd sha1 && + git config core.multiPackIndex true && + test_commit 1 && + git repack -a && + git multi-pack-index write && + mv .git/objects/pack/multi-pack-index ../mpi-sha1 + ) && + git init --object-format=sha256 sha256 && + ( + cd sha256 && + git config core.multiPackIndex true && + test_commit 1 && + git repack -a && + git multi-pack-index write && + mv .git/objects/pack/multi-pack-index ../mpi-sha256 + ) && + ( + cd sha1 && + mv ../mpi-sha256 .git/objects/pack/multi-pack-index && + git log -1 2>err && + test_i18ngrep "multi-pack-index hash version 2 does not match version 1" err + ) && + ( + cd sha256 && + mv ../mpi-sha1 .git/objects/pack/multi-pack-index && + git log -1 2>err && + test_i18ngrep "multi-pack-index hash version 1 does not match version 2" err + ) +' + + test_expect_success 'verify multi-pack-index success' ' git multi-pack-index verify --object-dir=$objdir ' @@ -243,7 +279,6 @@ test_expect_success 'verify bad signature' ' "multi-pack-index signature" ' -HASH_LEN=$(test_oid rawsz) NUM_OBJECTS=74 MIDX_BYTE_VERSION=4 MIDX_BYTE_OID_VERSION=5 @@ -272,7 +307,7 @@ test_expect_success 'verify bad version' ' ' test_expect_success 'verify bad OID version' ' - corrupt_midx_and_verify $MIDX_BYTE_OID_VERSION "\02" $objdir \ + corrupt_midx_and_verify $MIDX_BYTE_OID_VERSION "\03" $objdir \ "hash version" ' @@ -642,6 +677,7 @@ test_expect_success 'expire respects .keep files' ' ' test_expect_success 'repack --batch-size=0 repacks everything' ' + cp -r dup dup2 && ( cd dup && rm .git/objects/pack/*.keep && @@ -661,4 +697,21 @@ test_expect_success 'repack --batch-size=0 repacks everything' ' ) ' +test_expect_success 'repack --batch-size=<large> repacks everything' ' + ( + cd dup2 && + rm .git/objects/pack/*.keep && + ls .git/objects/pack/*idx >idx-list && + test_line_count = 2 idx-list && + git multi-pack-index repack --batch-size=2000000 && + ls .git/objects/pack/*idx >idx-list && + test_line_count = 3 idx-list && + test-tool read-midx .git/objects | grep idx >midx-list && + test_line_count = 3 midx-list && + git multi-pack-index expire && + ls -al .git/objects/pack/*idx >idx-list && + test_line_count = 1 idx-list + ) +' + test_done diff --git a/t/t5324-split-commit-graph.sh b/t/t5324-split-commit-graph.sh index ea28d522b8..18216463c7 100755 --- a/t/t5324-split-commit-graph.sh +++ b/t/t5324-split-commit-graph.sh @@ -18,6 +18,9 @@ test_expect_success 'setup repo' ' base sha1:1376 base sha256:1496 + + oid_version sha1:1 + oid_version sha256:2 EOM ' @@ -28,7 +31,7 @@ graph_read_expect() { NUM_BASE=$2 fi cat >expect <<- EOF - header: 43475048 1 1 3 $NUM_BASE + header: 43475048 1 $(test_oid oid_version) 3 $NUM_BASE num_commits: $1 chunks: oid_fanout oid_lookup commit_metadata EOF diff --git a/t/t5510-fetch.sh b/t/t5510-fetch.sh index 25695dfe22..2a1abe91f0 100755 --- a/t/t5510-fetch.sh +++ b/t/t5510-fetch.sh @@ -543,13 +543,24 @@ test_expect_success 'fetch into the current branch with --update-head-ok' ' ' -test_expect_success 'fetch --dry-run' ' - +test_expect_success 'fetch --dry-run does not touch FETCH_HEAD' ' rm -f .git/FETCH_HEAD && git fetch --dry-run . && ! test -f .git/FETCH_HEAD ' +test_expect_success '--no-write-fetch-head does not touch FETCH_HEAD' ' + rm -f .git/FETCH_HEAD && + git fetch --no-write-fetch-head . && + ! test -f .git/FETCH_HEAD +' + +test_expect_success '--write-fetch-head gets defeated by --dry-run' ' + rm -f .git/FETCH_HEAD && + git fetch --dry-run --write-fetch-head . && + ! test -f .git/FETCH_HEAD +' + test_expect_success "should be able to fetch with duplicate refspecs" ' mkdir dups && ( diff --git a/t/t5521-pull-options.sh b/t/t5521-pull-options.sh index 159afa7ac8..db1a381cd9 100755 --- a/t/t5521-pull-options.sh +++ b/t/t5521-pull-options.sh @@ -85,6 +85,13 @@ test_expect_success 'git pull --cleanup errors early on invalid argument' ' test -s err) ' +test_expect_success 'git pull --no-write-fetch-head fails' ' + mkdir clonedwfh && + (cd clonedwfh && git init && + test_expect_code 129 git pull --no-write-fetch-head "../parent" >out 2>err && + test_must_be_empty out && + test_i18ngrep "no-write-fetch-head" err) +' test_expect_success 'git pull --force' ' mkdir clonedoldstyle && diff --git a/t/t5616-partial-clone.sh b/t/t5616-partial-clone.sh index 8827c2ed18..5a01466db4 100755 --- a/t/t5616-partial-clone.sh +++ b/t/t5616-partial-clone.sh @@ -163,6 +163,22 @@ test_expect_success 'manual prefetch of missing objects' ' test_line_count = 0 observed.oids ' +test_expect_success 'partial clone with transfer.fsckobjects=1 works with submodules' ' + test_create_repo submodule && + test_commit -C submodule mycommit && + + test_create_repo src_with_sub && + test_config -C src_with_sub uploadpack.allowfilter 1 && + test_config -C src_with_sub uploadpack.allowanysha1inwant 1 && + + git -C src_with_sub submodule add "file://$(pwd)/submodule" mysub && + git -C src_with_sub commit -m "commit with submodule" && + + git -c transfer.fsckobjects=1 \ + clone --filter="blob:none" "file://$(pwd)/src_with_sub" dst && + test_when_finished rm -rf dst +' + test_expect_success 'partial clone with transfer.fsckobjects=1 uses index-pack --fsck-objects' ' git init src && test_commit -C src x && diff --git a/t/t6000-rev-list-misc.sh b/t/t6000-rev-list-misc.sh index 3bb0e4ff8f..fc4d55dcb2 100755 --- a/t/t6000-rev-list-misc.sh +++ b/t/t6000-rev-list-misc.sh @@ -128,8 +128,8 @@ test_expect_success 'rev-list can negate index objects' ' test_cmp expect actual ' -test_expect_success '--bisect and --first-parent can not be combined' ' - test_must_fail git rev-list --bisect --first-parent HEAD +test_expect_success '--bisect and --first-parent can be combined' ' + git rev-list --bisect --first-parent HEAD ' test_expect_success '--header shows a NUL after each commit' ' diff --git a/t/t6002-rev-list-bisect.sh b/t/t6002-rev-list-bisect.sh index a661408038..b95a0212ad 100755 --- a/t/t6002-rev-list-bisect.sh +++ b/t/t6002-rev-list-bisect.sh @@ -263,4 +263,49 @@ test_expect_success 'rev-parse --bisect can default to good/bad refs' ' test_cmp expect.sorted actual.sorted ' +test_output_expect_success '--bisect --first-parent' 'git rev-list --bisect --first-parent E ^F' <<EOF +e4 +EOF + +test_output_expect_success '--first-parent' 'git rev-list --first-parent E ^F' <<EOF +E +e1 +e2 +e3 +e4 +e5 +e6 +e7 +e8 +EOF + +test_output_expect_success '--bisect-vars --first-parent' 'git rev-list --bisect-vars --first-parent E ^F' <<EOF +bisect_rev='e5' +bisect_nr=4 +bisect_good=4 +bisect_bad=3 +bisect_all=9 +bisect_steps=2 +EOF + +test_expect_success '--bisect-all --first-parent' ' + cat >expect.unsorted <<-EOF && + $(git rev-parse E) (tag: E, dist=0) + $(git rev-parse e1) (tag: e1, dist=1) + $(git rev-parse e2) (tag: e2, dist=2) + $(git rev-parse e3) (tag: e3, dist=3) + $(git rev-parse e4) (tag: e4, dist=4) + $(git rev-parse e5) (tag: e5, dist=4) + $(git rev-parse e6) (tag: e6, dist=3) + $(git rev-parse e7) (tag: e7, dist=2) + $(git rev-parse e8) (tag: e8, dist=1) + EOF + + # expect results to be ordered by distance (descending), + # commit hash (ascending) + sort -k4,4r -k1,1 expect.unsorted >expect && + git rev-list --bisect-all --first-parent E ^F >actual && + test_cmp expect actual +' + test_done diff --git a/t/t6018-rev-list-glob.sh b/t/t6018-rev-list-glob.sh index bb5aeac07f..b31ff7eeec 100755 --- a/t/t6018-rev-list-glob.sh +++ b/t/t6018-rev-list-glob.sh @@ -345,6 +345,11 @@ test_expect_success 'rev-list should succeed with empty output with empty glob' test_must_be_empty actual ' +test_expect_success 'rev-list should succeed with empty output when ignoring missing' ' + git rev-list --ignore-missing $ZERO_OID >actual && + test_must_be_empty actual +' + test_expect_success 'shortlog accepts --glob/--tags/--remotes' ' compare shortlog "subspace/one subspace/two" --branches=subspace && diff --git a/t/t6030-bisect-porcelain.sh b/t/t6030-bisect-porcelain.sh index 36d9b2b2e4..b886529e59 100755 --- a/t/t6030-bisect-porcelain.sh +++ b/t/t6030-bisect-porcelain.sh @@ -243,32 +243,30 @@ test_expect_success 'bisect skip: with commit both bad and skipped' ' ' # We want to automatically find the commit that -# introduced "Another" into hello. -test_expect_success \ - '"git bisect run" simple case' \ - 'echo "#"\!"/bin/sh" > test_script.sh && - echo "grep Another hello > /dev/null" >> test_script.sh && - echo "test \$? -ne 0" >> test_script.sh && - chmod +x test_script.sh && - git bisect start && - git bisect good $HASH1 && - git bisect bad $HASH4 && - git bisect run ./test_script.sh > my_bisect_log.txt && - grep "$HASH3 is the first bad commit" my_bisect_log.txt && - git bisect reset' +# added "Another" into hello. +test_expect_success '"git bisect run" simple case' ' + write_script test_script.sh <<-\EOF && + ! grep Another hello >/dev/null + EOF + git bisect start && + git bisect good $HASH1 && + git bisect bad $HASH4 && + git bisect run ./test_script.sh >my_bisect_log.txt && + grep "$HASH3 is the first bad commit" my_bisect_log.txt && + git bisect reset +' # We want to automatically find the commit that -# introduced "Ciao" into hello. -test_expect_success \ - '"git bisect run" with more complex "git bisect start"' \ - 'echo "#"\!"/bin/sh" > test_script.sh && - echo "grep Ciao hello > /dev/null" >> test_script.sh && - echo "test \$? -ne 0" >> test_script.sh && - chmod +x test_script.sh && - git bisect start $HASH4 $HASH1 && - git bisect run ./test_script.sh > my_bisect_log.txt && - grep "$HASH4 is the first bad commit" my_bisect_log.txt && - git bisect reset' +# added "Ciao" into hello. +test_expect_success '"git bisect run" with more complex "git bisect start"' ' + write_script test_script.sh <<-\EOF && + ! grep Ciao hello >/dev/null + EOF + git bisect start $HASH4 $HASH1 && + git bisect run ./test_script.sh >my_bisect_log.txt && + grep "$HASH4 is the first bad commit" my_bisect_log.txt && + git bisect reset +' # $HASH1 is good, $HASH5 is bad, we skip $HASH3 # but $HASH4 is good, @@ -295,24 +293,17 @@ HASH6= test_expect_success 'bisect run & skip: cannot tell between 2' ' add_line_into_file "6: Yet a line." hello && HASH6=$(git rev-parse --verify HEAD) && - echo "#"\!"/bin/sh" > test_script.sh && - echo "sed -ne \\\$p hello | grep Ciao > /dev/null && exit 125" >> test_script.sh && - echo "grep line hello > /dev/null" >> test_script.sh && - echo "test \$? -ne 0" >> test_script.sh && - chmod +x test_script.sh && + write_script test_script.sh <<-\EOF && + sed -ne \$p hello | grep Ciao >/dev/null && exit 125 + ! grep line hello >/dev/null + EOF git bisect start $HASH6 $HASH1 && - if git bisect run ./test_script.sh > my_bisect_log.txt - then - echo Oops, should have failed. - false - else - test $? -eq 2 && - grep "first bad commit could be any of" my_bisect_log.txt && - ! grep $HASH3 my_bisect_log.txt && - ! grep $HASH6 my_bisect_log.txt && - grep $HASH4 my_bisect_log.txt && - grep $HASH5 my_bisect_log.txt - fi + test_expect_code 2 git bisect run ./test_script.sh >my_bisect_log.txt && + grep "first bad commit could be any of" my_bisect_log.txt && + ! grep $HASH3 my_bisect_log.txt && + ! grep $HASH6 my_bisect_log.txt && + grep $HASH4 my_bisect_log.txt && + grep $HASH5 my_bisect_log.txt ' HASH7= @@ -320,14 +311,13 @@ test_expect_success 'bisect run & skip: find first bad' ' git bisect reset && add_line_into_file "7: Should be the last line." hello && HASH7=$(git rev-parse --verify HEAD) && - echo "#"\!"/bin/sh" > test_script.sh && - echo "sed -ne \\\$p hello | grep Ciao > /dev/null && exit 125" >> test_script.sh && - echo "sed -ne \\\$p hello | grep day > /dev/null && exit 125" >> test_script.sh && - echo "grep Yet hello > /dev/null" >> test_script.sh && - echo "test \$? -ne 0" >> test_script.sh && - chmod +x test_script.sh && + write_script test_script.sh <<-\EOF && + sed -ne \$p hello | grep Ciao >/dev/null && exit 125 + sed -ne \$p hello | grep day >/dev/null && exit 125 + ! grep Yet hello >/dev/null + EOF git bisect start $HASH7 $HASH1 && - git bisect run ./test_script.sh > my_bisect_log.txt && + git bisect run ./test_script.sh >my_bisect_log.txt && grep "$HASH6 is the first bad commit" my_bisect_log.txt ' @@ -458,6 +448,24 @@ test_expect_success 'many merge bases creation' ' grep "$SIDE_HASH5" merge_bases.txt ' +# We want to automatically find the merge that +# added "line" into hello. +test_expect_success '"git bisect run --first-parent" simple case' ' + git rev-list --first-parent $B_HASH ^$HASH4 >first_parent_chain.txt && + write_script test_script.sh <<-\EOF && + grep $(git rev-parse HEAD) first_parent_chain.txt || exit -1 + ! grep line hello >/dev/null + EOF + git bisect start --first-parent && + test_path_is_file ".git/BISECT_FIRST_PARENT" && + git bisect good $HASH4 && + git bisect bad $B_HASH && + git bisect run ./test_script.sh >my_bisect_log.txt && + grep "$B_HASH is the first bad commit" my_bisect_log.txt && + git bisect reset && + test_path_is_missing .git/BISECT_FIRST_PARENT +' + test_expect_success 'good merge bases when good and bad are siblings' ' git bisect start "$B_HASH" "$A_HASH" > my_bisect_log.txt && test_i18ngrep "merge base must be tested" my_bisect_log.txt && diff --git a/t/t6300-for-each-ref.sh b/t/t6300-for-each-ref.sh index a83579fbdf..58adee7d18 100755 --- a/t/t6300-for-each-ref.sh +++ b/t/t6300-for-each-ref.sh @@ -776,61 +776,40 @@ test_expect_success 'set up trailers for next test' ' ' test_expect_success '%(trailers:unfold) unfolds trailers' ' - git for-each-ref --format="%(trailers:unfold)" refs/heads/master >actual && { unfold <trailers echo } >expect && + git for-each-ref --format="%(trailers:unfold)" refs/heads/master >actual && + test_cmp expect actual && + git for-each-ref --format="%(contents:trailers:unfold)" refs/heads/master >actual && test_cmp expect actual ' test_expect_success '%(trailers:only) shows only "key: value" trailers' ' - git for-each-ref --format="%(trailers:only)" refs/heads/master >actual && { grep -v patch.description <trailers && echo } >expect && + git for-each-ref --format="%(trailers:only)" refs/heads/master >actual && + test_cmp expect actual && + git for-each-ref --format="%(contents:trailers:only)" refs/heads/master >actual && test_cmp expect actual ' test_expect_success '%(trailers:only) and %(trailers:unfold) work together' ' - git for-each-ref --format="%(trailers:only,unfold)" refs/heads/master >actual && - git for-each-ref --format="%(trailers:unfold,only)" refs/heads/master >reverse && - test_cmp actual reverse && { grep -v patch.description <trailers | unfold && echo } >expect && - test_cmp expect actual -' - -test_expect_success '%(contents:trailers:unfold) unfolds trailers' ' - git for-each-ref --format="%(contents:trailers:unfold)" refs/heads/master >actual && - { - unfold <trailers - echo - } >expect && - test_cmp expect actual -' - -test_expect_success '%(contents:trailers:only) shows only "key: value" trailers' ' - git for-each-ref --format="%(contents:trailers:only)" refs/heads/master >actual && - { - grep -v patch.description <trailers && - echo - } >expect && - test_cmp expect actual -' - -test_expect_success '%(contents:trailers:only) and %(contents:trailers:unfold) work together' ' + git for-each-ref --format="%(trailers:only,unfold)" refs/heads/master >actual && + test_cmp expect actual && + git for-each-ref --format="%(trailers:unfold,only)" refs/heads/master >actual && + test_cmp actual actual && git for-each-ref --format="%(contents:trailers:only,unfold)" refs/heads/master >actual && - git for-each-ref --format="%(contents:trailers:unfold,only)" refs/heads/master >reverse && - test_cmp actual reverse && - { - grep -v patch.description <trailers | unfold && - echo - } >expect && - test_cmp expect actual + test_cmp expect actual && + git for-each-ref --format="%(contents:trailers:unfold,only)" refs/heads/master >actual && + test_cmp actual actual ' test_expect_success '%(trailers) rejects unknown trailers arguments' ' @@ -839,15 +818,16 @@ test_expect_success '%(trailers) rejects unknown trailers arguments' ' fatal: unknown %(trailers) argument: unsupported EOF test_must_fail git for-each-ref --format="%(trailers:unsupported)" 2>actual && + test_i18ncmp expect actual && + test_must_fail git for-each-ref --format="%(contents:trailers:unsupported)" 2>actual && test_i18ncmp expect actual ' -test_expect_success '%(contents:trailers) rejects unknown trailers arguments' ' - # error message cannot be checked under i18n +test_expect_success 'if arguments, %(contents:trailers) shows error if colon is missing' ' cat >expect <<-EOF && - fatal: unknown %(trailers) argument: unsupported + fatal: unrecognized %(contents) argument: trailersonly EOF - test_must_fail git for-each-ref --format="%(contents:trailers:unsupported)" 2>actual && + test_must_fail git for-each-ref --format="%(contents:trailersonly)" 2>actual && test_i18ncmp expect actual ' diff --git a/t/t6020-merge-df.sh b/t/t6400-merge-df.sh index 400a4cd139..400a4cd139 100755 --- a/t/t6020-merge-df.sh +++ b/t/t6400-merge-df.sh diff --git a/t/t6021-merge-criss-cross.sh b/t/t6401-merge-criss-cross.sh index 9d5e992878..9d5e992878 100755 --- a/t/t6021-merge-criss-cross.sh +++ b/t/t6401-merge-criss-cross.sh diff --git a/t/t6022-merge-rename.sh b/t/t6402-merge-rename.sh index bbbba3dcbf..bbbba3dcbf 100755 --- a/t/t6022-merge-rename.sh +++ b/t/t6402-merge-rename.sh diff --git a/t/t6023-merge-file.sh b/t/t6403-merge-file.sh index 2f421d967a..2f421d967a 100755 --- a/t/t6023-merge-file.sh +++ b/t/t6403-merge-file.sh diff --git a/t/t6024-recursive-merge.sh b/t/t6404-recursive-merge.sh index 332cfc53fd..332cfc53fd 100755 --- a/t/t6024-recursive-merge.sh +++ b/t/t6404-recursive-merge.sh diff --git a/t/t6025-merge-symlinks.sh b/t/t6405-merge-symlinks.sh index 6c0a90d044..6c0a90d044 100755 --- a/t/t6025-merge-symlinks.sh +++ b/t/t6405-merge-symlinks.sh diff --git a/t/t6026-merge-attr.sh b/t/t6406-merge-attr.sh index 76a55f838c..76a55f838c 100755 --- a/t/t6026-merge-attr.sh +++ b/t/t6406-merge-attr.sh diff --git a/t/t6027-merge-binary.sh b/t/t6407-merge-binary.sh index 4e6c7cb77e..4e6c7cb77e 100755 --- a/t/t6027-merge-binary.sh +++ b/t/t6407-merge-binary.sh diff --git a/t/t6028-merge-up-to-date.sh b/t/t6408-merge-up-to-date.sh index 7763c1ba98..7763c1ba98 100755 --- a/t/t6028-merge-up-to-date.sh +++ b/t/t6408-merge-up-to-date.sh diff --git a/t/t6029-merge-subtree.sh b/t/t6409-merge-subtree.sh index 793f0c8bf3..793f0c8bf3 100755 --- a/t/t6029-merge-subtree.sh +++ b/t/t6409-merge-subtree.sh diff --git a/t/t6031-merge-filemode.sh b/t/t6411-merge-filemode.sh index 87741efad3..87741efad3 100755 --- a/t/t6031-merge-filemode.sh +++ b/t/t6411-merge-filemode.sh diff --git a/t/t6032-merge-large-rename.sh b/t/t6412-merge-large-rename.sh index 80777386dc..80777386dc 100755 --- a/t/t6032-merge-large-rename.sh +++ b/t/t6412-merge-large-rename.sh diff --git a/t/t6033-merge-crlf.sh b/t/t6413-merge-crlf.sh index e8d65eefb5..e8d65eefb5 100755 --- a/t/t6033-merge-crlf.sh +++ b/t/t6413-merge-crlf.sh diff --git a/t/t6034-merge-rename-nocruft.sh b/t/t6414-merge-rename-nocruft.sh index a25e730460..a25e730460 100755 --- a/t/t6034-merge-rename-nocruft.sh +++ b/t/t6414-merge-rename-nocruft.sh diff --git a/t/t6035-merge-dir-to-symlink.sh b/t/t6415-merge-dir-to-symlink.sh index 2eddcc7664..2eddcc7664 100755 --- a/t/t6035-merge-dir-to-symlink.sh +++ b/t/t6415-merge-dir-to-symlink.sh diff --git a/t/t6036-recursive-corner-cases.sh b/t/t6416-recursive-corner-cases.sh index b3bf462617..fd98989b14 100755 --- a/t/t6036-recursive-corner-cases.sh +++ b/t/t6416-recursive-corner-cases.sh @@ -452,7 +452,7 @@ test_expect_success 'git detects conflict merging criss-cross+modify/delete, rev # # So choice 5 at least provides some kind of conflict for the original case, # and can merge cleanly as expected with D1 and E3. It also made things just -# slightly funny for merging D1 and e$, where E4 is defined as: +# slightly funny for merging D1 and E4, where E4 is defined as: # Commit E4: Merge B & C, modifying 'a' and renaming to 'a2', and deleting 'a/' # in this case, we'll get a rename/rename(1to2) conflict because a~$UNIQUE # gets renamed to 'a' in D1 and to 'a2' in E4. But that's better than having @@ -1144,7 +1144,7 @@ test_expect_failure 'check symlink add/add' ' test_must_fail git merge -s recursive E^0 && git ls-files -s >out && - test_line_count = 2 out && + test_line_count = 3 out && git ls-files -u >out && test_line_count = 2 out && git ls-files -o >out && diff --git a/t/t6037-merge-ours-theirs.sh b/t/t6417-merge-ours-theirs.sh index 0aebc6c028..0aebc6c028 100755 --- a/t/t6037-merge-ours-theirs.sh +++ b/t/t6417-merge-ours-theirs.sh diff --git a/t/t6038-merge-text-auto.sh b/t/t6418-merge-text-auto.sh index 89c86d4e56..30983d18b1 100755 --- a/t/t6038-merge-text-auto.sh +++ b/t/t6418-merge-text-auto.sh @@ -197,7 +197,8 @@ test_expect_success 'Test delete/normalize conflict' ' git commit -m "remove file" && git checkout master && git reset --hard a^ && - git merge side + git merge side && + test_path_is_missing file ' test_done diff --git a/t/t6039-merge-ignorecase.sh b/t/t6419-merge-ignorecase.sh index 531850d834..531850d834 100755 --- a/t/t6039-merge-ignorecase.sh +++ b/t/t6419-merge-ignorecase.sh diff --git a/t/t6042-merge-rename-corner-cases.sh b/t/t6422-merge-rename-corner-cases.sh index f163893ff9..3375eaf4e7 100755 --- a/t/t6042-merge-rename-corner-cases.sh +++ b/t/t6422-merge-rename-corner-cases.sh @@ -457,7 +457,7 @@ test_expect_success 'handle rename-with-content-merge vs. add' ' git checkout A^0 && test_must_fail git merge -s recursive B^0 >out && - test_i18ngrep "CONFLICT (rename/add)" out && + test_i18ngrep "CONFLICT (.*/add)" out && git ls-files -s >out && test_line_count = 2 out && @@ -503,7 +503,7 @@ test_expect_success 'handle rename-with-content-merge vs. add, merge other way' git checkout B^0 && test_must_fail git merge -s recursive A^0 >out && - test_i18ngrep "CONFLICT (rename/add)" out && + test_i18ngrep "CONFLICT (.*/add)" out && git ls-files -s >out && test_line_count = 2 out && @@ -583,7 +583,7 @@ test_expect_success 'handle rename/rename (2to1) conflict correctly' ' git checkout B^0 && test_must_fail git merge -s recursive C^0 >out && - test_i18ngrep "CONFLICT (rename/rename)" out && + test_i18ngrep "CONFLICT (\(.*\)/\1)" out && git ls-files -s >out && test_line_count = 2 out && @@ -886,12 +886,17 @@ test_expect_failure 'rad-check: rename/add/delete conflict' ' git checkout B^0 && test_must_fail git merge -s recursive A^0 >out 2>err && - # Not sure whether the output should contain just one - # "CONFLICT (rename/add/delete)" line, or if it should break - # it into a pair of "CONFLICT (rename/delete)" and - # "CONFLICT (rename/add)"; allow for either. - test_i18ngrep "CONFLICT (rename.*add)" out && - test_i18ngrep "CONFLICT (rename.*delete)" out && + # Instead of requiring the output to contain one combined line + # CONFLICT (rename/add/delete) + # or perhaps two lines: + # CONFLICT (rename/add): new file collides with rename target + # CONFLICT (rename/delete): rename source removed on other side + # and instead of requiring "rename/add" instead of "add/add", + # be flexible in the type of console output message(s) reported + # for this particular case; we will be more stringent about the + # contents of the index and working directory. + test_i18ngrep "CONFLICT (.*/add)" out && + test_i18ngrep "CONFLICT (rename.*/delete)" out && test_must_be_empty err && git ls-files -s >file_count && @@ -899,14 +904,14 @@ test_expect_failure 'rad-check: rename/add/delete conflict' ' git ls-files -u >file_count && test_line_count = 2 file_count && git ls-files -o >file_count && - test_line_count = 2 file_count && + test_line_count = 3 file_count && git rev-parse >actual \ :2:bar :3:bar && git rev-parse >expect \ B:bar A:bar && - test_cmp file_is_missing foo && + test_path_is_missing foo && # bar should have two-way merged contents of the different # versions of bar; check that content from both sides is # present. @@ -954,11 +959,17 @@ test_expect_failure 'rrdd-check: rename/rename(2to1)/delete/delete conflict' ' git checkout A^0 && test_must_fail git merge -s recursive B^0 >out 2>err && - # Not sure whether the output should contain just one - # "CONFLICT (rename/rename/delete/delete)" line, or if it - # should break it into three: "CONFLICT (rename/rename)" and - # two "CONFLICT (rename/delete)" lines; allow for either. - test_i18ngrep "CONFLICT (rename/rename)" out && + # Instead of requiring the output to contain one combined line + # CONFLICT (rename/rename/delete/delete) + # or perhaps two lines: + # CONFLICT (rename/rename): ... + # CONFLICT (rename/delete): info about pair 1 + # CONFLICT (rename/delete): info about pair 2 + # and instead of requiring "rename/rename" instead of "add/add", + # be flexible in the type of console output message(s) reported + # for this particular case; we will be more stringent about the + # contents of the index and working directory. + test_i18ngrep "CONFLICT (\(.*\)/\1)" out && test_i18ngrep "CONFLICT (rename.*delete)" out && test_must_be_empty err && @@ -967,15 +978,15 @@ test_expect_failure 'rrdd-check: rename/rename(2to1)/delete/delete conflict' ' git ls-files -u >file_count && test_line_count = 2 file_count && git ls-files -o >file_count && - test_line_count = 2 file_count && + test_line_count = 3 file_count && git rev-parse >actual \ :2:baz :3:baz && git rev-parse >expect \ O:foo O:bar && - test_cmp file_is_missing foo && - test_cmp file_is_missing bar && + test_path_is_missing foo && + test_path_is_missing bar && # baz should have two-way merged contents of the original # contents of foo and bar; check that content from both sides # is present. @@ -1042,25 +1053,25 @@ test_expect_failure 'mod6-check: chains of rename/rename(1to2) and rename/rename test_must_be_empty err && git ls-files -s >file_count && - test_line_count = 6 file_count && + test_line_count = 9 file_count && git ls-files -u >file_count && - test_line_count = 6 file_count && + test_line_count = 9 file_count && git ls-files -o >file_count && test_line_count = 3 file_count && test_seq 10 20 >merged-one && test_seq 51 60 >merged-five && # Determine what the merge of three would give us. - test_seq 30 40 >three-side-A && + test_seq 31 39 >three-base && + test_seq 31 40 >three-side-A && test_seq 31 39 >three-side-B && - echo forty >three-side-B && - >empty && + echo forty >>three-side-B && test_must_fail git merge-file \ - -L "HEAD" \ + -L "HEAD:four" \ -L "" \ - -L "B^0" \ - three-side-A empty three-side-B && - sed -e "s/^\([<=>]\)/\1\1\1/" three-side-A >merged-three && + -L "B^0:two" \ + three-side-A three-base three-side-B && + sed -e "s/^\([<=>]\)/\1\1/" three-side-A >merged-three && # Verify the index is as expected git rev-parse >actual \ @@ -1075,6 +1086,7 @@ test_expect_failure 'mod6-check: chains of rename/rename(1to2) and rename/rename git cat-file -p :2:two >expect && git cat-file -p :3:two >other && + >empty && test_must_fail git merge-file \ -L "HEAD" -L "" -L "B^0" \ expect empty other && diff --git a/t/t6043-merge-rename-directories.sh b/t/t6423-merge-rename-directories.sh index 83792c5ef1..f7ecbb886d 100755 --- a/t/t6043-merge-rename-directories.sh +++ b/t/t6423-merge-rename-directories.sh @@ -275,7 +275,7 @@ test_expect_success '1d: Directory renames cause a rename/rename(2to1) conflict' git checkout A^0 && test_must_fail git -c merge.directoryRenames=true merge -s recursive B^0 >out && - test_i18ngrep "CONFLICT (rename/rename)" out && + test_i18ngrep "CONFLICT (\(.*\)/\1)" out && git ls-files -s >out && test_line_count = 8 out && @@ -1686,7 +1686,7 @@ test_expect_success '7b: rename/rename(2to1), but only due to transitive rename' git checkout A^0 && test_must_fail git -c merge.directoryRenames=true merge -s recursive B^0 >out && - test_i18ngrep "CONFLICT (rename/rename)" out && + test_i18ngrep "CONFLICT (\(.*\)/\1)" out && git ls-files -s >out && test_line_count = 4 out && @@ -2260,24 +2260,23 @@ test_expect_success '8d: rename/delete...or not?' ' # Commit B: w/{b,c}, z/d # # Possible Resolutions: -# w/o dir-rename detection: z/d, CONFLICT(z/b -> y/b vs. w/b), -# CONFLICT(z/c -> y/c vs. w/c) -# Currently expected: y/d, CONFLICT(z/b -> y/b vs. w/b), -# CONFLICT(z/c -> y/c vs. w/c) -# Optimal: ?? +# if z not considered renamed: z/d, CONFLICT(z/b -> y/b vs. w/b), +# CONFLICT(z/c -> y/c vs. w/c) +# if z->y rename considered: y/d, CONFLICT(z/b -> y/b vs. w/b), +# CONFLICT(z/c -> y/c vs. w/c) +# Optimal: ?? # # Notes: In commit A, directory z got renamed to y. In commit B, directory z # did NOT get renamed; the directory is still present; instead it is # considered to have just renamed a subset of paths in directory z -# elsewhere. Therefore, the directory rename done in commit A to z/ -# applies to z/d and maps it to y/d. +# elsewhere. However, this is much like testcase 6b (where commit B +# moves all the original paths out of z/ but opted to keep d +# within z/). This makes it hard to judge where d should end up. # # It's possible that users would get confused about this, but what -# should we do instead? Silently leaving at z/d seems just as bad or -# maybe even worse. Perhaps we could print a big warning about z/d -# and how we're moving to y/d in this case, but when I started thinking -# about the ramifications of doing that, I didn't know how to rule out -# that opening other weird edge and corner cases so I just punted. +# should we do instead? It's not at all clear to me whether z/d or +# y/d or something else is a better resolution here, and other cases +# start getting really tricky, so I just picked one. test_setup_8e () { test_create_repo 8e && @@ -2844,6 +2843,14 @@ test_expect_success '9f: Renamed directory that only contained immediate subdirs # Commit A: priority/{alpha,bravo}/$more_files # Commit B: goal/{a,b}/$more_files, goal/c # Expected: priority/{alpha,bravo}/$more_files, priority/c +# We currently fail this test because the directory renames we detect are +# goal/a/ -> priority/alpha/ +# goal/b/ -> priority/bravo/ +# We do not detect +# goal/ -> priority/ +# because of no files found within goal/, and the fact that "a" != "alpha" +# and "b" != "bravo". But I'm not sure it's really a failure given that +# viewpoint... test_setup_9g () { test_create_repo 9g && @@ -2880,6 +2887,7 @@ test_setup_9g () { } test_expect_failure '9g: Renamed directory that only contained immediate subdirs, immediate subdirs renamed' ' + test_setup_9g && ( cd 9g && @@ -3362,6 +3370,7 @@ test_setup_10e () { } test_expect_failure '10e: Does git complain about untracked file that is not really in the way?' ' + test_setup_10e && ( cd 10e && @@ -4403,7 +4412,7 @@ test_expect_success '13b(info): messages for transitive rename with conflicted c # Commit O: z/{b,c}, x/{d,e} # Commit A: y/{b,c,d}, x/e # Commit B: z/{b,c,d}, x/e -# Expected: y/{b,c,d}, with info or conflict messages for d ( +# Expected: y/{b,c,d}, x/e, with info or conflict messages for d # A: renamed x/d -> z/d; B: renamed z/ -> y/ AND renamed x/d to y/d # One could argue A had partial knowledge of what was done with # d and B had full knowledge, but that's a slippery slope as diff --git a/t/t6044-merge-unrelated-index-changes.sh b/t/t6424-merge-unrelated-index-changes.sh index 5e3779ebc9..5e3779ebc9 100755 --- a/t/t6044-merge-unrelated-index-changes.sh +++ b/t/t6424-merge-unrelated-index-changes.sh diff --git a/t/t6045-merge-rename-delete.sh b/t/t6425-merge-rename-delete.sh index 5d33577d2f..f79d021590 100755 --- a/t/t6045-merge-rename-delete.sh +++ b/t/t6425-merge-rename-delete.sh @@ -17,7 +17,8 @@ test_expect_success 'rename/delete' ' git commit -m "delete" && test_must_fail git merge --strategy=recursive rename >output && - test_i18ngrep "CONFLICT (rename/delete): A deleted in HEAD and renamed to B in rename. Version rename of B left in tree." output + test_i18ngrep "CONFLICT (rename/delete): A.* renamed .*to B.* in rename" output && + test_i18ngrep "CONFLICT (rename/delete): A.*deleted in HEAD." output ' test_done diff --git a/t/t6046-merge-skip-unneeded-updates.sh b/t/t6426-merge-skip-unneeded-updates.sh index 5a2d07e516..699813671c 100755 --- a/t/t6046-merge-skip-unneeded-updates.sh +++ b/t/t6426-merge-skip-unneeded-updates.sh @@ -374,7 +374,7 @@ test_expect_success '2c: Modify b & add c VS rename b->c' ' export GIT_MERGE_VERBOSITY && test_must_fail git merge -s recursive B^0 >out 2>err && - test_i18ngrep "CONFLICT (rename/add): Rename b->c" out && + test_i18ngrep "CONFLICT (.*/add):" out && test_must_be_empty err && # Make sure c WAS updated diff --git a/t/t6047-diff3-conflict-markers.sh b/t/t6427-diff3-conflict-markers.sh index f4655bb358..f4655bb358 100755 --- a/t/t6047-diff3-conflict-markers.sh +++ b/t/t6427-diff3-conflict-markers.sh diff --git a/t/t3030-merge-recursive.sh b/t/t6430-merge-recursive.sh index d48d211a95..d48d211a95 100755 --- a/t/t3030-merge-recursive.sh +++ b/t/t6430-merge-recursive.sh diff --git a/t/t3031-merge-criscross.sh b/t/t6431-merge-criscross.sh index 3824756a02..3824756a02 100755 --- a/t/t3031-merge-criscross.sh +++ b/t/t6431-merge-criscross.sh diff --git a/t/t3032-merge-recursive-space-options.sh b/t/t6432-merge-recursive-space-options.sh index b56180ee4a..b56180ee4a 100755 --- a/t/t3032-merge-recursive-space-options.sh +++ b/t/t6432-merge-recursive-space-options.sh diff --git a/t/t3033-merge-toplevel.sh b/t/t6433-merge-toplevel.sh index e29c284b9b..e29c284b9b 100755 --- a/t/t3033-merge-toplevel.sh +++ b/t/t6433-merge-toplevel.sh diff --git a/t/t3034-merge-recursive-rename-options.sh b/t/t6434-merge-recursive-rename-options.sh index 3d9fae68c4..3d9fae68c4 100755 --- a/t/t3034-merge-recursive-rename-options.sh +++ b/t/t6434-merge-recursive-rename-options.sh diff --git a/t/t3035-merge-sparse.sh b/t/t6435-merge-sparse.sh index 74562e1235..74562e1235 100755 --- a/t/t3035-merge-sparse.sh +++ b/t/t6435-merge-sparse.sh diff --git a/t/t7607-merge-overwrite.sh b/t/t6436-merge-overwrite.sh index dd8ab7ede1..dd8ab7ede1 100755 --- a/t/t7607-merge-overwrite.sh +++ b/t/t6436-merge-overwrite.sh diff --git a/t/t7405-submodule-merge.sh b/t/t6437-submodule-merge.sh index 6a1e5f8232..6a1e5f8232 100755 --- a/t/t7405-submodule-merge.sh +++ b/t/t6437-submodule-merge.sh diff --git a/t/t7613-merge-submodule.sh b/t/t6438-submodule-directory-file-conflicts.sh index 04bf4be7d7..04bf4be7d7 100755 --- a/t/t7613-merge-submodule.sh +++ b/t/t6438-submodule-directory-file-conflicts.sh diff --git a/t/t7609-merge-co-error-msgs.sh b/t/t6439-merge-co-error-msgs.sh index 5c8894d94f..5c8894d94f 100755 --- a/t/t7609-merge-co-error-msgs.sh +++ b/t/t6439-merge-co-error-msgs.sh diff --git a/t/t7401-submodule-summary.sh b/t/t7401-submodule-summary.sh index 9bc841d085..cc87d26619 100755 --- a/t/t7401-submodule-summary.sh +++ b/t/t7401-submodule-summary.sh @@ -5,8 +5,11 @@ test_description='Summary support for submodules -This test tries to verify the sanity of summary subcommand of git submodule. +This test script tries to verify the sanity of summary subcommand of git submodule. ' +# NEEDSWORK: This test script is old fashioned and may need a big cleanup due to +# various reasons, one of them being that there are lots of commands taking place +# outside of 'test_expect_success' block, which is no longer in good-style. . ./test-lib.sh @@ -16,12 +19,12 @@ add_file () { owd=$(pwd) cd "$sm" for name; do - echo "$name" > "$name" && + echo "$name" >"$name" && git add "$name" && test_tick && git commit -m "Add $name" done >/dev/null - git rev-parse --verify HEAD | cut -c1-7 + git rev-parse --short HEAD cd "$owd" } commit_file () { @@ -38,10 +41,10 @@ test_expect_success 'added submodule' " git add sm1 && git submodule summary >actual && cat >expected <<-EOF && -* sm1 0000000...$head1 (2): - > Add foo2 + * sm1 0000000...$head1 (2): + > Add foo2 -EOF + EOF test_cmp expected actual " @@ -52,10 +55,10 @@ test_expect_success 'added submodule (subdirectory)' " git submodule summary >../actual ) && cat >expected <<-EOF && -* ../sm1 0000000...$head1 (2): - > Add foo2 + * ../sm1 0000000...$head1 (2): + > Add foo2 -EOF + EOF test_cmp expected actual " @@ -73,10 +76,10 @@ test_expect_success 'added submodule (subdirectory with explicit path)' " git submodule summary ../sm1 >../actual ) && cat >expected <<-EOF && -* ../sm1 0000000...$head1 (2): - > Add foo2 + * ../sm1 0000000...$head1 (2): + > Add foo2 -EOF + EOF test_cmp expected actual " @@ -86,20 +89,20 @@ head2=$(add_file sm1 foo3) test_expect_success 'modified submodule(forward)' " git submodule summary >actual && cat >expected <<-EOF && -* sm1 $head1...$head2 (1): - > Add foo3 + * sm1 $head1...$head2 (1): + > Add foo3 -EOF + EOF test_cmp expected actual " test_expect_success 'modified submodule(forward), --files' " git submodule summary --files >actual && cat >expected <<-EOF && -* sm1 $head1...$head2 (1): - > Add foo3 + * sm1 $head1...$head2 (1): + > Add foo3 -EOF + EOF test_cmp expected actual " @@ -110,10 +113,10 @@ test_expect_success 'no ignore=all setting has any effect' " git config diff.ignoreSubmodules all && git submodule summary >actual && cat >expected <<-EOF && -* sm1 $head1...$head2 (1): - > Add foo3 + * sm1 $head1...$head2 (1): + > Add foo3 -EOF + EOF test_cmp expected actual && git config --unset diff.ignoreSubmodules && git config --remove-section submodule.sm1 && @@ -125,17 +128,17 @@ commit_file sm1 && head3=$( cd sm1 && git reset --hard HEAD~2 >/dev/null && - git rev-parse --verify HEAD | cut -c1-7 + git rev-parse --short HEAD ) test_expect_success 'modified submodule(backward)' " git submodule summary >actual && cat >expected <<-EOF && -* sm1 $head2...$head3 (2): - < Add foo3 - < Add foo2 + * sm1 $head2...$head3 (2): + < Add foo3 + < Add foo2 -EOF + EOF test_cmp expected actual " @@ -144,25 +147,25 @@ head4_full=$(GIT_DIR=sm1/.git git rev-parse --verify HEAD) test_expect_success 'modified submodule(backward and forward)' " git submodule summary >actual && cat >expected <<-EOF && -* sm1 $head2...$head4 (4): - > Add foo5 - > Add foo4 - < Add foo3 - < Add foo2 + * sm1 $head2...$head4 (4): + > Add foo5 + > Add foo4 + < Add foo3 + < Add foo2 -EOF + EOF test_cmp expected actual " test_expect_success '--summary-limit' " git submodule summary -n 3 >actual && cat >expected <<-EOF && -* sm1 $head2...$head4 (4): - > Add foo5 - > Add foo4 - < Add foo3 + * sm1 $head2...$head4 (4): + > Add foo5 + > Add foo4 + < Add foo3 -EOF + EOF test_cmp expected actual " @@ -177,21 +180,21 @@ mv sm1-bak sm1 test_expect_success 'typechanged submodule(submodule->blob), --cached' " git submodule summary --cached >actual && cat >expected <<-EOF && -* sm1 $head4(submodule)->$head5(blob) (3): - < Add foo5 + * sm1 $head4(submodule)->$head5(blob) (3): + < Add foo5 -EOF - test_i18ncmp actual expected + EOF + test_i18ncmp expected actual " test_expect_success 'typechanged submodule(submodule->blob), --files' " git submodule summary --files >actual && cat >expected <<-EOF && -* sm1 $head5(blob)->$head4(submodule) (3): - > Add foo5 + * sm1 $head5(blob)->$head4(submodule) (3): + > Add foo5 -EOF - test_i18ncmp actual expected + EOF + test_i18ncmp expected actual " rm -rf sm1 && @@ -199,10 +202,10 @@ git checkout-index sm1 test_expect_success 'typechanged submodule(submodule->blob)' " git submodule summary >actual && cat >expected <<-EOF && -* sm1 $head4(submodule)->$head5(blob): + * sm1 $head4(submodule)->$head5(blob): -EOF - test_i18ncmp actual expected + EOF + test_i18ncmp expected actual " rm -f sm1 && @@ -211,21 +214,21 @@ head6=$(add_file sm1 foo6 foo7) test_expect_success 'nonexistent commit' " git submodule summary >actual && cat >expected <<-EOF && -* sm1 $head4...$head6: - Warn: sm1 doesn't contain commit $head4_full + * sm1 $head4...$head6: + Warn: sm1 doesn't contain commit $head4_full -EOF - test_i18ncmp actual expected + EOF + test_i18ncmp expected actual " commit_file test_expect_success 'typechanged submodule(blob->submodule)' " git submodule summary >actual && cat >expected <<-EOF && -* sm1 $head5(blob)->$head6(submodule) (2): - > Add foo7 + * sm1 $head5(blob)->$head6(submodule) (2): + > Add foo7 -EOF + EOF test_i18ncmp expected actual " @@ -234,9 +237,9 @@ rm -rf sm1 test_expect_success 'deleted submodule' " git submodule summary >actual && cat >expected <<-EOF && -* sm1 $head6...0000000: + * sm1 $head6...0000000: -EOF + EOF test_cmp expected actual " @@ -249,22 +252,22 @@ test_expect_success 'create second submodule' ' test_expect_success 'multiple submodules' " git submodule summary >actual && cat >expected <<-EOF && -* sm1 $head6...0000000: + * sm1 $head6...0000000: -* sm2 0000000...$head7 (2): - > Add foo9 + * sm2 0000000...$head7 (2): + > Add foo9 -EOF + EOF test_cmp expected actual " test_expect_success 'path filter' " git submodule summary sm2 >actual && cat >expected <<-EOF && -* sm2 0000000...$head7 (2): - > Add foo9 + * sm2 0000000...$head7 (2): + > Add foo9 -EOF + EOF test_cmp expected actual " @@ -272,24 +275,24 @@ commit_file sm2 test_expect_success 'given commit' " git submodule summary HEAD^ >actual && cat >expected <<-EOF && -* sm1 $head6...0000000: + * sm1 $head6...0000000: -* sm2 0000000...$head7 (2): - > Add foo9 + * sm2 0000000...$head7 (2): + > Add foo9 -EOF + EOF test_cmp expected actual " test_expect_success '--for-status' " git submodule summary --for-status HEAD^ >actual && - test_i18ncmp actual - <<EOF -* sm1 $head6...0000000: + test_i18ncmp - actual <<-EOF + * sm1 $head6...0000000: -* sm2 0000000...$head7 (2): - > Add foo9 + * sm2 0000000...$head7 (2): + > Add foo9 -EOF + EOF " test_expect_success 'fail when using --files together with --cached' " diff --git a/t/t7518-ident-corner-cases.sh b/t/t7518-ident-corner-cases.sh index b22f631261..dc3e9c8c88 100755 --- a/t/t7518-ident-corner-cases.sh +++ b/t/t7518-ident-corner-cases.sh @@ -29,7 +29,18 @@ test_expect_success 'empty configured name does not auto-detect' ' sane_unset GIT_AUTHOR_NAME && test_must_fail \ git -c user.name= commit --allow-empty -m foo 2>err && - test_i18ngrep "empty ident name" err + test_i18ngrep "empty ident name" err && + test_i18ngrep "Author identity unknown" err + ) +' + +test_expect_success 'empty configured name does not auto-detect for committer' ' + ( + sane_unset GIT_COMMITTER_NAME && + test_must_fail \ + git -c user.name= commit --allow-empty -m foo 2>err && + test_i18ngrep "empty ident name" err && + test_i18ngrep "Committer identity unknown" err ) ' diff --git a/t/t8003-blame-corner-cases.sh b/t/t8003-blame-corner-cases.sh index b871dd4f86..ba8013b002 100755 --- a/t/t8003-blame-corner-cases.sh +++ b/t/t8003-blame-corner-cases.sh @@ -273,18 +273,14 @@ test_expect_success 'blame file with CRLF core.autocrlf=true' ' grep "A U Thor" actual ' -# Tests the splitting and merging of blame entries in blame_coalesce(). -# The output of blame is the same, regardless of whether blame_coalesce() runs -# or not, so we'd likely only notice a problem if blame crashes or assigned -# blame to the "splitting" commit ('SPLIT' below). -test_expect_success 'blame coalesce' ' +test_expect_success 'setup coalesce tests' ' cat >giraffe <<-\EOF && ABC DEF EOF git add giraffe && git commit -m "original file" && - oid=$(git rev-parse HEAD) && + orig=$(git rev-parse HEAD) && cat >giraffe <<-\EOF && ABC @@ -293,6 +289,7 @@ test_expect_success 'blame coalesce' ' EOF git add giraffe && git commit -m "interior SPLIT line" && + split=$(git rev-parse HEAD) && cat >giraffe <<-\EOF && ABC @@ -300,12 +297,25 @@ test_expect_success 'blame coalesce' ' EOF git add giraffe && git commit -m "same contents as original" && + final=$(git rev-parse HEAD) +' + +test_expect_success 'blame coalesce' ' + cat >expect <<-EOF && + $orig 1 1 2 + $orig 2 2 + EOF + git blame --porcelain $final giraffe >actual.raw && + grep "^$orig" actual.raw >actual && + test_cmp expect actual +' +test_expect_success 'blame does not coalesce non-adjacent result lines' ' cat >expect <<-EOF && - $oid 1) ABC - $oid 2) DEF + $orig 1) ABC + $orig 3) DEF EOF - git -c core.abbrev=$(test_oid hexsz) blame -s giraffe >actual && + git blame --no-abbrev -s -L1,1 -L3,3 $split giraffe >actual && test_cmp expect actual ' diff --git a/t/t9001-send-email.sh b/t/t9001-send-email.sh index 3d68570450..a08f72596a 100755 --- a/t/t9001-send-email.sh +++ b/t/t9001-send-email.sh @@ -2142,4 +2142,33 @@ test_expect_success $PREREQ 'test that send-email works outside a repo' ' "$(pwd)/0001-add-master.patch" ' +test_expect_success $PREREQ 'test that sendmail config is rejected' ' + test_config sendmail.program sendmail && + test_must_fail git send-email \ + --from="Example <nobody@example.com>" \ + --to=nobody@example.com \ + --smtp-server="$(pwd)/fake.sendmail" \ + HEAD^ 2>err && + test_i18ngrep "found configuration options for '"'"sendmail"'"'" err +' + +test_expect_success $PREREQ 'test that sendmail config rejection is specific' ' + test_config resendmail.program sendmail && + git send-email \ + --from="Example <nobody@example.com>" \ + --to=nobody@example.com \ + --smtp-server="$(pwd)/fake.sendmail" \ + HEAD^ +' + +test_expect_success $PREREQ 'test forbidSendmailVariables behavior override' ' + test_config sendmail.program sendmail && + test_config sendemail.forbidSendmailVariables false && + git send-email \ + --from="Example <nobody@example.com>" \ + --to=nobody@example.com \ + --smtp-server="$(pwd)/fake.sendmail" \ + HEAD^ +' + test_done diff --git a/t/test-lib-functions.sh b/t/test-lib-functions.sh index 596c549cdd..6a8e194a99 100644 --- a/t/test-lib-functions.sh +++ b/t/test-lib-functions.sh @@ -952,7 +952,13 @@ test_expect_code () { # - not all diff versions understand "-u" test_cmp() { - eval "$GIT_TEST_CMP" '"$@"' + test $# -eq 2 || BUG "test_cmp requires two arguments" + if ! eval "$GIT_TEST_CMP" '"$@"' + then + test "x$1" = x- || test -e "$1" || BUG "test_cmp '$1' missing" + test "x$2" = x- || test -e "$2" || BUG "test_cmp '$2' missing" + return 1 + fi } # Check that the given config key has the expected value. @@ -981,7 +987,13 @@ test_cmp_config() { # test_cmp_bin - helper to compare binary files test_cmp_bin() { - cmp "$@" + test $# -eq 2 || BUG "test_cmp_bin requires two arguments" + if ! cmp "$@" + then + test "x$1" = x- || test -e "$1" || BUG "test_cmp_bin '$1' missing" + test "x$2" = x- || test -e "$2" || BUG "test_cmp_bin '$2' missing" + return 1 + fi } # Use this instead of test_cmp to compare files that contain expected and diff --git a/transport.c b/transport.c index 2d4fd851dc..419be0b6ea 100644 --- a/transport.c +++ b/transport.c @@ -443,6 +443,7 @@ void transport_update_tracking_ref(struct remote *remote, struct ref *ref, int v if (ref->status != REF_STATUS_OK && ref->status != REF_STATUS_UPTODATE) return; + memset(&rs, 0, sizeof(rs)); rs.src = ref->name; rs.dst = NULL; diff --git a/upload-pack.c b/upload-pack.c index 80ad9a38d8..3b858eb457 100644 --- a/upload-pack.c +++ b/upload-pack.c @@ -603,9 +603,8 @@ static int do_reachable_revlist(struct child_process *cmd, "rev-list", "--stdin", NULL, }; struct object *o; - char namebuf[GIT_MAX_HEXSZ + 2]; /* ^ + hash + LF */ + FILE *cmd_in = NULL; int i; - const unsigned hexsz = the_hash_algo->hexsz; cmd->argv = argv; cmd->git_cmd = 1; @@ -623,8 +622,8 @@ static int do_reachable_revlist(struct child_process *cmd, if (start_command(cmd)) goto error; - namebuf[0] = '^'; - namebuf[hexsz + 1] = '\n'; + cmd_in = xfdopen(cmd->in, "w"); + for (i = get_max_object_index(); 0 < i; ) { o = get_indexed_object(--i); if (!o) @@ -633,11 +632,9 @@ static int do_reachable_revlist(struct child_process *cmd, o->flags &= ~TMP_MARK; if (!is_our_ref(o, allow_uor)) continue; - memcpy(namebuf + 1, oid_to_hex(&o->oid), hexsz); - if (write_in_full(cmd->in, namebuf, hexsz + 2) < 0) + if (fprintf(cmd_in, "^%s\n", oid_to_hex(&o->oid)) < 0) goto error; } - namebuf[hexsz] = '\n'; for (i = 0; i < src->nr; i++) { o = src->objects[i].item; if (is_our_ref(o, allow_uor)) { @@ -647,11 +644,12 @@ static int do_reachable_revlist(struct child_process *cmd, } if (reachable && o->type == OBJ_COMMIT) o->flags |= TMP_MARK; - memcpy(namebuf, oid_to_hex(&o->oid), hexsz); - if (write_in_full(cmd->in, namebuf, hexsz + 1) < 0) + if (fprintf(cmd_in, "%s\n", oid_to_hex(&o->oid)) < 0) goto error; } - close(cmd->in); + if (ferror(cmd_in) || fflush(cmd_in)) + goto error; + fclose(cmd_in); cmd->in = -1; sigchain_pop(SIGPIPE); @@ -660,8 +658,8 @@ static int do_reachable_revlist(struct child_process *cmd, error: sigchain_pop(SIGPIPE); - if (cmd->in >= 0) - close(cmd->in); + if (cmd_in) + fclose(cmd_in); if (cmd->out >= 0) close(cmd->out); return -1; @@ -739,7 +737,6 @@ static int has_unreachable(struct object_array *src, enum allow_uor allow_uor) return 0; error: - sigchain_pop(SIGPIPE); if (cmd.out >= 0) close(cmd.out); return 1; diff --git a/userdiff.c b/userdiff.c index 1df884ef0b..fde02f225b 100644 --- a/userdiff.c +++ b/userdiff.c @@ -46,10 +46,14 @@ PATTERNS("elixir", /* Not real operators, but should be grouped */ "|:?%[A-Za-z0-9_.]\\{\\}?"), IPATTERN("fortran", + /* Don't match comment lines */ "!^([C*]|[ \t]*!)\n" + /* Don't match 'module procedure' lines */ "!^[ \t]*MODULE[ \t]+PROCEDURE[ \t]\n" + /* Program, module, block data */ "^[ \t]*((END[ \t]+)?(PROGRAM|MODULE|BLOCK[ \t]+DATA" - "|([^'\" \t]+[ \t]+)*(SUBROUTINE|FUNCTION))[ \t]+[A-Z].*)$", + /* Subroutines and functions */ + "|([^!'\" \t]+[ \t]+)*(SUBROUTINE|FUNCTION))[ \t]+[A-Z].*)$", /* -- */ "[a-zA-Z][a-zA-Z0-9_]*" "|\\.([Ee][Qq]|[Nn][Ee]|[Gg][TtEe]|[Ll][TtEe]|[Tt][Rr][Uu][Ee]|[Ff][Aa][Ll][Ss][Ee]|[Aa][Nn][Dd]|[Oo][Rr]|[Nn]?[Ee][Qq][Vv]|[Nn][Oo][Tt])\\." diff --git a/wt-status.c b/wt-status.c index d75399085d..bb0f9120de 100644 --- a/wt-status.c +++ b/wt-status.c @@ -703,7 +703,7 @@ static void wt_status_collect_untracked(struct wt_status *s) if (!s->show_untracked_files) return; - memset(&dir, 0, sizeof(dir)); + dir_init(&dir); if (s->show_untracked_files != SHOW_ALL_UNTRACKED_FILES) dir.flags |= DIR_SHOW_OTHER_DIRECTORIES | DIR_HIDE_EMPTY_DIRECTORIES; @@ -724,19 +724,15 @@ static void wt_status_collect_untracked(struct wt_status *s) struct dir_entry *ent = dir.entries[i]; if (index_name_is_other(istate, ent->name, ent->len)) string_list_insert(&s->untracked, ent->name); - free(ent); } for (i = 0; i < dir.ignored_nr; i++) { struct dir_entry *ent = dir.ignored[i]; if (index_name_is_other(istate, ent->name, ent->len)) string_list_insert(&s->ignored, ent->name); - free(ent); } - free(dir.entries); - free(dir.ignored); - clear_directory(&dir); + dir_clear(&dir); if (advice_status_u_option) s->untracked_in_ms = (getnanotime() - t_begin) / 1000000; @@ -1672,13 +1668,13 @@ void wt_status_get_state(struct repository *r, state->merge_in_progress = 1; } else if (wt_status_check_rebase(NULL, state)) { ; /* all set */ - } else if (!stat(git_path_cherry_pick_head(r), &st) && - !get_oid("CHERRY_PICK_HEAD", &oid)) { + } else if (refs_ref_exists(get_main_ref_store(r), "CHERRY_PICK_HEAD") && + !get_oid("CHERRY_PICK_HEAD", &oid)) { state->cherry_pick_in_progress = 1; oidcpy(&state->cherry_pick_head_oid, &oid); } wt_status_check_bisect(NULL, state); - if (!stat(git_path_revert_head(r), &st) && + if (refs_ref_exists(get_main_ref_store(r), "REVERT_HEAD") && !get_oid("REVERT_HEAD", &oid)) { state->revert_in_progress = 1; oidcpy(&state->revert_head_oid, &oid); |