diff options
161 files changed, 2634 insertions, 760 deletions
@@ -220,6 +220,7 @@ Philipp A. Hartmann <pah@qo.cx> <ph@sorgh.de> Philippe Bruhat <book@cpan.org> Ralf Thielow <ralf.thielow@gmail.com> <ralf.thielow@googlemail.com> Ramsay Jones <ramsay@ramsayjones.plus.com> <ramsay@ramsay1.demon.co.uk> +Ramkumar Ramachandra <r@artagnon.com> <artagnon@gmail.com> Randall S. Becker <randall.becker@nexbridge.ca> <rsbecker@nexbridge.com> René Scharfe <l.s.r@web.de> <rene.scharfe@lsrfire.ath.cx> René Scharfe <l.s.r@web.de> Rene Scharfe diff --git a/Documentation/RelNotes/2.32.0.txt b/Documentation/RelNotes/2.32.0.txt new file mode 100644 index 0000000000..6de5c3dac8 --- /dev/null +++ b/Documentation/RelNotes/2.32.0.txt @@ -0,0 +1,99 @@ +Git 2.32 Release Notes +====================== + +Backward compatibility notes +---------------------------- + + * ".gitattributes", ".gitignore", and ".mailmap" files that are + symbolic links are ignored. + + +Updates since v2.32 +------------------- + +UI, Workflows & Features + + * It does not make sense to make ".gitattributes", ".gitignore" and + ".mailmap" symlinks, as they are supposed to be usable from the + object store (think: bare repositories where HEAD:.mailmap etc. are + used). When these files are symbolic links, we used to read the + contents of the files pointed by them by mistake, which has been + corrected. + + * "git stash show" learned to optionally show untracked part of the + stash. + + * "git log --format='...'" learned "%(describe)" placeholder. + + +Performance, Internal Implementation, Development Support etc. + + * Rename detection rework continues. + + +Fixes since v2.31 +----------------- + + * The fsmonitor interface read from its input without making sure + there is something to read from. This bug is new in 2.31 + timeframe. + (merge 097ea2c848 jh/fsmonitor-prework later to maint). + + * The data structure used by fsmonitor interface was not properly + duplicated during an in-core merge, leading to use-after-free etc. + (merge 4abc57848d js/fsmonitor-unpack-fix later to maint). + + * "git bisect" reimplemented more in C during 2.30 timeframe did not + take an annotated tag as a good/bad endpoint well. This regression + has been corrected. + (merge 7730f85594 jk/bisect-peel-tag-fix later to maint). + + * Fix macros that can silently inject unintended null-statements. + (merge 116affac3f rs/avoid-null-statement-after-macro-call later to maint). + + * CALLOC_ARRAY() macro replaces many uses of xcalloc(). + (merge 1c57cc70ec rs/calloc-array later to maint). + + * Update insn in Makefile comments to run fuzz-all target. + (merge 68b5c3aa48 ah/make-fuzz-all-doc-update later to maint). + + * Fix a corner case bug in "git mv" on case insensitive systems, + which was introduced in 2.29 timeframe. + (merge 93c3d297b5 tb/git-mv-icase-fix later to maint). + + * We had a code to diagnose and die cleanly when a required + clean/smudge filter is missing, but an assert before that + unnecessarily fired, hiding the end-user facing die() message. + (merge 6fab35f748 mt/cleanly-die-upon-missing-required-filter later to maint). + + * Update C code that sets a few configuration variables when a remote + is configured so that it spells configuration variable names in the + canonical camelCase. + (merge 0f1da600e6 ab/remote-write-config-in-camel-case later to maint). + + * A new configuration variable has been introduced to allow choosing + which version of the generation number gets used in the + commit-graph file. + (merge 702110aac6 ds/commit-graph-generation-config later to maint). + + * Perf test update to work better in secondary worktrees. + (merge 36e834abc1 jk/perf-in-worktrees later to maint). + + * Updates to memory allocation code around the use of pcre2 library. + (merge c1760352e0 ab/grep-pcre2-allocfix later to maint). + + * "git -c core.bare=false clone --bare ..." would have segfaulted, + which has been corrected. + (merge 75555676ad bc/clone-bare-with-conflicting-config later to maint). + + * Other code cleanup, docfix, build fix, etc. + (merge 486f4bd183 jc/calloc-fix later to maint). + (merge 5f70859c15 jt/clone-unborn-head later to maint). + (merge cfd409ed09 km/config-doc-typofix later to maint). + (merge 8588aa8657 jk/slimmed-down later to maint). + (merge 241b5d3ebe rs/xcalloc-takes-nelem-first later to maint). + (merge f451960708 dl/cat-file-doc-cleanup later to maint). + (merge 12604a8d0c sv/t9801-test-path-is-file-cleanup later to maint). + (merge ea7e63921c jr/doc-ignore-typofix later to maint). + (merge 23c781f173 ps/update-ref-trans-hook-doc later to maint). + (merge 42efa1231a jk/filter-branch-sha256 later to maint). diff --git a/Documentation/config.txt b/Documentation/config.txt index d08e83a148..bf82766a6a 100644 --- a/Documentation/config.txt +++ b/Documentation/config.txt @@ -46,7 +46,7 @@ Subsection names are case sensitive and can contain any characters except newline and the null byte. Doublequote `"` and backslash can be included by escaping them as `\"` and `\\`, respectively. Backslashes preceding other characters are dropped when reading; for example, `\t` is read as -`t` and `\0` is read as `0` Section headers cannot span multiple lines. +`t` and `\0` is read as `0`. Section headers cannot span multiple lines. Variables may belong directly to a section or to a given subsection. You can have `[section]` if you have `[section "subsection"]`, but you don't need to. diff --git a/Documentation/config/commitgraph.txt b/Documentation/config/commitgraph.txt index 4582c39fc4..30604e4a4c 100644 --- a/Documentation/config/commitgraph.txt +++ b/Documentation/config/commitgraph.txt @@ -1,3 +1,9 @@ +commitGraph.generationVersion:: + Specifies the type of generation number version to use when writing + or reading the commit-graph file. If version 1 is specified, then + the corrected commit dates will not be written or read. Defaults to + 2. + commitGraph.maxNewFilters:: Specifies the default value for the `--max-new-filters` option of `git commit-graph write` (c.f., linkgit:git-commit-graph[1]). diff --git a/Documentation/config/stash.txt b/Documentation/config/stash.txt index 00eb35434e..413f907cba 100644 --- a/Documentation/config/stash.txt +++ b/Documentation/config/stash.txt @@ -5,6 +5,11 @@ stash.useBuiltin:: is always used. Setting this will emit a warning, to alert any remaining users that setting this now does nothing. +stash.showIncludeUntracked:: + If this is set to true, the `git stash show` command without an + option will show the untracked files of a stash entry. Defaults to + false. See description of 'show' command in linkgit:git-stash[1]. + stash.showPatch:: If this is set to true, the `git stash show` command without an option will show the stash entry in patch form. Defaults to false. diff --git a/Documentation/git-cat-file.txt b/Documentation/git-cat-file.txt index 8e192d87db..4eb0421b3f 100644 --- a/Documentation/git-cat-file.txt +++ b/Documentation/git-cat-file.txt @@ -35,42 +35,42 @@ OPTIONS -t:: Instead of the content, show the object type identified by - <object>. + `<object>`. -s:: Instead of the content, show the object size identified by - <object>. + `<object>`. -e:: - Exit with zero status if <object> exists and is a valid - object. If <object> is of an invalid format exit with non-zero and + Exit with zero status if `<object>` exists and is a valid + object. If `<object>` is of an invalid format exit with non-zero and emits an error on stderr. -p:: - Pretty-print the contents of <object> based on its type. + Pretty-print the contents of `<object>` based on its type. <type>:: - Typically this matches the real type of <object> but asking + Typically this matches the real type of `<object>` but asking for a type that can trivially be dereferenced from the given - <object> is also permitted. An example is to ask for a - "tree" with <object> being a commit object that contains it, - or to ask for a "blob" with <object> being a tag object that + `<object>` is also permitted. An example is to ask for a + "tree" with `<object>` being a commit object that contains it, + or to ask for a "blob" with `<object>` being a tag object that points at it. --textconv:: Show the content as transformed by a textconv filter. In this case, - <object> has to be of the form <tree-ish>:<path>, or :<path> in + `<object>` has to be of the form `<tree-ish>:<path>`, or `:<path>` in order to apply the filter to the content recorded in the index at - <path>. + `<path>`. --filters:: Show the content as converted by the filters configured in - the current working tree for the given <path> (i.e. smudge filters, - end-of-line conversion, etc). In this case, <object> has to be of - the form <tree-ish>:<path>, or :<path>. + the current working tree for the given `<path>` (i.e. smudge filters, + end-of-line conversion, etc). In this case, `<object>` has to be of + the form `<tree-ish>:<path>`, or `:<path>`. --path=<path>:: - For use with --textconv or --filters, to allow specifying an object + For use with `--textconv` or `--filters`, to allow specifying an object name and a path separately, e.g. when it is difficult to figure out the revision from which the blob came. @@ -115,15 +115,15 @@ OPTIONS repository. --allow-unknown-type:: - Allow -s or -t to query broken/corrupt objects of unknown type. + Allow `-s` or `-t` to query broken/corrupt objects of unknown type. --follow-symlinks:: - With --batch or --batch-check, follow symlinks inside the + With `--batch` or `--batch-check`, follow symlinks inside the repository when requesting objects with extended SHA-1 expressions of the form tree-ish:path-in-tree. Instead of providing output about the link itself, provide output about the linked-to object. If a symlink points outside the - tree-ish (e.g. a link to /foo or a root-level link to ../foo), + tree-ish (e.g. a link to `/foo` or a root-level link to `../foo`), the portion of the link which is outside the tree will be printed. + @@ -175,15 +175,15 @@ respectively print: OUTPUT ------ -If `-t` is specified, one of the <type>. +If `-t` is specified, one of the `<type>`. -If `-s` is specified, the size of the <object> in bytes. +If `-s` is specified, the size of the `<object>` in bytes. -If `-e` is specified, no output, unless the <object> is malformed. +If `-e` is specified, no output, unless the `<object>` is malformed. -If `-p` is specified, the contents of <object> are pretty-printed. +If `-p` is specified, the contents of `<object>` are pretty-printed. -If <type> is specified, the raw (though uncompressed) contents of the <object> +If `<type>` is specified, the raw (though uncompressed) contents of the `<object>` will be returned. BATCH OUTPUT @@ -200,7 +200,7 @@ object, with placeholders of the form `%(atom)` expanded, followed by a newline. The available atoms are: `objectname`:: - The 40-hex object name of the object. + The full hex representation of the object name. `objecttype`:: The type of the object (the same as `cat-file -t` reports). @@ -215,8 +215,9 @@ newline. The available atoms are: `deltabase`:: If the object is stored as a delta on-disk, this expands to the - 40-hex sha1 of the delta base object. Otherwise, expands to the - null sha1 (40 zeroes). See `CAVEATS` below. + full hex representation of the delta base object name. + Otherwise, expands to the null OID (all zeroes). See `CAVEATS` + below. `rest`:: If this atom is used in the output string, input lines are split @@ -235,14 +236,14 @@ newline. For example, `--batch` without a custom format would produce: ------------ -<sha1> SP <type> SP <size> LF +<oid> SP <type> SP <size> LF <contents> LF ------------ Whereas `--batch-check='%(objectname) %(objecttype)'` would produce: ------------ -<sha1> SP <type> LF +<oid> SP <type> LF ------------ If a name is specified on stdin that cannot be resolved to an object in @@ -258,7 +259,7 @@ If a name is specified that might refer to more than one object (an ambiguous sh <object> SP ambiguous LF ------------ -If --follow-symlinks is used, and a symlink in the repository points +If `--follow-symlinks` is used, and a symlink in the repository points outside the repository, then `cat-file` will ignore any custom format and print: @@ -267,11 +268,11 @@ symlink SP <size> LF <symlink> LF ------------ -The symlink will either be absolute (beginning with a /), or relative -to the tree root. For instance, if dir/link points to ../../foo, then -<symlink> will be ../foo. <size> is the size of the symlink in bytes. +The symlink will either be absolute (beginning with a `/`), or relative +to the tree root. For instance, if dir/link points to `../../foo`, then +`<symlink>` will be `../foo`. `<size>` is the size of the symlink in bytes. -If --follow-symlinks is used, the following error messages will be +If `--follow-symlinks` is used, the following error messages will be displayed: ------------ diff --git a/Documentation/git-pack-objects.txt b/Documentation/git-pack-objects.txt index f85cb7ea93..25d9fbe37a 100644 --- a/Documentation/git-pack-objects.txt +++ b/Documentation/git-pack-objects.txt @@ -85,6 +85,16 @@ base-name:: reference was included in the resulting packfile. This can be useful to send new tags to native Git clients. +--stdin-packs:: + Read the basenames of packfiles (e.g., `pack-1234abcd.pack`) + from the standard input, instead of object names or revision + arguments. The resulting pack contains all objects listed in the + included packs (those not beginning with `^`), excluding any + objects listed in the excluded packs (beginning with `^`). ++ +Incompatible with `--revs`, or options that imply `--revs` (such as +`--all`), with the exception of `--unpacked`, which is compatible. + --window=<n>:: --depth=<n>:: These two options affect how the objects contained in diff --git a/Documentation/git-push.txt b/Documentation/git-push.txt index ab103c82cf..a953c7c387 100644 --- a/Documentation/git-push.txt +++ b/Documentation/git-push.txt @@ -600,7 +600,7 @@ EXAMPLES `git push origin`:: Without additional configuration, pushes the current branch to - the configured upstream (`remote.origin.merge` configuration + the configured upstream (`branch.<name>.merge` configuration variable) if it has the same name as the current branch, and errors out without pushing otherwise. + diff --git a/Documentation/git-repack.txt b/Documentation/git-repack.txt index fbd4b4ae06..317d63cf0d 100644 --- a/Documentation/git-repack.txt +++ b/Documentation/git-repack.txt @@ -165,6 +165,29 @@ depth is 4095. Pass the `--delta-islands` option to `git-pack-objects`, see linkgit:git-pack-objects[1]. +-g=<factor>:: +--geometric=<factor>:: + Arrange resulting pack structure so that each successive pack + contains at least `<factor>` times the number of objects as the + next-largest pack. ++ +`git repack` ensures this by determining a "cut" of packfiles that need +to be repacked into one in order to ensure a geometric progression. It +picks the smallest set of packfiles such that as many of the larger +packfiles (by count of objects contained in that pack) may be left +intact. ++ +Unlike other repack modes, the set of objects to pack is determined +uniquely by the set of packs being "rolled-up"; in other words, the +packs determined to need to be combined in order to restore a geometric +progression. ++ +When `--unpacked` is specified, loose objects are implicitly included in +this "roll-up", without respect to their reachability. This is subject +to change in the future. This option (implying a drastically different +repack mode) is not guaranteed to work with all other combinations of +option to `git repack`). + CONFIGURATION ------------- diff --git a/Documentation/git-stash.txt b/Documentation/git-stash.txt index f1197d641b..a8c8c32f1e 100644 --- a/Documentation/git-stash.txt +++ b/Documentation/git-stash.txt @@ -9,7 +9,7 @@ SYNOPSIS -------- [verse] 'git stash' list [<log-options>] -'git stash' show [<diff-options>] [<stash>] +'git stash' show [-u|--include-untracked|--only-untracked] [<diff-options>] [<stash>] 'git stash' drop [-q|--quiet] [<stash>] 'git stash' ( pop | apply ) [--index] [-q|--quiet] [<stash>] 'git stash' branch <branchname> [<stash>] @@ -83,7 +83,7 @@ stash@{1}: On master: 9cc0589... Add git-stash The command takes options applicable to the 'git log' command to control what is shown and how. See linkgit:git-log[1]. -show [<diff-options>] [<stash>]:: +show [-u|--include-untracked|--only-untracked] [<diff-options>] [<stash>]:: Show the changes recorded in the stash entry as a diff between the stashed contents and the commit back when the stash entry was first @@ -91,8 +91,8 @@ show [<diff-options>] [<stash>]:: By default, the command shows the diffstat, but it will accept any format known to 'git diff' (e.g., `git stash show -p stash@{1}` to view the second most recent entry in patch form). - You can use stash.showStat and/or stash.showPatch config variables - to change the default behavior. + You can use stash.showIncludeUntracked, stash.showStat, and + stash.showPatch config variables to change the default behavior. pop [--index] [-q|--quiet] [<stash>]:: @@ -160,10 +160,18 @@ up with `git clean`. -u:: --include-untracked:: - This option is only valid for `push` and `save` commands. +--no-include-untracked:: + When used with the `push` and `save` commands, + all untracked files are also stashed and then cleaned up with + `git clean`. ++ +When used with the `show` command, show the untracked files in the stash +entry as part of the diff. + +--only-untracked:: + This option is only valid for the `show` command. + -All untracked files are also stashed and then cleaned up with -`git clean`. +Show only the untracked files in the stash entry as part of the diff. --index:: This option is only valid for `pop` and `apply` commands. diff --git a/Documentation/gitattributes.txt b/Documentation/gitattributes.txt index e84e104f93..0a60472bb5 100644 --- a/Documentation/gitattributes.txt +++ b/Documentation/gitattributes.txt @@ -1174,7 +1174,8 @@ tag then no replacement will be done. The placeholders are the same as those for the option `--pretty=format:` of linkgit:git-log[1], except that they need to be wrapped like this: `$Format:PLACEHOLDERS$` in the file. E.g. the string `$Format:%H$` will be replaced by the -commit hash. +commit hash. However, only one `%(describe)` placeholder is expanded +per archive to avoid denial-of-service attacks. Packing objects diff --git a/Documentation/gitdiffcore.txt b/Documentation/gitdiffcore.txt index 1c7269655f..0d57f86abc 100644 --- a/Documentation/gitdiffcore.txt +++ b/Documentation/gitdiffcore.txt @@ -187,7 +187,7 @@ mark a file pair as a rename and stop considering other candidates for better matches. At most, one comparison is done per file in this preliminary pass; so if there are several remaining ext.txt files throughout the directory hierarchy after exact rename detection, this -preliminary step will be skipped for those files. +preliminary step may be skipped for those files. Note. When the "-C" option is used with `--find-copies-harder` option, 'git diff-{asterisk}' commands feed unmodified filepairs to diff --git a/Documentation/githooks.txt b/Documentation/githooks.txt index 1f3b57d04d..b51959ff94 100644 --- a/Documentation/githooks.txt +++ b/Documentation/githooks.txt @@ -138,7 +138,7 @@ given); `template` (if a `-t` option was given or the configuration option `commit.template` is set); `merge` (if the commit is a merge or a `.git/MERGE_MSG` file exists); `squash` (if a `.git/SQUASH_MSG` file exists); or `commit`, followed by -a commit SHA-1 (if a `-c`, `-C` or `--amend` option was given). +a commit object name (if a `-c`, `-C` or `--amend` option was given). If the exit status is non-zero, `git commit` will abort. @@ -231,19 +231,19 @@ named remote is not being used both values will be the same. Information about what is to be pushed is provided on the hook's standard input with lines of the form: - <local ref> SP <local sha1> SP <remote ref> SP <remote sha1> LF + <local ref> SP <local object name> SP <remote ref> SP <remote object name> LF For instance, if the command +git push origin master:foreign+ were run the hook would receive a line like the following: refs/heads/master 67890 refs/heads/foreign 12345 -although the full, 40-character SHA-1s would be supplied. If the foreign ref -does not yet exist the `<remote SHA-1>` will be 40 `0`. If a ref is to be -deleted, the `<local ref>` will be supplied as `(delete)` and the `<local -SHA-1>` will be 40 `0`. If the local commit was specified by something other -than a name which could be expanded (such as `HEAD~`, or a SHA-1) it will be -supplied as it was originally given. +although the full object name would be supplied. If the foreign ref does not +yet exist the `<remote object name>` will be the all-zeroes object name. If a +ref is to be deleted, the `<local ref>` will be supplied as `(delete)` and the +`<local object name>` will be the all-zeroes object name. If the local commit +was specified by something other than a name which could be expanded (such as +`HEAD~`, or an object name) it will be supplied as it was originally given. If this hook exits with a non-zero status, `git push` will abort without pushing anything. Information about why the push is rejected may be sent @@ -268,7 +268,7 @@ input a line of the format: where `<old-value>` is the old object name stored in the ref, `<new-value>` is the new object name to be stored in the ref and `<ref-name>` is the full name of the ref. -When creating a new ref, `<old-value>` is 40 `0`. +When creating a new ref, `<old-value>` is the all-zeroes object name. If the hook exits with non-zero status, none of the refs will be updated. If the hook exits with zero, updating of individual refs can @@ -473,7 +473,8 @@ reference-transaction This hook is invoked by any Git command that performs reference updates. It executes whenever a reference transaction is prepared, -committed or aborted and may thus get called multiple times. +committed or aborted and may thus get called multiple times. The hook +does not cover symbolic references (but that may change in the future). The hook takes exactly one argument, which is the current state the given reference transaction is in: @@ -492,6 +493,14 @@ receives on standard input a line of the format: <old-value> SP <new-value> SP <ref-name> LF +where `<old-value>` is the old object name passed into the reference +transaction, `<new-value>` is the new object name to be stored in the +ref and `<ref-name>` is the full name of the ref. When force updating +the reference regardless of its current value or when the reference is +to be created anew, `<old-value>` is the all-zeroes object name. To +distinguish these cases, you can inspect the current value of +`<ref-name>` via `git rev-parse`. + The exit status of the hook is ignored for any state except for the "prepared" state. In the "prepared" state, a non-zero exit status will cause the transaction to be aborted. The hook will not be called with @@ -550,7 +559,7 @@ command-dependent arguments may be passed in the future. The hook receives a list of the rewritten commits on stdin, in the format - <old-sha1> SP <new-sha1> [ SP <extra-info> ] LF + <old-object-name> SP <new-object-name> [ SP <extra-info> ] LF The 'extra-info' is again command-dependent. If it is empty, the preceding SP is also omitted. Currently, no commands pass any @@ -566,7 +575,7 @@ rebase:: For the 'squash' and 'fixup' operation, all commits that were squashed are listed as being rewritten to the squashed commit. This means that there will be several lines sharing the same - 'new-sha1'. + 'new-object-name'. + The commits are guaranteed to be listed in the order that they were processed by rebase. diff --git a/Documentation/gitignore.txt b/Documentation/gitignore.txt index d47b1ae296..5751603b13 100644 --- a/Documentation/gitignore.txt +++ b/Documentation/gitignore.txt @@ -153,7 +153,7 @@ EXAMPLES -------- - The pattern `hello.*` matches any file or folder - whose name begins with `hello`. If one wants to restrict + whose name begins with `hello.`. If one wants to restrict this only to the directory and not in its subdirectories, one can prepend the pattern with a slash, i.e. `/hello.*`; the pattern now matches `hello.txt`, `hello.c` but not diff --git a/Documentation/pretty-formats.txt b/Documentation/pretty-formats.txt index 6b59e28d44..45133066e4 100644 --- a/Documentation/pretty-formats.txt +++ b/Documentation/pretty-formats.txt @@ -208,6 +208,19 @@ The placeholders are: '%cs':: committer date, short format (`YYYY-MM-DD`) '%d':: ref names, like the --decorate option of linkgit:git-log[1] '%D':: ref names without the " (", ")" wrapping. +'%(describe[:options])':: human-readable name, like + linkgit:git-describe[1]; empty string for + undescribable commits. The `describe` string + may be followed by a colon and zero or more + comma-separated options. Descriptions can be + inconsistent when tags are added or removed at + the same time. ++ +** 'match=<pattern>': Only consider tags matching the given + `glob(7)` pattern, excluding the "refs/tags/" prefix. +** 'exclude=<pattern>': Do not consider tags matching the given + `glob(7)` pattern, excluding the "refs/tags/" prefix. + '%S':: ref name given on the command line by which the commit was reached (like `git log --source`), only works with `git log` '%e':: encoding diff --git a/GIT-VERSION-GEN b/GIT-VERSION-GEN index 8f6334d4e4..d2ff91775e 100755 --- a/GIT-VERSION-GEN +++ b/GIT-VERSION-GEN @@ -1,7 +1,7 @@ #!/bin/sh GVF=GIT-VERSION-FILE -DEF_VER=v2.31.0 +DEF_VER=v2.31.GIT LF=' ' @@ -3299,11 +3299,11 @@ cover_db_html: cover_db # are not necessarily appropriate for general builds, and that vary greatly # depending on the compiler version used. # -# An example command to build against libFuzzer from LLVM 4.0.0: +# An example command to build against libFuzzer from LLVM 11.0.0: # # make CC=clang CXX=clang++ \ -# CFLAGS="-fsanitize-coverage=trace-pc-guard -fsanitize=address" \ -# LIB_FUZZING_ENGINE=/usr/lib/llvm-4.0/lib/libFuzzer.a \ +# CFLAGS="-fsanitize=fuzzer-no-link,address" \ +# LIB_FUZZING_ENGINE="-fsanitize=fuzzer" \ # fuzz-all # FUZZ_CXXFLAGS ?= $(CFLAGS) @@ -1 +1 @@ -Documentation/RelNotes/2.31.0.txt
\ No newline at end of file +Documentation/RelNotes/2.32.0.txt
\ No newline at end of file diff --git a/add-interactive.c b/add-interactive.c index 9b8cdb4a31..36ebdbdf7e 100644 --- a/add-interactive.c +++ b/add-interactive.c @@ -413,7 +413,7 @@ struct file_item { static void add_file_item(struct string_list *files, const char *name) { - struct file_item *item = xcalloc(sizeof(*item), 1); + struct file_item *item = xcalloc(1, sizeof(*item)); string_list_append(files, name)->util = item; } @@ -476,7 +476,7 @@ static void collect_changes_cb(struct diff_queue_struct *q, add_file_item(s->files, name); - entry = xcalloc(sizeof(*entry), 1); + CALLOC_ARRAY(entry, 1); hashmap_entry_init(&entry->ent, hash); entry->name = s->files->items[s->files->nr - 1].string; entry->item = s->files->items[s->files->nr - 1].util; @@ -1120,7 +1120,7 @@ int run_add_i(struct repository *r, const struct pathspec *ps) int res = 0; for (i = 0; i < ARRAY_SIZE(command_list); i++) { - struct command_item *util = xcalloc(sizeof(*util), 1); + struct command_item *util = xcalloc(1, sizeof(*util)); util->command = command_list[i].command; string_list_append(&commands.items, command_list[i].string) ->util = util; @@ -1781,7 +1781,7 @@ static int parse_single_patch(struct apply_state *state, struct fragment *fragment; int len; - fragment = xcalloc(1, sizeof(*fragment)); + CALLOC_ARRAY(fragment, 1); fragment->linenr = state->linenr; len = parse_fragment(state, line, size, patch, fragment); if (len <= 0) { @@ -1959,7 +1959,7 @@ static struct fragment *parse_binary_hunk(struct apply_state *state, size -= llen; } - frag = xcalloc(1, sizeof(*frag)); + CALLOC_ARRAY(frag, 1); frag->patch = inflate_it(data, hunk_size, origlen); frag->free_patch = 1; if (!frag->patch) @@ -4681,7 +4681,7 @@ static int apply_patch(struct apply_state *state, struct patch *patch; int nr; - patch = xcalloc(1, sizeof(*patch)); + CALLOC_ARRAY(patch, 1); patch->inaccurate_eof = !!(options & APPLY_OPT_INACCURATE_EOF); patch->recount = !!(options & APPLY_OPT_RECOUNT); nr = parse_chunk(state, buf.buf + offset, buf.len - offset, patch); diff --git a/archive-tar.c b/archive-tar.c index a971fdc0f6..05d2455870 100644 --- a/archive-tar.c +++ b/archive-tar.c @@ -371,7 +371,7 @@ static int tar_filter_config(const char *var, const char *value, void *data) ar = find_tar_filter(name, namelen); if (!ar) { - ar = xcalloc(1, sizeof(*ar)); + CALLOC_ARRAY(ar, 1); ar->name = xmemdupz(name, namelen); ar->write_archive = write_tar_filter_archive; ar->flags = ARCHIVER_WANT_COMPRESSION_LEVELS | @@ -37,13 +37,10 @@ void init_archivers(void) static void format_subst(const struct commit *commit, const char *src, size_t len, - struct strbuf *buf) + struct strbuf *buf, struct pretty_print_context *ctx) { char *to_free = NULL; struct strbuf fmt = STRBUF_INIT; - struct pretty_print_context ctx = {0}; - ctx.date_mode.type = DATE_NORMAL; - ctx.abbrev = DEFAULT_ABBREV; if (src == buf->buf) to_free = strbuf_detach(buf, NULL); @@ -61,7 +58,7 @@ static void format_subst(const struct commit *commit, strbuf_add(&fmt, b + 8, c - b - 8); strbuf_add(buf, src, b - src); - format_commit_message(commit, fmt.buf, buf, &ctx); + format_commit_message(commit, fmt.buf, buf, ctx); len -= c + 1 - src; src = c + 1; } @@ -94,7 +91,7 @@ static void *object_file_to_archive(const struct archiver_args *args, strbuf_attach(&buf, buffer, *sizep, *sizep + 1); convert_to_working_tree(args->repo->index, path, buf.buf, buf.len, &buf, &meta); if (commit) - format_subst(commit, buf.buf, buf.len, &buf); + format_subst(commit, buf.buf, buf.len, &buf, args->pretty_ctx); buffer = strbuf_detach(&buf, &size); *sizep = size; } @@ -633,12 +630,19 @@ int write_archive(int argc, const char **argv, const char *prefix, const char *name_hint, int remote) { const struct archiver *ar = NULL; + struct pretty_print_describe_status describe_status = {0}; + struct pretty_print_context ctx = {0}; struct archiver_args args; int rc; git_config_get_bool("uploadarchive.allowunreachable", &remote_allow_unreachable); git_config(git_default_config, NULL); + describe_status.max_invocations = 1; + ctx.date_mode.type = DATE_NORMAL; + ctx.abbrev = DEFAULT_ABBREV; + ctx.describe_status = &describe_status; + args.pretty_ctx = &ctx; args.repo = repo; args.prefix = prefix; string_list_init(&args.extra_files, 1); @@ -5,6 +5,7 @@ #include "pathspec.h" struct repository; +struct pretty_print_context; struct archiver_args { struct repository *repo; @@ -22,6 +23,7 @@ struct archiver_args { unsigned int convert : 1; int compression_level; struct string_list extra_files; + struct pretty_print_context *pretty_ctx; }; /* main api */ @@ -278,6 +278,10 @@ struct match_attr { static const char blank[] = " \t\r\n"; +/* Flags usable in read_attr() and parse_attr_line() family of functions. */ +#define READ_ATTR_MACRO_OK (1<<0) +#define READ_ATTR_NOFOLLOW (1<<1) + /* * Parse a whitespace-delimited attribute state (i.e., "attr", * "-attr", "!attr", or "attr=value") from the string starting at src. @@ -331,7 +335,7 @@ static const char *parse_attr(const char *src, int lineno, const char *cp, } static struct match_attr *parse_attr_line(const char *line, const char *src, - int lineno, int macro_ok) + int lineno, unsigned flags) { int namelen; int num_attr, i; @@ -355,7 +359,7 @@ static struct match_attr *parse_attr_line(const char *line, const char *src, if (strlen(ATTRIBUTE_MACRO_PREFIX) < namelen && starts_with(name, ATTRIBUTE_MACRO_PREFIX)) { - if (!macro_ok) { + if (!(flags & READ_ATTR_MACRO_OK)) { fprintf_ln(stderr, _("%s not allowed: %s:%d"), name, src, lineno); goto fail_return; @@ -569,7 +573,7 @@ struct attr_check *attr_check_initl(const char *one, ...) check = attr_check_alloc(); check->nr = cnt; check->alloc = cnt; - check->items = xcalloc(cnt, sizeof(struct attr_check_item)); + CALLOC_ARRAY(check->items, cnt); check->items[0].attr = git_attr(one); va_start(params, one); @@ -653,11 +657,11 @@ static void handle_attr_line(struct attr_stack *res, const char *line, const char *src, int lineno, - int macro_ok) + unsigned flags) { struct match_attr *a; - a = parse_attr_line(line, src, lineno, macro_ok); + a = parse_attr_line(line, src, lineno, flags); if (!a) return; ALLOC_GROW(res->attrs, res->num_matches + 1, res->alloc); @@ -670,9 +674,10 @@ static struct attr_stack *read_attr_from_array(const char **list) const char *line; int lineno = 0; - res = xcalloc(1, sizeof(*res)); + CALLOC_ARRAY(res, 1); while ((line = *(list++)) != NULL) - handle_attr_line(res, line, "[builtin]", ++lineno, 1); + handle_attr_line(res, line, "[builtin]", ++lineno, + READ_ATTR_MACRO_OK); return res; } @@ -698,21 +703,31 @@ void git_attr_set_direction(enum git_attr_direction new_direction) direction = new_direction; } -static struct attr_stack *read_attr_from_file(const char *path, int macro_ok) +static struct attr_stack *read_attr_from_file(const char *path, unsigned flags) { - FILE *fp = fopen_or_warn(path, "r"); + int fd; + FILE *fp; struct attr_stack *res; char buf[2048]; int lineno = 0; - if (!fp) + if (flags & READ_ATTR_NOFOLLOW) + fd = open_nofollow(path, O_RDONLY); + else + fd = open(path, O_RDONLY); + + if (fd < 0) { + warn_on_fopen_errors(path); return NULL; - res = xcalloc(1, sizeof(*res)); + } + fp = xfdopen(fd, "r"); + + CALLOC_ARRAY(res, 1); while (fgets(buf, sizeof(buf), fp)) { char *bufp = buf; if (!lineno) skip_utf8_bom(&bufp, strlen(bufp)); - handle_attr_line(res, bufp, path, ++lineno, macro_ok); + handle_attr_line(res, bufp, path, ++lineno, flags); } fclose(fp); return res; @@ -720,7 +735,7 @@ static struct attr_stack *read_attr_from_file(const char *path, int macro_ok) static struct attr_stack *read_attr_from_index(const struct index_state *istate, const char *path, - int macro_ok) + unsigned flags) { struct attr_stack *res; char *buf, *sp; @@ -733,7 +748,7 @@ static struct attr_stack *read_attr_from_index(const struct index_state *istate, if (!buf) return NULL; - res = xcalloc(1, sizeof(*res)); + CALLOC_ARRAY(res, 1); for (sp = buf; *sp; ) { char *ep; int more; @@ -741,7 +756,7 @@ static struct attr_stack *read_attr_from_index(const struct index_state *istate, ep = strchrnul(sp, '\n'); more = (*ep == '\n'); *ep = '\0'; - handle_attr_line(res, sp, path, ++lineno, macro_ok); + handle_attr_line(res, sp, path, ++lineno, flags); sp = ep + more; } free(buf); @@ -749,19 +764,19 @@ static struct attr_stack *read_attr_from_index(const struct index_state *istate, } static struct attr_stack *read_attr(const struct index_state *istate, - const char *path, int macro_ok) + const char *path, unsigned flags) { struct attr_stack *res = NULL; if (direction == GIT_ATTR_INDEX) { - res = read_attr_from_index(istate, path, macro_ok); + res = read_attr_from_index(istate, path, flags); } else if (!is_bare_repository()) { if (direction == GIT_ATTR_CHECKOUT) { - res = read_attr_from_index(istate, path, macro_ok); + res = read_attr_from_index(istate, path, flags); if (!res) - res = read_attr_from_file(path, macro_ok); + res = read_attr_from_file(path, flags); } else if (direction == GIT_ATTR_CHECKIN) { - res = read_attr_from_file(path, macro_ok); + res = read_attr_from_file(path, flags); if (!res) /* * There is no checked out .gitattributes file @@ -769,12 +784,12 @@ static struct attr_stack *read_attr(const struct index_state *istate, * We allow operation in a sparsely checked out * work tree, so read from it. */ - res = read_attr_from_index(istate, path, macro_ok); + res = read_attr_from_index(istate, path, flags); } } if (!res) - res = xcalloc(1, sizeof(*res)); + CALLOC_ARRAY(res, 1); return res; } @@ -844,6 +859,7 @@ static void bootstrap_attr_stack(const struct index_state *istate, struct attr_stack **stack) { struct attr_stack *e; + unsigned flags = READ_ATTR_MACRO_OK; if (*stack) return; @@ -854,27 +870,27 @@ static void bootstrap_attr_stack(const struct index_state *istate, /* system-wide frame */ if (git_attr_system()) { - e = read_attr_from_file(git_etc_gitattributes(), 1); + e = read_attr_from_file(git_etc_gitattributes(), flags); push_stack(stack, e, NULL, 0); } /* home directory */ if (get_home_gitattributes()) { - e = read_attr_from_file(get_home_gitattributes(), 1); + e = read_attr_from_file(get_home_gitattributes(), flags); push_stack(stack, e, NULL, 0); } /* root directory */ - e = read_attr(istate, GITATTRIBUTES_FILE, 1); + e = read_attr(istate, GITATTRIBUTES_FILE, flags | READ_ATTR_NOFOLLOW); push_stack(stack, e, xstrdup(""), 0); /* info frame */ if (startup_info->have_repository) - e = read_attr_from_file(git_path_info_attributes(), 1); + e = read_attr_from_file(git_path_info_attributes(), flags); else e = NULL; if (!e) - e = xcalloc(1, sizeof(struct attr_stack)); + CALLOC_ARRAY(e, 1); push_stack(stack, e, NULL, 0); } @@ -956,7 +972,7 @@ static void prepare_attr_stack(const struct index_state *istate, strbuf_add(&pathbuf, path + pathbuf.len, (len - pathbuf.len)); strbuf_addf(&pathbuf, "/%s", GITATTRIBUTES_FILE); - next = read_attr(istate, pathbuf.buf, 0); + next = read_attr(istate, pathbuf.buf, READ_ATTR_NOFOLLOW); /* reset the pathbuf to not include "/.gitattributes" */ strbuf_setlen(&pathbuf, len); @@ -423,7 +423,7 @@ void find_bisection(struct commit_list **commit_list, int *reaches, show_list("bisection 2 sorted", 0, nr, list); *all = nr; - weights = xcalloc(on_list, sizeof(*weights)); + CALLOC_ARRAY(weights, on_list); /* Do the real work of finding bisection commit. */ best = do_find_bisection(list, nr, weights, bisect_flags); @@ -951,13 +951,13 @@ static int *fuzzy_find_matching_lines(struct blame_origin *parent, max_search_distance_b = ((2 * max_search_distance_a + 1) * length_b - 1) / length_a; - result = xcalloc(sizeof(int), length_b); - second_best_result = xcalloc(sizeof(int), length_b); - certainties = xcalloc(sizeof(int), length_b); + CALLOC_ARRAY(result, length_b); + CALLOC_ARRAY(second_best_result, length_b); + CALLOC_ARRAY(certainties, length_b); /* See get_similarity() for details of similarities. */ similarity_count = length_b * (max_search_distance_a * 2 + 1); - similarities = xcalloc(sizeof(int), similarity_count); + CALLOC_ARRAY(similarities, similarity_count); for (i = 0; i < length_b; ++i) { result[i] = -1; @@ -995,7 +995,7 @@ static void fill_origin_fingerprints(struct blame_origin *o) return; o->num_lines = find_line_starts(&line_starts, o->file.ptr, o->file.size); - o->fingerprints = xcalloc(sizeof(struct fingerprint), o->num_lines); + CALLOC_ARRAY(o->fingerprints, o->num_lines); get_line_fingerprints(o->fingerprints, o->file.ptr, line_starts, 0, o->num_lines); free(line_starts); @@ -1853,8 +1853,7 @@ static void blame_chunk(struct blame_entry ***dstq, struct blame_entry ***srcq, diffp = NULL; if (ignore_diffs && same - tlno > 0) { - line_blames = xcalloc(sizeof(struct blame_line_tracker), - same - tlno); + CALLOC_ARRAY(line_blames, same - tlno); guess_line_blames(parent, target, tlno, offset, same, parent_len, line_blames); } @@ -2216,7 +2215,7 @@ static struct blame_list *setup_blame_list(struct blame_entry *unblamed, for (e = unblamed, num_ents = 0; e; e = e->next) num_ents++; if (num_ents) { - blame_list = xcalloc(num_ents, sizeof(struct blame_list)); + CALLOC_ARRAY(blame_list, num_ents); for (e = unblamed, i = 0; e; e = e->next) blame_list[i++].ent = e; } @@ -2428,7 +2427,7 @@ static void pass_blame(struct blame_scoreboard *sb, struct blame_origin *origin, else if (num_sg < ARRAY_SIZE(sg_buf)) memset(sg_buf, 0, sizeof(sg_buf)); else - sg_origin = xcalloc(num_sg, sizeof(*sg_origin)); + CALLOC_ARRAY(sg_origin, num_sg); /* * The first pass looks for unrenamed path to optimize for diff --git a/block-sha1/sha1.c b/block-sha1/sha1.c index 8681031402..1bb6e7c069 100644 --- a/block-sha1/sha1.c +++ b/block-sha1/sha1.c @@ -70,7 +70,7 @@ * the input data, the next mix it from the 512-bit array. */ #define SHA_SRC(t) get_be32((unsigned char *) block + (t)*4) -#define SHA_MIX(t) SHA_ROL(W((t)+13) ^ W((t)+8) ^ W((t)+2) ^ W(t), 1); +#define SHA_MIX(t) SHA_ROL(W((t)+13) ^ W((t)+8) ^ W((t)+2) ^ W(t), 1) #define SHA_ROUND(t, input, fn, constant, A, B, C, D, E) do { \ unsigned int TEMP = input(t); setW(t, TEMP); \ @@ -277,7 +277,7 @@ struct bloom_filter *get_or_compute_bloom_filter(struct repository *r, *computed |= BLOOM_TRUNC_EMPTY; filter->len = 1; } - filter->data = xcalloc(filter->len, sizeof(unsigned char)); + CALLOC_ARRAY(filter->data, filter->len); hashmap_for_each_entry(&pathmap, &iter, e, entry) { struct bloom_key key; diff --git a/builtin/bisect--helper.c b/builtin/bisect--helper.c index d69e13335d..1fdb7d9d10 100644 --- a/builtin/bisect--helper.c +++ b/builtin/bisect--helper.c @@ -874,12 +874,19 @@ static enum bisect_error bisect_state(struct bisect_terms *terms, const char **a */ for (; argc; argc--, argv++) { + struct commit *commit; + if (get_oid(*argv, &oid)){ error(_("Bad rev input: %s"), *argv); oid_array_clear(&revs); return BISECT_FAILED; } - oid_array_append(&revs, &oid); + + commit = lookup_commit_reference(the_repository, &oid); + if (!commit) + die(_("Bad rev input (not a commit): %s"), *argv); + + oid_array_append(&revs, &commit->object.oid); } if (strbuf_read_file(&buf, git_path_bisect_expected_rev(), 0) < the_hash_algo->hexsz || diff --git a/builtin/clean.c b/builtin/clean.c index 687ab473c2..995053b791 100644 --- a/builtin/clean.c +++ b/builtin/clean.c @@ -623,7 +623,7 @@ static int *list_and_choose(struct menu_opts *opts, struct menu_stuff *stuff) nr += chosen[i]; } - result = xcalloc(st_add(nr, 1), sizeof(int)); + CALLOC_ARRAY(result, st_add(nr, 1)); for (i = 0; i < stuff->nr && j < nr; i++) { if (chosen[i]) result[j++] = i; diff --git a/builtin/fast-import.c b/builtin/fast-import.c index dd4d09cece..3afa81cf9a 100644 --- a/builtin/fast-import.c +++ b/builtin/fast-import.c @@ -3322,7 +3322,7 @@ static void option_rewrite_submodules(const char *arg, struct string_list *list) die(_("Expected format name:filename for submodule rewrite option")); *f = '\0'; f++; - ms = xcalloc(1, sizeof(*ms)); + CALLOC_ARRAY(ms, 1); fp = fopen(f, "r"); if (!fp) @@ -3519,9 +3519,9 @@ int cmd_fast_import(int argc, const char **argv, const char *prefix) alloc_objects(object_entry_alloc); strbuf_init(&command_buf, 0); - atom_table = xcalloc(atom_table_sz, sizeof(struct atom_str*)); - branch_table = xcalloc(branch_table_sz, sizeof(struct branch*)); - avail_tree_table = xcalloc(avail_tree_table_sz, sizeof(struct avail_tree_content*)); + CALLOC_ARRAY(atom_table, atom_table_sz); + CALLOC_ARRAY(branch_table, branch_table_sz); + CALLOC_ARRAY(avail_tree_table, avail_tree_table_sz); marks = mem_pool_calloc(&fi_mem_pool, 1, sizeof(struct mark_set)); hashmap_init(&object_table, object_entry_hashcmp, NULL, 0); diff --git a/builtin/grep.c b/builtin/grep.c index 4e91a253ac..5de725f904 100644 --- a/builtin/grep.c +++ b/builtin/grep.c @@ -211,7 +211,7 @@ static void start_threads(struct grep_opt *opt) strbuf_init(&todo[i].out, 0); } - threads = xcalloc(num_threads, sizeof(*threads)); + CALLOC_ARRAY(threads, num_threads); for (i = 0; i < num_threads; i++) { int err; struct grep_opt *o = grep_opt_dup(opt); @@ -1181,6 +1181,5 @@ int cmd_grep(int argc, const char **argv, const char *prefix) run_pager(&opt, prefix); clear_pathspec(&pathspec); free_grep_patterns(&opt); - grep_destroy(); return !hit; } diff --git a/builtin/index-pack.c b/builtin/index-pack.c index bad5748807..21899687e2 100644 --- a/builtin/index-pack.c +++ b/builtin/index-pack.c @@ -185,7 +185,7 @@ static void init_thread(void) if (show_stat) pthread_mutex_init(&deepest_delta_mutex, NULL); pthread_key_create(&key, NULL); - thread_data = xcalloc(nr_threads, sizeof(*thread_data)); + CALLOC_ARRAY(thread_data, nr_threads); for (i = 0; i < nr_threads; i++) { thread_data[i].pack_fd = open(curr_pack, O_RDONLY); if (thread_data[i].pack_fd == -1) @@ -1674,7 +1674,7 @@ static void show_pack_info(int stat_only) unsigned long *chain_histogram = NULL; if (deepest_delta) - chain_histogram = xcalloc(deepest_delta, sizeof(unsigned long)); + CALLOC_ARRAY(chain_histogram, deepest_delta); for (i = 0; i < nr_objects; i++) { struct object_entry *obj = &objects[i]; @@ -1912,10 +1912,10 @@ int cmd_index_pack(int argc, const char **argv, const char *prefix) curr_pack = open_pack_file(pack_name); parse_pack_header(); - objects = xcalloc(st_add(nr_objects, 1), sizeof(struct object_entry)); + CALLOC_ARRAY(objects, st_add(nr_objects, 1)); if (show_stat) - obj_stat = xcalloc(st_add(nr_objects, 1), sizeof(struct object_stat)); - ofs_deltas = xcalloc(nr_objects, sizeof(struct ofs_delta_entry)); + CALLOC_ARRAY(obj_stat, st_add(nr_objects, 1)); + CALLOC_ARRAY(ofs_deltas, nr_objects); parse_pack_objects(pack_hash); if (report_end_of_input) write_in_full(2, "\0", 1); diff --git a/builtin/init-db.c b/builtin/init-db.c index dcc45bef51..f82efe4aff 100644 --- a/builtin/init-db.c +++ b/builtin/init-db.c @@ -212,6 +212,7 @@ static int create_default_files(const char *template_path, int reinit; int filemode; struct strbuf err = STRBUF_INIT; + const char *work_tree = get_git_work_tree(); /* Just look for `init.templatedir` */ init_db_template_dir = NULL; /* re-set in case it was set before */ @@ -235,7 +236,7 @@ static int create_default_files(const char *template_path, * We must make sure command-line options continue to override any * values we might have just re-read from the config. */ - is_bare_repository_cfg = init_is_bare_repository; + is_bare_repository_cfg = init_is_bare_repository || !work_tree; if (init_shared_repository != -1) set_shared_repository(init_shared_repository); @@ -299,7 +300,6 @@ static int create_default_files(const char *template_path, if (is_bare_repository()) git_config_set("core.bare", "true"); else { - const char *work_tree = get_git_work_tree(); git_config_set("core.bare", "false"); /* allow template config file to override the default */ if (log_all_ref_updates == LOG_REFS_UNSET) diff --git a/builtin/ls-remote.c b/builtin/ls-remote.c index ef604752a0..abfa984737 100644 --- a/builtin/ls-remote.c +++ b/builtin/ls-remote.c @@ -88,7 +88,7 @@ int cmd_ls_remote(int argc, const char **argv, const char *prefix) if (argc > 1) { int i; - pattern = xcalloc(argc, sizeof(const char *)); + CALLOC_ARRAY(pattern, argc); for (i = 1; i < argc; i++) { pattern[i - 1] = xstrfmt("*/%s", argv[i]); } diff --git a/builtin/merge.c b/builtin/merge.c index eb00b273e6..388619536a 100644 --- a/builtin/merge.c +++ b/builtin/merge.c @@ -210,7 +210,7 @@ static struct strategy *get_strategy(const char *name) exit(1); } - ret = xcalloc(1, sizeof(struct strategy)); + CALLOC_ARRAY(ret, 1); ret->name = xstrdup(name); ret->attr = NO_TRIVIAL; return ret; diff --git a/builtin/mv.c b/builtin/mv.c index 7dac714af9..3fccdcb645 100644 --- a/builtin/mv.c +++ b/builtin/mv.c @@ -221,7 +221,7 @@ int cmd_mv(int argc, const char **argv, const char *prefix) } argc += last - first; } - } else if (!(ce = cache_file_exists(src, length, ignore_case))) { + } else if (!(ce = cache_file_exists(src, length, 0))) { bad = _("not under version control"); } else if (ce_stage(ce)) { bad = _("conflicted"); diff --git a/builtin/notes.c b/builtin/notes.c index 08b8914d29..74bba39ca8 100644 --- a/builtin/notes.c +++ b/builtin/notes.c @@ -730,7 +730,7 @@ static int merge_commit(struct notes_merge_options *o) else oidclr(&parent_oid); - t = xcalloc(1, sizeof(struct notes_tree)); + CALLOC_ARRAY(t, 1); init_notes(t, "NOTES_MERGE_PARTIAL", combine_notes_overwrite, 0); o->local_ref = local_ref_to_free = diff --git a/builtin/pack-objects.c b/builtin/pack-objects.c index 6d62aaf59a..8319831514 100644 --- a/builtin/pack-objects.c +++ b/builtin/pack-objects.c @@ -1188,7 +1188,8 @@ static int have_duplicate_entry(const struct object_id *oid, return 1; } -static int want_found_object(int exclude, struct packed_git *p) +static int want_found_object(const struct object_id *oid, int exclude, + struct packed_git *p) { if (exclude) return 1; @@ -1204,27 +1205,82 @@ static int want_found_object(int exclude, struct packed_git *p) * make sure no copy of this object appears in _any_ pack that makes us * to omit the object, so we need to check all the packs. * - * We can however first check whether these options can possible matter; + * We can however first check whether these options can possibly matter; * if they do not matter we know we want the object in generated pack. * Otherwise, we signal "-1" at the end to tell the caller that we do * not know either way, and it needs to check more packs. */ - if (!ignore_packed_keep_on_disk && - !ignore_packed_keep_in_core && - (!local || !have_non_local_packs)) - return 1; + /* + * Objects in packs borrowed from elsewhere are discarded regardless of + * if they appear in other packs that weren't borrowed. + */ if (local && !p->pack_local) return 0; - if (p->pack_local && - ((ignore_packed_keep_on_disk && p->pack_keep) || - (ignore_packed_keep_in_core && p->pack_keep_in_core))) - return 0; + + /* + * Then handle .keep first, as we have a fast(er) path there. + */ + if (ignore_packed_keep_on_disk || ignore_packed_keep_in_core) { + /* + * Set the flags for the kept-pack cache to be the ones we want + * to ignore. + * + * That is, if we are ignoring objects in on-disk keep packs, + * then we want to search through the on-disk keep and ignore + * the in-core ones. + */ + unsigned flags = 0; + if (ignore_packed_keep_on_disk) + flags |= ON_DISK_KEEP_PACKS; + if (ignore_packed_keep_in_core) + flags |= IN_CORE_KEEP_PACKS; + + if (ignore_packed_keep_on_disk && p->pack_keep) + return 0; + if (ignore_packed_keep_in_core && p->pack_keep_in_core) + return 0; + if (has_object_kept_pack(oid, flags)) + return 0; + } + + /* + * At this point we know definitively that either we don't care about + * keep-packs, or the object is not in one. Keep checking other + * conditions... + */ + if (!local || !have_non_local_packs) + return 1; /* we don't know yet; keep looking for more packs */ return -1; } +static int want_object_in_pack_one(struct packed_git *p, + const struct object_id *oid, + int exclude, + struct packed_git **found_pack, + off_t *found_offset) +{ + off_t offset; + + if (p == *found_pack) + offset = *found_offset; + else + offset = find_pack_entry_one(oid->hash, p); + + if (offset) { + if (!*found_pack) { + if (!is_pack_valid(p)) + return -1; + *found_offset = offset; + *found_pack = p; + } + return want_found_object(oid, exclude, p); + } + return -1; +} + /* * Check whether we want the object in the pack (e.g., we do not want * objects found in non-local stores if the "--local" option was used). @@ -1252,7 +1308,7 @@ static int want_object_in_pack(const struct object_id *oid, * are present we will determine the answer right now. */ if (*found_pack) { - want = want_found_object(exclude, *found_pack); + want = want_found_object(oid, exclude, *found_pack); if (want != -1) return want; } @@ -1260,51 +1316,20 @@ static int want_object_in_pack(const struct object_id *oid, for (m = get_multi_pack_index(the_repository); m; m = m->next) { struct pack_entry e; if (fill_midx_entry(the_repository, oid, &e, m)) { - struct packed_git *p = e.p; - off_t offset; - - if (p == *found_pack) - offset = *found_offset; - else - offset = find_pack_entry_one(oid->hash, p); - - if (offset) { - if (!*found_pack) { - if (!is_pack_valid(p)) - continue; - *found_offset = offset; - *found_pack = p; - } - want = want_found_object(exclude, p); - if (want != -1) - return want; - } + want = want_object_in_pack_one(e.p, oid, exclude, found_pack, found_offset); + if (want != -1) + return want; } } list_for_each(pos, get_packed_git_mru(the_repository)) { struct packed_git *p = list_entry(pos, struct packed_git, mru); - off_t offset; - - if (p == *found_pack) - offset = *found_offset; - else - offset = find_pack_entry_one(oid->hash, p); - - if (offset) { - if (!*found_pack) { - if (!is_pack_valid(p)) - continue; - *found_offset = offset; - *found_pack = p; - } - want = want_found_object(exclude, p); - if (!exclude && want > 0) - list_move(&p->mru, - get_packed_git_mru(the_repository)); - if (want != -1) - return want; - } + want = want_object_in_pack_one(p, oid, exclude, found_pack, found_offset); + if (!exclude && want > 0) + list_move(&p->mru, + get_packed_git_mru(the_repository)); + if (want != -1) + return want; } if (uri_protocols.nr) { @@ -1635,7 +1660,7 @@ static void add_preferred_base(struct object_id *oid) } } - it = xcalloc(1, sizeof(*it)); + CALLOC_ARRAY(it, 1); it->next = pbase_tree; pbase_tree = it; @@ -2096,7 +2121,7 @@ static void get_object_details(void) progress_state = start_progress(_("Counting objects"), to_pack.nr_objects); - sorted_by_offset = xcalloc(to_pack.nr_objects, sizeof(struct object_entry *)); + CALLOC_ARRAY(sorted_by_offset, to_pack.nr_objects); for (i = 0; i < to_pack.nr_objects; i++) sorted_by_offset[i] = to_pack.objects + i; QSORT(sorted_by_offset, to_pack.nr_objects, pack_offset_sort); @@ -2428,7 +2453,7 @@ static void find_deltas(struct object_entry **list, unsigned *list_size, struct unpacked *array; unsigned long mem_usage = 0; - array = xcalloc(window, sizeof(struct unpacked)); + CALLOC_ARRAY(array, window); for (;;) { struct object_entry *entry; @@ -2665,7 +2690,7 @@ static void ll_find_deltas(struct object_entry **list, unsigned list_size, if (progress > pack_to_stdout) fprintf_ln(stderr, _("Delta compression using up to %d threads"), delta_search_threads); - p = xcalloc(delta_search_threads, sizeof(*p)); + CALLOC_ARRAY(p, delta_search_threads); /* Partition the work amongst work threads. */ for (i = 0; i < delta_search_threads; i++) { @@ -2986,6 +3011,191 @@ static int git_pack_config(const char *k, const char *v, void *cb) return git_default_config(k, v, cb); } +/* Counters for trace2 output when in --stdin-packs mode. */ +static int stdin_packs_found_nr; +static int stdin_packs_hints_nr; + +static int add_object_entry_from_pack(const struct object_id *oid, + struct packed_git *p, + uint32_t pos, + void *_data) +{ + struct rev_info *revs = _data; + struct object_info oi = OBJECT_INFO_INIT; + off_t ofs; + enum object_type type; + + display_progress(progress_state, ++nr_seen); + + if (have_duplicate_entry(oid, 0)) + return 0; + + ofs = nth_packed_object_offset(p, pos); + if (!want_object_in_pack(oid, 0, &p, &ofs)) + return 0; + + oi.typep = &type; + if (packed_object_info(the_repository, p, ofs, &oi) < 0) + die(_("could not get type of object %s in pack %s"), + oid_to_hex(oid), p->pack_name); + else if (type == OBJ_COMMIT) { + /* + * commits in included packs are used as starting points for the + * subsequent revision walk + */ + add_pending_oid(revs, NULL, oid, 0); + } + + stdin_packs_found_nr++; + + create_object_entry(oid, type, 0, 0, 0, p, ofs); + + return 0; +} + +static void show_commit_pack_hint(struct commit *commit, void *_data) +{ + /* nothing to do; commits don't have a namehash */ +} + +static void show_object_pack_hint(struct object *object, const char *name, + void *_data) +{ + struct object_entry *oe = packlist_find(&to_pack, &object->oid); + if (!oe) + return; + + /* + * Our 'to_pack' list was constructed by iterating all objects packed in + * included packs, and so doesn't have a non-zero hash field that you + * would typically pick up during a reachability traversal. + * + * Make a best-effort attempt to fill in the ->hash and ->no_try_delta + * here using a now in order to perhaps improve the delta selection + * process. + */ + oe->hash = pack_name_hash(name); + oe->no_try_delta = name && no_try_delta(name); + + stdin_packs_hints_nr++; +} + +static int pack_mtime_cmp(const void *_a, const void *_b) +{ + struct packed_git *a = ((const struct string_list_item*)_a)->util; + struct packed_git *b = ((const struct string_list_item*)_b)->util; + + /* + * order packs by descending mtime so that objects are laid out + * roughly as newest-to-oldest + */ + if (a->mtime < b->mtime) + return 1; + else if (b->mtime < a->mtime) + return -1; + else + return 0; +} + +static void read_packs_list_from_stdin(void) +{ + struct strbuf buf = STRBUF_INIT; + struct string_list include_packs = STRING_LIST_INIT_DUP; + struct string_list exclude_packs = STRING_LIST_INIT_DUP; + struct string_list_item *item = NULL; + + struct packed_git *p; + struct rev_info revs; + + repo_init_revisions(the_repository, &revs, NULL); + /* + * Use a revision walk to fill in the namehash of objects in the include + * packs. To save time, we'll avoid traversing through objects that are + * in excluded packs. + * + * That may cause us to avoid populating all of the namehash fields of + * all included objects, but our goal is best-effort, since this is only + * an optimization during delta selection. + */ + revs.no_kept_objects = 1; + revs.keep_pack_cache_flags |= IN_CORE_KEEP_PACKS; + revs.blob_objects = 1; + revs.tree_objects = 1; + revs.tag_objects = 1; + revs.ignore_missing_links = 1; + + while (strbuf_getline(&buf, stdin) != EOF) { + if (!buf.len) + continue; + + if (*buf.buf == '^') + string_list_append(&exclude_packs, buf.buf + 1); + else + string_list_append(&include_packs, buf.buf); + + strbuf_reset(&buf); + } + + string_list_sort(&include_packs); + string_list_sort(&exclude_packs); + + for (p = get_all_packs(the_repository); p; p = p->next) { + const char *pack_name = pack_basename(p); + + item = string_list_lookup(&include_packs, pack_name); + if (!item) + item = string_list_lookup(&exclude_packs, pack_name); + + if (item) + item->util = p; + } + + /* + * First handle all of the excluded packs, marking them as kept in-core + * so that later calls to add_object_entry() discards any objects that + * are also found in excluded packs. + */ + for_each_string_list_item(item, &exclude_packs) { + struct packed_git *p = item->util; + if (!p) + die(_("could not find pack '%s'"), item->string); + p->pack_keep_in_core = 1; + } + + /* + * Order packs by ascending mtime; use QSORT directly to access the + * string_list_item's ->util pointer, which string_list_sort() does not + * provide. + */ + QSORT(include_packs.items, include_packs.nr, pack_mtime_cmp); + + for_each_string_list_item(item, &include_packs) { + struct packed_git *p = item->util; + if (!p) + die(_("could not find pack '%s'"), item->string); + for_each_object_in_pack(p, + add_object_entry_from_pack, + &revs, + FOR_EACH_OBJECT_PACK_ORDER); + } + + if (prepare_revision_walk(&revs)) + die(_("revision walk setup failed")); + traverse_commit_list(&revs, + show_commit_pack_hint, + show_object_pack_hint, + NULL); + + trace2_data_intmax("pack-objects", the_repository, "stdin_packs_found", + stdin_packs_found_nr); + trace2_data_intmax("pack-objects", the_repository, "stdin_packs_hints", + stdin_packs_hints_nr); + + strbuf_release(&buf); + string_list_clear(&include_packs, 0); + string_list_clear(&exclude_packs, 0); +} + static void read_object_list_from_stdin(void) { char line[GIT_MAX_HEXSZ + 1 + PATH_MAX + 2]; @@ -3489,6 +3699,7 @@ int cmd_pack_objects(int argc, const char **argv, const char *prefix) struct strvec rp = STRVEC_INIT; int rev_list_unpacked = 0, rev_list_all = 0, rev_list_reflog = 0; int rev_list_index = 0; + int stdin_packs = 0; struct string_list keep_pack_list = STRING_LIST_INIT_NODUP; struct option pack_objects_options[] = { OPT_SET_INT('q', "quiet", &progress, @@ -3539,6 +3750,8 @@ int cmd_pack_objects(int argc, const char **argv, const char *prefix) OPT_SET_INT_F(0, "indexed-objects", &rev_list_index, N_("include objects referred to by the index"), 1, PARSE_OPT_NONEG), + OPT_BOOL(0, "stdin-packs", &stdin_packs, + N_("read packs from stdin")), OPT_BOOL(0, "stdout", &pack_to_stdout, N_("output pack to stdout")), OPT_BOOL(0, "include-tag", &include_tag, @@ -3645,7 +3858,7 @@ int cmd_pack_objects(int argc, const char **argv, const char *prefix) use_internal_rev_list = 1; strvec_push(&rp, "--indexed-objects"); } - if (rev_list_unpacked) { + if (rev_list_unpacked && !stdin_packs) { use_internal_rev_list = 1; strvec_push(&rp, "--unpacked"); } @@ -3690,8 +3903,13 @@ int cmd_pack_objects(int argc, const char **argv, const char *prefix) if (filter_options.choice) { if (!pack_to_stdout) die(_("cannot use --filter without --stdout")); + if (stdin_packs) + die(_("cannot use --filter with --stdin-packs")); } + if (stdin_packs && use_internal_rev_list) + die(_("cannot use internal rev list with --stdin-packs")); + /* * "soft" reasons not to use bitmaps - for on-disk repack by default we want * @@ -3750,7 +3968,13 @@ int cmd_pack_objects(int argc, const char **argv, const char *prefix) if (progress) progress_state = start_progress(_("Enumerating objects"), 0); - if (!use_internal_rev_list) + if (stdin_packs) { + /* avoids adding objects in excluded packs */ + ignore_packed_keep_in_core = 1; + read_packs_list_from_stdin(); + if (rev_list_unpacked) + add_unreachable_loose_objects(); + } else if (!use_internal_rev_list) read_object_list_from_stdin(); else { get_object_list(rp.nr, rp.v); diff --git a/builtin/pack-redundant.c b/builtin/pack-redundant.c index 6e115a811a..7102996c75 100644 --- a/builtin/pack-redundant.c +++ b/builtin/pack-redundant.c @@ -373,7 +373,7 @@ static void sort_pack_list(struct pack_list **pl) return; /* prepare an array of packed_list for easier sorting */ - ary = xcalloc(n, sizeof(struct pack_list *)); + CALLOC_ARRAY(ary, n); for (n = 0, p = *pl; p; p = p->next) ary[n++] = p; diff --git a/builtin/receive-pack.c b/builtin/receive-pack.c index d26040c477..6bc12c828a 100644 --- a/builtin/receive-pack.c +++ b/builtin/receive-pack.c @@ -358,7 +358,7 @@ static void proc_receive_ref_append(const char *prefix) char *p; int len; - ref_pattern = xcalloc(1, sizeof(struct proc_receive_ref)); + CALLOC_ARRAY(ref_pattern, 1); p = strchr(prefix, ':'); if (p) { while (prefix < p) { @@ -1024,7 +1024,7 @@ static int read_proc_receive_report(struct packet_reader *reader, } if (new_report) { if (!hint->report) { - hint->report = xcalloc(1, sizeof(struct ref_push_report)); + CALLOC_ARRAY(hint->report, 1); report = hint->report; } else { report = hint->report; @@ -2313,11 +2313,9 @@ static void prepare_shallow_update(struct shallow_info *si) ALLOC_ARRAY(si->used_shallow, si->shallow->nr); assign_shallow_commits_to_refs(si, si->used_shallow, NULL); - si->need_reachability_test = - xcalloc(si->shallow->nr, sizeof(*si->need_reachability_test)); - si->reachable = - xcalloc(si->shallow->nr, sizeof(*si->reachable)); - si->shallow_ref = xcalloc(si->ref->nr, sizeof(*si->shallow_ref)); + CALLOC_ARRAY(si->need_reachability_test, si->shallow->nr); + CALLOC_ARRAY(si->reachable, si->shallow->nr); + CALLOC_ARRAY(si->shallow_ref, si->ref->nr); for (i = 0; i < si->nr_ours; i++) si->need_reachability_test[si->ours[i]] = 1; diff --git a/builtin/remote.c b/builtin/remote.c index d11a5589e4..717b662d45 100644 --- a/builtin/remote.c +++ b/builtin/remote.c @@ -221,7 +221,7 @@ static int add(int argc, const char **argv) if (fetch_tags != TAGS_DEFAULT) { strbuf_reset(&buf); - strbuf_addf(&buf, "remote.%s.tagopt", name); + strbuf_addf(&buf, "remote.%s.tagOpt", name); git_config_set(buf.buf, fetch_tags == TAGS_SET ? "--tags" : "--no-tags"); } @@ -746,7 +746,7 @@ static int mv(int argc, const char **argv) } if (info->push_remote_name && !strcmp(info->push_remote_name, rename.old_name)) { strbuf_reset(&buf); - strbuf_addf(&buf, "branch.%s.pushremote", item->string); + strbuf_addf(&buf, "branch.%s.pushRemote", item->string); git_config_set(buf.buf, rename.new_name); } } diff --git a/builtin/repack.c b/builtin/repack.c index 01440de2d5..6ce2556c9e 100644 --- a/builtin/repack.c +++ b/builtin/repack.c @@ -297,6 +297,142 @@ static void repack_promisor_objects(const struct pack_objects_args *args, #define ALL_INTO_ONE 1 #define LOOSEN_UNREACHABLE 2 +struct pack_geometry { + struct packed_git **pack; + uint32_t pack_nr, pack_alloc; + uint32_t split; +}; + +static uint32_t geometry_pack_weight(struct packed_git *p) +{ + if (open_pack_index(p)) + die(_("cannot open index for %s"), p->pack_name); + return p->num_objects; +} + +static int geometry_cmp(const void *va, const void *vb) +{ + uint32_t aw = geometry_pack_weight(*(struct packed_git **)va), + bw = geometry_pack_weight(*(struct packed_git **)vb); + + if (aw < bw) + return -1; + if (aw > bw) + return 1; + return 0; +} + +static void init_pack_geometry(struct pack_geometry **geometry_p) +{ + struct packed_git *p; + struct pack_geometry *geometry; + + *geometry_p = xcalloc(1, sizeof(struct pack_geometry)); + geometry = *geometry_p; + + for (p = get_all_packs(the_repository); p; p = p->next) { + if (!pack_kept_objects && p->pack_keep) + continue; + + ALLOC_GROW(geometry->pack, + geometry->pack_nr + 1, + geometry->pack_alloc); + + geometry->pack[geometry->pack_nr] = p; + geometry->pack_nr++; + } + + QSORT(geometry->pack, geometry->pack_nr, geometry_cmp); +} + +static void split_pack_geometry(struct pack_geometry *geometry, int factor) +{ + uint32_t i; + uint32_t split; + off_t total_size = 0; + + if (!geometry->pack_nr) { + geometry->split = geometry->pack_nr; + return; + } + + /* + * First, count the number of packs (in descending order of size) which + * already form a geometric progression. + */ + for (i = geometry->pack_nr - 1; i > 0; i--) { + struct packed_git *ours = geometry->pack[i]; + struct packed_git *prev = geometry->pack[i - 1]; + + if (unsigned_mult_overflows(factor, geometry_pack_weight(prev))) + die(_("pack %s too large to consider in geometric " + "progression"), + prev->pack_name); + + if (geometry_pack_weight(ours) < factor * geometry_pack_weight(prev)) + break; + } + + split = i; + + if (split) { + /* + * Move the split one to the right, since the top element in the + * last-compared pair can't be in the progression. Only do this + * when we split in the middle of the array (otherwise if we got + * to the end, then the split is in the right place). + */ + split++; + } + + /* + * Then, anything to the left of 'split' must be in a new pack. But, + * creating that new pack may cause packs in the heavy half to no longer + * form a geometric progression. + * + * Compute an expected size of the new pack, and then determine how many + * packs in the heavy half need to be joined into it (if any) to restore + * the geometric progression. + */ + for (i = 0; i < split; i++) { + struct packed_git *p = geometry->pack[i]; + + if (unsigned_add_overflows(total_size, geometry_pack_weight(p))) + die(_("pack %s too large to roll up"), p->pack_name); + total_size += geometry_pack_weight(p); + } + for (i = split; i < geometry->pack_nr; i++) { + struct packed_git *ours = geometry->pack[i]; + + if (unsigned_mult_overflows(factor, total_size)) + die(_("pack %s too large to roll up"), ours->pack_name); + + if (geometry_pack_weight(ours) < factor * total_size) { + if (unsigned_add_overflows(total_size, + geometry_pack_weight(ours))) + die(_("pack %s too large to roll up"), + ours->pack_name); + + split++; + total_size += geometry_pack_weight(ours); + } else + break; + } + + geometry->split = split; +} + +static void clear_pack_geometry(struct pack_geometry *geometry) +{ + if (!geometry) + return; + + free(geometry->pack); + geometry->pack_nr = 0; + geometry->pack_alloc = 0; + geometry->split = 0; +} + int cmd_repack(int argc, const char **argv, const char *prefix) { struct child_process cmd = CHILD_PROCESS_INIT; @@ -304,6 +440,7 @@ int cmd_repack(int argc, const char **argv, const char *prefix) struct string_list names = STRING_LIST_INIT_DUP; struct string_list rollback = STRING_LIST_INIT_NODUP; struct string_list existing_packs = STRING_LIST_INIT_DUP; + struct pack_geometry *geometry = NULL; struct strbuf line = STRBUF_INIT; int i, ext, ret; FILE *out; @@ -316,6 +453,7 @@ int cmd_repack(int argc, const char **argv, const char *prefix) struct string_list keep_pack_list = STRING_LIST_INIT_NODUP; int no_update_server_info = 0; struct pack_objects_args po_args = {NULL}; + int geometric_factor = 0; struct option builtin_repack_options[] = { OPT_BIT('a', NULL, &pack_everything, @@ -356,6 +494,8 @@ int cmd_repack(int argc, const char **argv, const char *prefix) N_("repack objects in packs marked with .keep")), OPT_STRING_LIST(0, "keep-pack", &keep_pack_list, N_("name"), N_("do not repack this pack")), + OPT_INTEGER('g', "geometric", &geometric_factor, + N_("find a geometric progression with factor <N>")), OPT_END() }; @@ -382,6 +522,13 @@ int cmd_repack(int argc, const char **argv, const char *prefix) if (write_bitmaps && !(pack_everything & ALL_INTO_ONE)) die(_(incremental_bitmap_conflict_error)); + if (geometric_factor) { + if (pack_everything) + die(_("--geometric is incompatible with -A, -a")); + init_pack_geometry(&geometry); + split_pack_geometry(geometry, geometric_factor); + } + packdir = mkpathdup("%s/pack", get_object_directory()); packtmp = mkpathdup("%s/.tmp-%d-pack", packdir, (int)getpid()); @@ -396,9 +543,21 @@ int cmd_repack(int argc, const char **argv, const char *prefix) strvec_pushf(&cmd.args, "--keep-pack=%s", keep_pack_list.items[i].string); strvec_push(&cmd.args, "--non-empty"); - strvec_push(&cmd.args, "--all"); - strvec_push(&cmd.args, "--reflog"); - strvec_push(&cmd.args, "--indexed-objects"); + if (!geometry) { + /* + * We need to grab all reachable objects, including those that + * are reachable from reflogs and the index. + * + * When repacking into a geometric progression of packs, + * however, we ask 'git pack-objects --stdin-packs', and it is + * not about packing objects based on reachability but about + * repacking all the objects in specified packs and loose ones + * (indeed, --stdin-packs is incompatible with these options). + */ + strvec_push(&cmd.args, "--all"); + strvec_push(&cmd.args, "--reflog"); + strvec_push(&cmd.args, "--indexed-objects"); + } if (has_promisor_remote()) strvec_push(&cmd.args, "--exclude-promisor-objects"); if (write_bitmaps > 0) @@ -429,17 +588,37 @@ int cmd_repack(int argc, const char **argv, const char *prefix) strvec_push(&cmd.env_array, "GIT_REF_PARANOIA=1"); } } + } else if (geometry) { + strvec_push(&cmd.args, "--stdin-packs"); + strvec_push(&cmd.args, "--unpacked"); } else { strvec_push(&cmd.args, "--unpacked"); strvec_push(&cmd.args, "--incremental"); } - cmd.no_stdin = 1; + if (geometry) + cmd.in = -1; + else + cmd.no_stdin = 1; ret = start_command(&cmd); if (ret) return ret; + if (geometry) { + FILE *in = xfdopen(cmd.in, "w"); + /* + * The resulting pack should contain all objects in packs that + * are going to be rolled up, but exclude objects in packs which + * are being left alone. + */ + for (i = 0; i < geometry->split; i++) + fprintf(in, "%s\n", pack_basename(geometry->pack[i])); + for (i = geometry->split; i < geometry->pack_nr; i++) + fprintf(in, "^%s\n", pack_basename(geometry->pack[i])); + fclose(in); + } + out = xfdopen(cmd.out, "r"); while (strbuf_getline_lf(&line, out) != EOF) { if (line.len != the_hash_algo->hexsz) @@ -507,6 +686,25 @@ int cmd_repack(int argc, const char **argv, const char *prefix) if (!string_list_has_string(&names, sha1)) remove_redundant_pack(packdir, item->string); } + + if (geometry) { + struct strbuf buf = STRBUF_INIT; + + uint32_t i; + for (i = 0; i < geometry->split; i++) { + struct packed_git *p = geometry->pack[i]; + if (string_list_has_string(&names, + hash_to_hex(p->hash))) + continue; + + strbuf_reset(&buf); + strbuf_addstr(&buf, pack_basename(p)); + strbuf_strip_suffix(&buf, ".pack"); + + remove_redundant_pack(packdir, buf.buf); + } + strbuf_release(&buf); + } if (!po_args.quiet && isatty(2)) opts |= PRUNE_PACKED_VERBOSE; prune_packed_objects(opts); @@ -528,6 +726,7 @@ int cmd_repack(int argc, const char **argv, const char *prefix) string_list_clear(&names, 0); string_list_clear(&rollback, 0); string_list_clear(&existing_packs, 0); + clear_pack_geometry(geometry); strbuf_release(&line); return 0; diff --git a/builtin/sparse-checkout.c b/builtin/sparse-checkout.c index 2306a9ad98..d7da50ada5 100644 --- a/builtin/sparse-checkout.c +++ b/builtin/sparse-checkout.c @@ -64,7 +64,7 @@ static int sparse_checkout_list(int argc, const char **argv) pl.use_cone_patterns = core_sparse_checkout_cone; sparse_filename = get_sparse_checkout_filename(); - res = add_patterns_from_file_to_list(sparse_filename, "", 0, &pl, NULL); + res = add_patterns_from_file_to_list(sparse_filename, "", 0, &pl, NULL, 0); free(sparse_filename); if (res < 0) { @@ -321,7 +321,7 @@ static int sparse_checkout_init(int argc, const char **argv) memset(&pl, 0, sizeof(pl)); sparse_filename = get_sparse_checkout_filename(); - res = add_patterns_from_file_to_list(sparse_filename, "", 0, &pl, NULL); + res = add_patterns_from_file_to_list(sparse_filename, "", 0, &pl, NULL, 0); /* If we already have a sparse-checkout file, use it. */ if (res >= 0) { @@ -483,7 +483,7 @@ static void add_patterns_cone_mode(int argc, const char **argv, existing.use_cone_patterns = core_sparse_checkout_cone; if (add_patterns_from_file_to_list(sparse_filename, "", 0, - &existing, NULL)) + &existing, NULL, 0)) die(_("unable to load existing sparse-checkout patterns")); free(sparse_filename); @@ -507,7 +507,7 @@ static void add_patterns_literal(int argc, const char **argv, { char *sparse_filename = get_sparse_checkout_filename(); if (add_patterns_from_file_to_list(sparse_filename, "", 0, - pl, NULL)) + pl, NULL, 0)) die(_("unable to load existing sparse-checkout patterns")); free(sparse_filename); add_patterns_from_input(pl, argc, argv); diff --git a/builtin/stash.c b/builtin/stash.c index ba774cce67..3477e940e3 100644 --- a/builtin/stash.c +++ b/builtin/stash.c @@ -768,6 +768,7 @@ static int list_stash(int argc, const char **argv, const char *prefix) static int show_stat = 1; static int show_patch; +static int show_include_untracked; static int use_legacy_stash; static int git_stash_config(const char *var, const char *value, void *cb) @@ -780,6 +781,10 @@ static int git_stash_config(const char *var, const char *value, void *cb) show_patch = git_config_bool(var, value); return 0; } + if (!strcmp(var, "stash.showincludeuntracked")) { + show_include_untracked = git_config_bool(var, value); + return 0; + } if (!strcmp(var, "stash.usebuiltin")) { use_legacy_stash = !git_config_bool(var, value); return 0; @@ -787,6 +792,33 @@ static int git_stash_config(const char *var, const char *value, void *cb) return git_diff_basic_config(var, value, cb); } +static void diff_include_untracked(const struct stash_info *info, struct diff_options *diff_opt) +{ + const struct object_id *oid[] = { &info->w_commit, &info->u_tree }; + struct tree *tree[ARRAY_SIZE(oid)]; + struct tree_desc tree_desc[ARRAY_SIZE(oid)]; + struct unpack_trees_options unpack_tree_opt = { 0 }; + int i; + + for (i = 0; i < ARRAY_SIZE(oid); i++) { + tree[i] = parse_tree_indirect(oid[i]); + if (parse_tree(tree[i]) < 0) + die(_("failed to parse tree")); + init_tree_desc(&tree_desc[i], tree[i]->buffer, tree[i]->size); + } + + unpack_tree_opt.head_idx = -1; + unpack_tree_opt.src_index = &the_index; + unpack_tree_opt.dst_index = &the_index; + unpack_tree_opt.merge = 1; + unpack_tree_opt.fn = stash_worktree_untracked_merge; + + if (unpack_trees(ARRAY_SIZE(tree_desc), tree_desc, &unpack_tree_opt)) + die(_("failed to unpack trees")); + + do_diff_cache(&info->b_commit, diff_opt); +} + static int show_stash(int argc, const char **argv, const char *prefix) { int i; @@ -795,7 +827,18 @@ static int show_stash(int argc, const char **argv, const char *prefix) struct rev_info rev; struct strvec stash_args = STRVEC_INIT; struct strvec revision_args = STRVEC_INIT; + enum { + UNTRACKED_NONE, + UNTRACKED_INCLUDE, + UNTRACKED_ONLY + } show_untracked = UNTRACKED_NONE; struct option options[] = { + OPT_SET_INT('u', "include-untracked", &show_untracked, + N_("include untracked files in the stash"), + UNTRACKED_INCLUDE), + OPT_SET_INT_F(0, "only-untracked", &show_untracked, + N_("only show untracked files in the stash"), + UNTRACKED_ONLY, PARSE_OPT_NONEG), OPT_END() }; @@ -803,6 +846,10 @@ static int show_stash(int argc, const char **argv, const char *prefix) git_config(git_diff_ui_config, NULL); init_revisions(&rev, prefix); + argc = parse_options(argc, argv, prefix, options, git_stash_show_usage, + PARSE_OPT_KEEP_ARGV0 | PARSE_OPT_KEEP_UNKNOWN | + PARSE_OPT_KEEP_DASHDASH); + strvec_push(&revision_args, argv[0]); for (i = 1; i < argc; i++) { if (argv[i][0] != '-') @@ -827,6 +874,9 @@ static int show_stash(int argc, const char **argv, const char *prefix) if (show_patch) rev.diffopt.output_format |= DIFF_FORMAT_PATCH; + if (show_include_untracked) + show_untracked = UNTRACKED_INCLUDE; + if (!show_stat && !show_patch) { free_stash_info(&info); return 0; @@ -845,7 +895,17 @@ static int show_stash(int argc, const char **argv, const char *prefix) rev.diffopt.flags.recursive = 1; setup_diff_pager(&rev.diffopt); - diff_tree_oid(&info.b_commit, &info.w_commit, "", &rev.diffopt); + switch (show_untracked) { + case UNTRACKED_NONE: + diff_tree_oid(&info.b_commit, &info.w_commit, "", &rev.diffopt); + break; + case UNTRACKED_ONLY: + diff_root_tree_oid(&info.u_tree, "", &rev.diffopt); + break; + case UNTRACKED_INCLUDE: + diff_include_untracked(&info, &rev.diffopt); + break; + } log_tree_diff_flush(&rev); free_stash_info(&info); diff --git a/builtin/unpack-objects.c b/builtin/unpack-objects.c index dd4a75e030..a4ba2ebac6 100644 --- a/builtin/unpack-objects.c +++ b/builtin/unpack-objects.c @@ -46,7 +46,7 @@ static struct obj_buffer *lookup_object_buffer(struct object *base) static void add_object_buffer(struct object *object, char *buffer, unsigned long size) { struct obj_buffer *obj; - obj = xcalloc(1, sizeof(struct obj_buffer)); + CALLOC_ARRAY(obj, 1); obj->buffer = buffer; obj->size = size; if (add_decoration(&obj_decorate, object, obj)) @@ -500,7 +500,7 @@ static void unpack_all(void) if (!quiet) progress = start_progress(_("Unpacking objects"), nr_objects); - obj_list = xcalloc(nr_objects, sizeof(*obj_list)); + CALLOC_ARRAY(obj_list, nr_objects); for (i = 0; i < nr_objects; i++) { unpack_one(i); display_progress(progress, i + 1); diff --git a/bulk-checkin.c b/bulk-checkin.c index 583aacb9e3..6f3c97cd34 100644 --- a/bulk-checkin.c +++ b/bulk-checkin.c @@ -211,7 +211,7 @@ static int deflate_to_pack(struct bulk_checkin_state *state, /* Note: idx is non-NULL when we are writing */ if ((flags & HASH_WRITE_OBJECT) != 0) - idx = xcalloc(1, sizeof(*idx)); + CALLOC_ARRAY(idx, 1); already_hashed_to = 0; diff --git a/cache-tree.c b/cache-tree.c index 2fb483d3c0..add1f07713 100644 --- a/cache-tree.c +++ b/cache-tree.c @@ -564,7 +564,7 @@ static struct cache_tree *read_one(const char **buffer, unsigned long *size_p) * hence +2. */ it->subtree_alloc = subtree_nr + 2; - it->down = xcalloc(it->subtree_alloc, sizeof(struct cache_tree_sub *)); + CALLOC_ARRAY(it->down, it->subtree_alloc); for (i = 0; i < subtree_nr; i++) { /* read each subtree */ struct cache_tree *sub; diff --git a/combine-diff.c b/combine-diff.c index 9228aebc16..06635f91bc 100644 --- a/combine-diff.c +++ b/combine-diff.c @@ -214,11 +214,11 @@ static struct lline *coalesce_lines(struct lline *base, int *lenbase, * - Else if we have NEW, insert newend lline into base and * consume newend */ - lcs = xcalloc(st_add(origbaselen, 1), sizeof(int*)); - directions = xcalloc(st_add(origbaselen, 1), sizeof(enum coalesce_direction*)); + CALLOC_ARRAY(lcs, st_add(origbaselen, 1)); + CALLOC_ARRAY(directions, st_add(origbaselen, 1)); for (i = 0; i < origbaselen + 1; i++) { - lcs[i] = xcalloc(st_add(lennew, 1), sizeof(int)); - directions[i] = xcalloc(st_add(lennew, 1), sizeof(enum coalesce_direction)); + CALLOC_ARRAY(lcs[i], st_add(lennew, 1)); + CALLOC_ARRAY(directions[i], st_add(lennew, 1)); directions[i][0] = BASE; } for (j = 1; j < lennew + 1; j++) @@ -398,8 +398,8 @@ static void consume_hunk(void *state_, state->lost_bucket = &state->sline[state->nb-1]; } if (!state->sline[state->nb-1].p_lno) - state->sline[state->nb-1].p_lno = - xcalloc(state->num_parent, sizeof(unsigned long)); + CALLOC_ARRAY(state->sline[state->nb - 1].p_lno, + state->num_parent); state->sline[state->nb-1].p_lno[state->n] = state->ob; } @@ -1159,7 +1159,7 @@ static void show_patch_diff(struct combine_diff_path *elem, int num_parent, if (result_size && result[result_size-1] != '\n') cnt++; /* incomplete line */ - sline = xcalloc(st_add(cnt, 2), sizeof(*sline)); + CALLOC_ARRAY(sline, st_add(cnt, 2)); sline[0].bol = result; for (lno = 0, cp = result; cp < result + result_size; cp++) { if (*cp == '\n') { @@ -1178,7 +1178,7 @@ static void show_patch_diff(struct combine_diff_path *elem, int num_parent, /* Even p_lno[cnt+1] is valid -- that is for the end line number * for deletion hunk at the end. */ - sline[0].p_lno = xcalloc(st_mult(st_add(cnt, 2), num_parent), sizeof(unsigned long)); + CALLOC_ARRAY(sline[0].p_lno, st_mult(st_add(cnt, 2), num_parent)); for (lno = 0; lno <= cnt; lno++) sline[lno+1].p_lno = sline[lno].p_lno + num_parent; @@ -1319,7 +1319,7 @@ static struct diff_filepair *combined_pair(struct combine_diff_path *p, struct diff_filespec *pool; pair = xmalloc(sizeof(*pair)); - pool = xcalloc(st_add(num_parent, 1), sizeof(struct diff_filespec)); + CALLOC_ARRAY(pool, st_add(num_parent, 1)); pair->one = pool + 1; pair->two = pool; @@ -1348,7 +1348,7 @@ static void handle_combined_callback(struct diff_options *opt, struct diff_queue_struct q; int i; - q.queue = xcalloc(num_paths, sizeof(struct diff_filepair *)); + CALLOC_ARRAY(q.queue, num_paths); q.alloc = num_paths; q.nr = num_paths; for (i = 0, p = paths; p; p = p->next) diff --git a/commit-graph.c b/commit-graph.c index ca025ce8eb..f18380b922 100644 --- a/commit-graph.c +++ b/commit-graph.c @@ -96,6 +96,13 @@ define_commit_slab(commit_graph_data_slab, struct commit_graph_data); static struct commit_graph_data_slab commit_graph_data_slab = COMMIT_SLAB_INIT(1, commit_graph_data_slab); +static int get_configured_generation_version(struct repository *r) +{ + int version = 2; + repo_config_get_int(r, "commitgraph.generationversion", &version); + return version; +} + uint32_t commit_graph_position(const struct commit *c) { struct commit_graph_data *data = @@ -394,10 +401,13 @@ struct commit_graph *parse_commit_graph(struct repository *r, pair_chunk(cf, GRAPH_CHUNKID_DATA, &graph->chunk_commit_data); pair_chunk(cf, GRAPH_CHUNKID_EXTRAEDGES, &graph->chunk_extra_edges); pair_chunk(cf, GRAPH_CHUNKID_BASE, &graph->chunk_base_graphs); - pair_chunk(cf, GRAPH_CHUNKID_GENERATION_DATA, - &graph->chunk_generation_data); - pair_chunk(cf, GRAPH_CHUNKID_GENERATION_DATA_OVERFLOW, - &graph->chunk_generation_data_overflow); + + if (get_configured_generation_version(r) >= 2) { + pair_chunk(cf, GRAPH_CHUNKID_GENERATION_DATA, + &graph->chunk_generation_data); + pair_chunk(cf, GRAPH_CHUNKID_GENERATION_DATA_OVERFLOW, + &graph->chunk_generation_data_overflow); + } if (r->settings.commit_graph_read_changed_paths) { pair_chunk(cf, GRAPH_CHUNKID_BLOOMINDEXES, @@ -516,7 +526,7 @@ static struct commit_graph *load_commit_graph_chain(struct repository *r, return NULL; count = st.st_size / (the_hash_algo->hexsz + 1); - oids = xcalloc(count, sizeof(struct object_id)); + CALLOC_ARRAY(oids, count); prepare_alt_odb(r); @@ -1839,8 +1849,6 @@ static int write_commit_graph_file(struct write_commit_graph_context *ctx) add_chunk(cf, GRAPH_CHUNKID_DATA, (hashsz + 16) * ctx->commits.nr, write_graph_chunk_data); - if (git_env_bool(GIT_TEST_COMMIT_GRAPH_NO_GDAT, 0)) - ctx->write_generation_data = 0; if (ctx->write_generation_data) add_chunk(cf, GRAPH_CHUNKID_GENERATION_DATA, sizeof(uint32_t) * ctx->commits.nr, @@ -2223,6 +2231,7 @@ int write_commit_graph(struct object_directory *odb, enum commit_graph_write_flags flags, const struct commit_graph_opts *opts) { + struct repository *r = the_repository; struct write_commit_graph_context *ctx; uint32_t i; int res = 0; @@ -2230,23 +2239,23 @@ int write_commit_graph(struct object_directory *odb, struct bloom_filter_settings bloom_settings = DEFAULT_BLOOM_FILTER_SETTINGS; struct topo_level_slab topo_levels; - prepare_repo_settings(the_repository); - if (!the_repository->settings.core_commit_graph) { + prepare_repo_settings(r); + if (!r->settings.core_commit_graph) { warning(_("attempting to write a commit-graph, but 'core.commitGraph' is disabled")); return 0; } - if (!commit_graph_compatible(the_repository)) + if (!commit_graph_compatible(r)) return 0; - ctx = xcalloc(1, sizeof(struct write_commit_graph_context)); - ctx->r = the_repository; + CALLOC_ARRAY(ctx, 1); + ctx->r = r; ctx->odb = odb; ctx->append = flags & COMMIT_GRAPH_WRITE_APPEND ? 1 : 0; ctx->report_progress = flags & COMMIT_GRAPH_WRITE_PROGRESS ? 1 : 0; ctx->split = flags & COMMIT_GRAPH_WRITE_SPLIT ? 1 : 0; ctx->opts = opts; ctx->total_bloom_filter_data_size = 0; - ctx->write_generation_data = 1; + ctx->write_generation_data = (get_configured_generation_version(r) == 2); ctx->num_generation_data_overflows = 0; bloom_settings.bits_per_entry = git_env_ulong("GIT_TEST_BLOOM_SETTINGS_BITS_PER_ENTRY", diff --git a/commit-graph.h b/commit-graph.h index 97f3497c27..96c24fb577 100644 --- a/commit-graph.h +++ b/commit-graph.h @@ -6,7 +6,6 @@ #include "oidset.h" #define GIT_TEST_COMMIT_GRAPH "GIT_TEST_COMMIT_GRAPH" -#define GIT_TEST_COMMIT_GRAPH_NO_GDAT "GIT_TEST_COMMIT_GRAPH_NO_GDAT" #define GIT_TEST_COMMIT_GRAPH_DIE_ON_PARSE "GIT_TEST_COMMIT_GRAPH_DIE_ON_PARSE" #define GIT_TEST_COMMIT_GRAPH_CHANGED_PATHS "GIT_TEST_COMMIT_GRAPH_CHANGED_PATHS" diff --git a/commit-reach.c b/commit-reach.c index 2ea84d3dc0..c226ee3da4 100644 --- a/commit-reach.c +++ b/commit-reach.c @@ -183,7 +183,7 @@ static int remove_redundant_no_gen(struct repository *r, int *filled_index; int i, j, filled; - work = xcalloc(cnt, sizeof(*work)); + CALLOC_ARRAY(work, cnt); redundant = xcalloc(cnt, 1); ALLOC_ARRAY(filled_index, cnt - 1); @@ -399,7 +399,7 @@ static struct commit_list *get_merge_bases_many_0(struct repository *r, /* There are more than one */ cnt = commit_list_count(result); - rslt = xcalloc(cnt, sizeof(*rslt)); + CALLOC_ARRAY(rslt, cnt); for (list = result, i = 0; list; list = list->next) rslt[i++] = list->item; free_commit_list(result); @@ -541,7 +541,7 @@ struct commit_list *reduce_heads(struct commit_list *heads) p->item->object.flags |= STALE; num_head++; } - array = xcalloc(num_head, sizeof(*array)); + CALLOC_ARRAY(array, num_head); for (p = heads, i = 0; p; p = p->next) { if (p->item->object.flags & STALE) { array[i++] = p->item; @@ -1171,7 +1171,7 @@ static void handle_signed_tag(struct commit *parent, struct commit_extra_header * if (verify_signed_buffer(buf, len, buf + len, size - len, ...)) * warn("warning: signed tag unverified."); */ - mergetag = xcalloc(1, sizeof(*mergetag)); + CALLOC_ARRAY(mergetag, 1); mergetag->key = xstrdup("mergetag"); mergetag->value = buf; mergetag->len = size; @@ -1336,7 +1336,7 @@ static struct commit_extra_header *read_commit_extra_header_lines( excluded_header_field(line, eof - line, exclude)) continue; - it = xcalloc(1, sizeof(*it)); + CALLOC_ARRAY(it, 1); it->key = xmemdupz(line, eof-line); *tail = it; tail = &it->next; @@ -2269,7 +2269,7 @@ static void repo_read_config(struct repository *repo) opts.git_dir = repo->gitdir; if (!repo->config) - repo->config = xcalloc(1, sizeof(struct config_set)); + CALLOC_ARRAY(repo->config, 1); else git_configset_clear(repo->config); diff --git a/contrib/coccinelle/array.cocci b/contrib/coccinelle/array.cocci index 46b8d2ee11..9a4f00cb1b 100644 --- a/contrib/coccinelle/array.cocci +++ b/contrib/coccinelle/array.cocci @@ -88,3 +88,11 @@ expression n; @@ - ptr = xmalloc((n) * sizeof(T)); + ALLOC_ARRAY(ptr, n); + +@@ +type T; +T *ptr; +expression n != 1; +@@ +- ptr = xcalloc(n, \( sizeof(*ptr) \| sizeof(T) \) ) ++ CALLOC_ARRAY(ptr, n) diff --git a/contrib/coccinelle/xcalloc.cocci b/contrib/coccinelle/xcalloc.cocci new file mode 100644 index 0000000000..c291011607 --- /dev/null +++ b/contrib/coccinelle/xcalloc.cocci @@ -0,0 +1,10 @@ +@@ +type T; +T *ptr; +expression n; +@@ + xcalloc( ++ n, + \( sizeof(T) \| sizeof(*ptr) \) +- , n + ) diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash index 7dc6cd8eb8..e1a66954fe 100644 --- a/contrib/completion/git-completion.bash +++ b/contrib/completion/git-completion.bash @@ -3053,7 +3053,7 @@ _git_stash () __gitcomp "--name-status --oneline --patch-with-stat" ;; show,--*) - __gitcomp "$__git_diff_common_options" + __gitcomp "--include-untracked --only-untracked $__git_diff_common_options" ;; branch,--*) ;; @@ -1028,7 +1028,7 @@ static int read_convert_config(const char *var, const char *value, void *cb) if (!strncmp(drv->name, name, namelen) && !drv->name[namelen]) break; if (!drv) { - drv = xcalloc(1, sizeof(struct convert_driver)); + CALLOC_ARRAY(drv, 1); drv->name = xmemdupz(name, namelen); *user_convert_tail = drv; user_convert_tail = &(drv->next); @@ -1456,7 +1456,6 @@ void convert_to_git_filter_fd(const struct index_state *istate, convert_attrs(istate, &ca, path); assert(ca.drv); - assert(ca.drv->clean || ca.drv->process); if (!apply_filter(path, NULL, 0, fd, dst, ca.drv, CAP_CLEAN, NULL, NULL)) die(_("%s: clean filter '%s' failed"), path, ca.drv->name); @@ -840,7 +840,7 @@ static void add_child(struct child_process *cld, struct sockaddr *addr, socklen_ { struct child *newborn, **cradle; - newborn = xcalloc(1, sizeof(*newborn)); + CALLOC_ARRAY(newborn, 1); live_children++; memcpy(&newborn->cld, cld, sizeof(*cld)); memcpy(&newborn->address, addr, addrlen); @@ -1148,7 +1148,7 @@ static int service_loop(struct socketlist *socklist) struct pollfd *pfd; int i; - pfd = xcalloc(socklist->nr, sizeof(struct pollfd)); + CALLOC_ARRAY(pfd, socklist->nr); for (i = 0; i < socklist->nr; i++) { pfd[i].fd = socklist->list[i]; diff --git a/decorate.c b/decorate.c index a605b1b5f4..2036d15967 100644 --- a/decorate.c +++ b/decorate.c @@ -39,7 +39,7 @@ static void grow_decoration(struct decoration *n) struct decoration_entry *old_entries = n->entries; n->size = (old_size + 1000) * 3 / 2; - n->entries = xcalloc(n->size, sizeof(struct decoration_entry)); + CALLOC_ARRAY(n->entries, n->size); n->nr = 0; for (i = 0; i < old_size; i++) { @@ -2233,14 +2233,12 @@ static void init_diff_words_data(struct emit_callback *ecbdata, struct diff_options *o = xmalloc(sizeof(struct diff_options)); memcpy(o, orig_opts, sizeof(struct diff_options)); - ecbdata->diff_words = - xcalloc(1, sizeof(struct diff_words_data)); + CALLOC_ARRAY(ecbdata->diff_words, 1); ecbdata->diff_words->type = o->word_diff; ecbdata->diff_words->opt = o; if (orig_opts->emitted_symbols) - o->emitted_symbols = - xcalloc(1, sizeof(struct emitted_diff_symbols)); + CALLOC_ARRAY(o->emitted_symbols, 1); if (!o->word_regex) o->word_regex = userdiff_word_regex(one, o->repo->index); @@ -2509,7 +2507,7 @@ static struct diffstat_file *diffstat_add(struct diffstat_t *diffstat, const char *name_b) { struct diffstat_file *x; - x = xcalloc(1, sizeof(*x)); + CALLOC_ARRAY(x, 1); ALLOC_GROW(diffstat->files, diffstat->nr + 1, diffstat->alloc); diffstat->files[diffstat->nr++] = x; if (name_b) { @@ -4918,7 +4916,7 @@ static int diff_opt_find_object(const struct option *option, return error(_("unable to resolve '%s'"), arg); if (!opt->objfind) - opt->objfind = xcalloc(1, sizeof(*opt->objfind)); + CALLOC_ARRAY(opt->objfind, 1); opt->pickaxe_opts |= DIFF_PICKAXE_KIND_OBJFIND; opt->flags.recursive = 1; diff --git a/diffcore-rename.c b/diffcore-rename.c index 41558185ae..e2ed648176 100644 --- a/diffcore-rename.c +++ b/diffcore-rename.c @@ -367,6 +367,296 @@ static int find_exact_renames(struct diff_options *options) return renames; } +struct dir_rename_info { + struct strintmap idx_map; + struct strmap dir_rename_guess; + struct strmap *dir_rename_count; + struct strset *relevant_source_dirs; + unsigned setup; +}; + +static char *get_dirname(const char *filename) +{ + char *slash = strrchr(filename, '/'); + return slash ? xstrndup(filename, slash - filename) : xstrdup(""); +} + +static void dirname_munge(char *filename) +{ + char *slash = strrchr(filename, '/'); + if (!slash) + slash = filename; + *slash = '\0'; +} + +static const char *get_highest_rename_path(struct strintmap *counts) +{ + int highest_count = 0; + const char *highest_destination_dir = NULL; + struct hashmap_iter iter; + struct strmap_entry *entry; + + strintmap_for_each_entry(counts, &iter, entry) { + const char *destination_dir = entry->key; + intptr_t count = (intptr_t)entry->value; + if (count > highest_count) { + highest_count = count; + highest_destination_dir = destination_dir; + } + } + return highest_destination_dir; +} + +static void increment_count(struct dir_rename_info *info, + char *old_dir, + char *new_dir) +{ + struct strintmap *counts; + struct strmap_entry *e; + + /* Get the {new_dirs -> counts} mapping using old_dir */ + e = strmap_get_entry(info->dir_rename_count, old_dir); + if (e) { + counts = e->value; + } else { + counts = xmalloc(sizeof(*counts)); + strintmap_init_with_options(counts, 0, NULL, 1); + strmap_put(info->dir_rename_count, old_dir, counts); + } + + /* Increment the count for new_dir */ + strintmap_incr(counts, new_dir, 1); +} + +static void update_dir_rename_counts(struct dir_rename_info *info, + struct strset *dirs_removed, + const char *oldname, + const char *newname) +{ + char *old_dir = xstrdup(oldname); + char *new_dir = xstrdup(newname); + char new_dir_first_char = new_dir[0]; + int first_time_in_loop = 1; + + if (!info->setup) + /* + * info->setup is 0 here in two cases: (1) all auxiliary + * vars (like dirs_removed) were NULL so + * initialize_dir_rename_info() returned early, or (2) + * either break detection or copy detection are active so + * that we never called initialize_dir_rename_info(). In + * the former case, we don't have enough info to know if + * directories were renamed (because dirs_removed lets us + * know about a necessary prerequisite, namely if they were + * removed), and in the latter, we don't care about + * directory renames or find_basename_matches. + * + * This matters because both basename and inexact matching + * will also call update_dir_rename_counts(). In either of + * the above two cases info->dir_rename_counts will not + * have been properly initialized which prevents us from + * updating it, but in these two cases we don't care about + * dir_rename_counts anyway, so we can just exit early. + */ + return; + + while (1) { + /* Get old_dir, skip if its directory isn't relevant. */ + dirname_munge(old_dir); + if (info->relevant_source_dirs && + !strset_contains(info->relevant_source_dirs, old_dir)) + break; + + /* Get new_dir */ + dirname_munge(new_dir); + + /* + * When renaming + * "a/b/c/d/e/foo.c" -> "a/b/some/thing/else/e/foo.c" + * then this suggests that both + * a/b/c/d/e/ => a/b/some/thing/else/e/ + * a/b/c/d/ => a/b/some/thing/else/ + * so we want to increment counters for both. We do NOT, + * however, also want to suggest that there was the following + * rename: + * a/b/c/ => a/b/some/thing/ + * so we need to quit at that point. + * + * Note the when first_time_in_loop, we only strip off the + * basename, and we don't care if that's different. + */ + if (!first_time_in_loop) { + char *old_sub_dir = strchr(old_dir, '\0')+1; + char *new_sub_dir = strchr(new_dir, '\0')+1; + if (!*new_dir) { + /* + * Special case when renaming to root directory, + * i.e. when new_dir == "". In this case, we had + * something like + * a/b/subdir => subdir + * and so dirname_munge() sets things up so that + * old_dir = "a/b\0subdir\0" + * new_dir = "\0ubdir\0" + * We didn't have a '/' to overwrite a '\0' onto + * in new_dir, so we have to compare differently. + */ + if (new_dir_first_char != old_sub_dir[0] || + strcmp(old_sub_dir+1, new_sub_dir)) + break; + } else { + if (strcmp(old_sub_dir, new_sub_dir)) + break; + } + } + + if (strset_contains(dirs_removed, old_dir)) + increment_count(info, old_dir, new_dir); + else + break; + + /* If we hit toplevel directory ("") for old or new dir, quit */ + if (!*old_dir || !*new_dir) + break; + + first_time_in_loop = 0; + } + + /* Free resources we don't need anymore */ + free(old_dir); + free(new_dir); +} + +static void initialize_dir_rename_info(struct dir_rename_info *info, + struct strset *dirs_removed, + struct strmap *dir_rename_count) +{ + struct hashmap_iter iter; + struct strmap_entry *entry; + int i; + + if (!dirs_removed) { + info->setup = 0; + return; + } + info->setup = 1; + + info->dir_rename_count = dir_rename_count; + if (!info->dir_rename_count) { + info->dir_rename_count = xmalloc(sizeof(*dir_rename_count)); + strmap_init(info->dir_rename_count); + } + strintmap_init_with_options(&info->idx_map, -1, NULL, 0); + strmap_init_with_options(&info->dir_rename_guess, NULL, 0); + + /* Setup info->relevant_source_dirs */ + info->relevant_source_dirs = dirs_removed; + + /* + * Loop setting up both info->idx_map, and doing setup of + * info->dir_rename_count. + */ + for (i = 0; i < rename_dst_nr; ++i) { + /* + * For non-renamed files, make idx_map contain mapping of + * filename -> index (index within rename_dst, that is) + */ + if (!rename_dst[i].is_rename) { + char *filename = rename_dst[i].p->two->path; + strintmap_set(&info->idx_map, filename, i); + continue; + } + + /* + * For everything else (i.e. renamed files), make + * dir_rename_count contain a map of a map: + * old_directory -> {new_directory -> count} + * In other words, for every pair look at the directories for + * the old filename and the new filename and count how many + * times that pairing occurs. + */ + update_dir_rename_counts(info, dirs_removed, + rename_dst[i].p->one->path, + rename_dst[i].p->two->path); + } + + /* + * Now we collapse + * dir_rename_count: old_directory -> {new_directory -> count} + * down to + * dir_rename_guess: old_directory -> best_new_directory + * where best_new_directory is the one with the highest count. + */ + strmap_for_each_entry(info->dir_rename_count, &iter, entry) { + /* entry->key is source_dir */ + struct strintmap *counts = entry->value; + char *best_newdir; + + best_newdir = xstrdup(get_highest_rename_path(counts)); + strmap_put(&info->dir_rename_guess, entry->key, + best_newdir); + } +} + +void partial_clear_dir_rename_count(struct strmap *dir_rename_count) +{ + struct hashmap_iter iter; + struct strmap_entry *entry; + + strmap_for_each_entry(dir_rename_count, &iter, entry) { + struct strintmap *counts = entry->value; + strintmap_clear(counts); + } + strmap_partial_clear(dir_rename_count, 1); +} + +static void cleanup_dir_rename_info(struct dir_rename_info *info, + struct strset *dirs_removed, + int keep_dir_rename_count) +{ + struct hashmap_iter iter; + struct strmap_entry *entry; + struct string_list to_remove = STRING_LIST_INIT_NODUP; + int i; + + if (!info->setup) + return; + + /* idx_map */ + strintmap_clear(&info->idx_map); + + /* dir_rename_guess */ + strmap_clear(&info->dir_rename_guess, 1); + + /* dir_rename_count */ + if (!keep_dir_rename_count) { + partial_clear_dir_rename_count(info->dir_rename_count); + strmap_clear(info->dir_rename_count, 1); + FREE_AND_NULL(info->dir_rename_count); + return; + } + + /* + * Although dir_rename_count was passed in + * diffcore_rename_extended() and we want to keep it around and + * return it to that caller, we first want to remove any data + * associated with directories that weren't renamed. + */ + strmap_for_each_entry(info->dir_rename_count, &iter, entry) { + const char *source_dir = entry->key; + struct strintmap *counts = entry->value; + + if (!strset_contains(dirs_removed, source_dir)) { + string_list_append(&to_remove, source_dir); + strintmap_clear(counts); + continue; + } + } + for (i = 0; i < to_remove.nr; ++i) + strmap_remove(info->dir_rename_count, + to_remove.items[i].string, 1); + string_list_clear(&to_remove, 0); +} + static const char *get_basename(const char *filename) { /* @@ -379,8 +669,87 @@ static const char *get_basename(const char *filename) return base ? base + 1 : filename; } +static int idx_possible_rename(char *filename, struct dir_rename_info *info) +{ + /* + * Our comparison of files with the same basename (see + * find_basename_matches() below), is only helpful when after exact + * rename detection we have exactly one file with a given basename + * among the rename sources and also only exactly one file with + * that basename among the rename destinations. When we have + * multiple files with the same basename in either set, we do not + * know which to compare against. However, there are some + * filenames that occur in large numbers (particularly + * build-related filenames such as 'Makefile', '.gitignore', or + * 'build.gradle' that potentially exist within every single + * subdirectory), and for performance we want to be able to quickly + * find renames for these files too. + * + * The reason basename comparisons are a useful heuristic was that it + * is common for people to move files across directories while keeping + * their filename the same. If we had a way of determining or even + * making a good educated guess about which directory these non-unique + * basename files had moved the file to, we could check it. + * Luckily... + * + * When an entire directory is in fact renamed, we have two factors + * helping us out: + * (a) the original directory disappeared giving us a hint + * about when we can apply an extra heuristic. + * (a) we often have several files within that directory and + * subdirectories that are renamed without changes + * So, rules for a heuristic: + * (0) If there basename matches are non-unique (the condition under + * which this function is called) AND + * (1) the directory in which the file was found has disappeared + * (i.e. dirs_removed is non-NULL and has a relevant entry) THEN + * (2) use exact renames of files within the directory to determine + * where the directory is likely to have been renamed to. IF + * there is at least one exact rename from within that + * directory, we can proceed. + * (3) If there are multiple places the directory could have been + * renamed to based on exact renames, ignore all but one of them. + * Just use the destination with the most renames going to it. + * (4) Check if applying that directory rename to the original file + * would result in a destination filename that is in the + * potential rename set. If so, return the index of the + * destination file (the index within rename_dst). + * (5) Compare the original file and returned destination for + * similarity, and if they are sufficiently similar, record the + * rename. + * + * This function, idx_possible_rename(), is only responsible for (4). + * The conditions/steps in (1)-(3) are handled via setting up + * dir_rename_count and dir_rename_guess in + * initialize_dir_rename_info(). Steps (0) and (5) are handled by + * the caller of this function. + */ + char *old_dir, *new_dir; + struct strbuf new_path = STRBUF_INIT; + int idx; + + if (!info->setup) + return -1; + + old_dir = get_dirname(filename); + new_dir = strmap_get(&info->dir_rename_guess, old_dir); + free(old_dir); + if (!new_dir) + return -1; + + strbuf_addstr(&new_path, new_dir); + strbuf_addch(&new_path, '/'); + strbuf_addstr(&new_path, get_basename(filename)); + + idx = strintmap_get(&info->idx_map, new_path.buf); + strbuf_release(&new_path); + return idx; +} + static int find_basename_matches(struct diff_options *options, - int minimum_score) + int minimum_score, + struct dir_rename_info *info, + struct strset *dirs_removed) { /* * When I checked in early 2020, over 76% of file renames in linux @@ -415,8 +784,6 @@ static int find_basename_matches(struct diff_options *options, int i, renames = 0; struct strintmap sources; struct strintmap dests; - struct hashmap_iter iter; - struct strmap_entry *entry; /* * The prefeteching stuff wants to know if it can skip prefetching @@ -466,17 +833,39 @@ static int find_basename_matches(struct diff_options *options, } /* Now look for basename matchups and do similarity estimation */ - strintmap_for_each_entry(&sources, &iter, entry) { - const char *base = entry->key; - intptr_t src_index = (intptr_t)entry->value; + for (i = 0; i < rename_src_nr; ++i) { + char *filename = rename_src[i].p->one->path; + const char *base = NULL; + intptr_t src_index; intptr_t dst_index; - if (src_index == -1) - continue; - if (0 <= (dst_index = strintmap_get(&dests, base))) { + /* + * If the basename is unique among remaining sources, then + * src_index will equal 'i' and we can attempt to match it + * to a unique basename in the destinations. Otherwise, + * use directory rename heuristics, if possible. + */ + base = get_basename(filename); + src_index = strintmap_get(&sources, base); + assert(src_index == -1 || src_index == i); + + if (strintmap_contains(&dests, base)) { struct diff_filespec *one, *two; int score; + /* Find a matching destination, if possible */ + dst_index = strintmap_get(&dests, base); + if (src_index == -1 || dst_index == -1) { + src_index = i; + dst_index = idx_possible_rename(filename, info); + } + if (dst_index == -1) + continue; + + /* Ignore this dest if already used in a rename */ + if (rename_dst[dst_index].is_rename) + continue; /* already used previously */ + /* Estimate the similarity */ one = rename_src[src_index].p->one; two = rename_dst[dst_index].p->two; @@ -488,6 +877,8 @@ static int find_basename_matches(struct diff_options *options, continue; record_rename_pair(dst_index, src_index, score); renames++; + update_dir_rename_counts(info, dirs_removed, + one->path, two->path); /* * Found a rename so don't need text anymore; if we @@ -571,7 +962,12 @@ static int too_many_rename_candidates(int num_destinations, int num_sources, return 1; } -static int find_renames(struct diff_score *mx, int dst_cnt, int minimum_score, int copies) +static int find_renames(struct diff_score *mx, + int dst_cnt, + int minimum_score, + int copies, + struct dir_rename_info *info, + struct strset *dirs_removed) { int count = 0, i; @@ -588,6 +984,9 @@ static int find_renames(struct diff_score *mx, int dst_cnt, int minimum_score, i continue; record_rename_pair(mx[i].dst, mx[i].src, mx[i].score); count++; + update_dir_rename_counts(info, dirs_removed, + rename_src[mx[i].src].p->one->path, + rename_dst[mx[i].dst].p->two->path); } return count; } @@ -640,7 +1039,9 @@ static void remove_unneeded_paths_from_src(int detecting_copies) rename_src_nr = new_num_src; } -void diffcore_rename(struct diff_options *options) +void diffcore_rename_extended(struct diff_options *options, + struct strset *dirs_removed, + struct strmap *dir_rename_count) { int detect_rename = options->detect_rename; int minimum_score = options->rename_score; @@ -651,9 +1052,14 @@ void diffcore_rename(struct diff_options *options) int num_destinations, dst_cnt; int num_sources, want_copies; struct progress *progress = NULL; + struct dir_rename_info info; trace2_region_enter("diff", "setup", options->repo); + info.setup = 0; + assert(!dir_rename_count || strmap_empty(dir_rename_count)); want_copies = (detect_rename == DIFF_DETECT_COPY); + if (dirs_removed && (break_idx || want_copies)) + BUG("dirs_removed incompatible with break/copy detection"); if (!minimum_score) minimum_score = DEFAULT_RENAME_SCORE; @@ -745,10 +1151,17 @@ void diffcore_rename(struct diff_options *options) remove_unneeded_paths_from_src(want_copies); trace2_region_leave("diff", "cull after exact", options->repo); + /* Preparation for basename-driven matching. */ + trace2_region_enter("diff", "dir rename setup", options->repo); + initialize_dir_rename_info(&info, + dirs_removed, dir_rename_count); + trace2_region_leave("diff", "dir rename setup", options->repo); + /* Utilize file basenames to quickly find renames. */ trace2_region_enter("diff", "basename matches", options->repo); rename_count += find_basename_matches(options, - min_basename_score); + min_basename_score, + &info, dirs_removed); trace2_region_leave("diff", "basename matches", options->repo); /* @@ -787,8 +1200,7 @@ void diffcore_rename(struct diff_options *options) (uint64_t)num_destinations * (uint64_t)num_sources); } - mx = xcalloc(st_mult(NUM_CANDIDATE_PER_DST, num_destinations), - sizeof(*mx)); + CALLOC_ARRAY(mx, st_mult(NUM_CANDIDATE_PER_DST, num_destinations)); for (dst_cnt = i = 0; i < rename_dst_nr; i++) { struct diff_filespec *two = rename_dst[i].p->two; struct diff_score *m; @@ -834,9 +1246,11 @@ void diffcore_rename(struct diff_options *options) /* cost matrix sorted by most to least similar pair */ STABLE_QSORT(mx, dst_cnt * NUM_CANDIDATE_PER_DST, score_compare); - rename_count += find_renames(mx, dst_cnt, minimum_score, 0); + rename_count += find_renames(mx, dst_cnt, minimum_score, 0, + &info, dirs_removed); if (want_copies) - rename_count += find_renames(mx, dst_cnt, minimum_score, 1); + rename_count += find_renames(mx, dst_cnt, minimum_score, 1, + &info, dirs_removed); free(mx); trace2_region_leave("diff", "inexact renames", options->repo); @@ -912,6 +1326,7 @@ void diffcore_rename(struct diff_options *options) if (rename_dst[i].filespec_to_free) free_filespec(rename_dst[i].filespec_to_free); + cleanup_dir_rename_info(&info, dirs_removed, dir_rename_count != NULL); FREE_AND_NULL(rename_dst); rename_dst_nr = rename_dst_alloc = 0; FREE_AND_NULL(rename_src); @@ -923,3 +1338,8 @@ void diffcore_rename(struct diff_options *options) trace2_region_leave("diff", "write back to queue", options->repo); return; } + +void diffcore_rename(struct diff_options *options) +{ + diffcore_rename_extended(options, NULL, NULL); +} diff --git a/diffcore.h b/diffcore.h index c1592bcd01..b9a230ab7f 100644 --- a/diffcore.h +++ b/diffcore.h @@ -8,6 +8,8 @@ struct diff_options; struct repository; +struct strmap; +struct strset; struct userdiff_driver; /* This header file is internal between diff.c and its diff transformers @@ -159,8 +161,13 @@ struct diff_filepair *diff_queue(struct diff_queue_struct *, struct diff_filespec *); void diff_q(struct diff_queue_struct *, struct diff_filepair *); +void partial_clear_dir_rename_count(struct strmap *dir_rename_count); + void diffcore_break(struct repository *, int); void diffcore_rename(struct diff_options *); +void diffcore_rename_extended(struct diff_options *options, + struct strset *dirs_removed, + struct strmap *dir_rename_count); void diffcore_merge_broken(void); void diffcore_pickaxe(struct diff_options *); void diffcore_order(const char *orderfile); @@ -1035,6 +1035,9 @@ static int add_patterns_from_buffer(char *buf, size_t size, const char *base, int baselen, struct pattern_list *pl); +/* Flags for add_patterns() */ +#define PATTERN_NOFOLLOW (1<<0) + /* * Given a file with name "fname", read it (either from disk, or from * an index if 'istate' is non-null), parse it and store the @@ -1046,7 +1049,7 @@ static int add_patterns_from_buffer(char *buf, size_t size, */ static int add_patterns(const char *fname, const char *base, int baselen, struct pattern_list *pl, struct index_state *istate, - struct oid_stat *oid_stat) + unsigned flags, struct oid_stat *oid_stat) { struct stat st; int r; @@ -1054,7 +1057,11 @@ static int add_patterns(const char *fname, const char *base, int baselen, size_t size = 0; char *buf; - fd = open(fname, O_RDONLY); + if (flags & PATTERN_NOFOLLOW) + fd = open_nofollow(fname, O_RDONLY); + else + fd = open(fname, O_RDONLY); + if (fd < 0 || fstat(fd, &st) < 0) { if (fd < 0) warn_on_fopen_errors(fname); @@ -1143,9 +1150,10 @@ static int add_patterns_from_buffer(char *buf, size_t size, int add_patterns_from_file_to_list(const char *fname, const char *base, int baselen, struct pattern_list *pl, - struct index_state *istate) + struct index_state *istate, + unsigned flags) { - return add_patterns(fname, base, baselen, pl, istate, NULL); + return add_patterns(fname, base, baselen, pl, istate, flags, NULL); } int add_patterns_from_blob_to_list( @@ -1194,7 +1202,7 @@ static void add_patterns_from_file_1(struct dir_struct *dir, const char *fname, if (!dir->untracked) dir->unmanaged_exclude_files++; pl = add_pattern_list(dir, EXC_FILE, fname); - if (add_patterns(fname, "", 0, pl, NULL, oid_stat) < 0) + if (add_patterns(fname, "", 0, pl, NULL, 0, oid_stat) < 0) die(_("cannot use %s as an exclude file"), fname); } @@ -1488,7 +1496,7 @@ static void prep_exclude(struct dir_struct *dir, const char *cp; struct oid_stat oid_stat; - stk = xcalloc(1, sizeof(*stk)); + CALLOC_ARRAY(stk, 1); if (current < 0) { cp = base; current = 0; @@ -1558,6 +1566,7 @@ static void prep_exclude(struct dir_struct *dir, strbuf_addstr(&sb, dir->exclude_per_dir); pl->src = strbuf_detach(&sb, NULL); add_patterns(pl->src, pl->src, stk->baselen, pl, istate, + PATTERN_NOFOLLOW, untracked ? &oid_stat : NULL); } /* @@ -3006,7 +3015,7 @@ int get_sparse_checkout_patterns(struct pattern_list *pl) char *sparse_filename = get_sparse_checkout_filename(); pl->use_cone_patterns = core_sparse_checkout_cone; - res = add_patterns_from_file_to_list(sparse_filename, "", 0, pl, NULL); + res = add_patterns_from_file_to_list(sparse_filename, "", 0, pl, NULL, 0); free(sparse_filename); return res; @@ -3162,7 +3171,7 @@ void write_untracked_extension(struct strbuf *out, struct untracked_cache *untra int varint_len; const unsigned hashsz = the_hash_algo->rawsz; - ouc = xcalloc(1, sizeof(*ouc)); + CALLOC_ARRAY(ouc, 1); stat_data_to_disk(&ouc->info_exclude_stat, &untracked->ss_info_exclude.stat); stat_data_to_disk(&ouc->excludes_file_stat, &untracked->ss_excludes_file.stat); ouc->dir_flags = htonl(untracked->dir_flags); @@ -3373,7 +3382,7 @@ struct untracked_cache *read_untracked_extension(const void *data, unsigned long if (next + exclude_per_dir_offset + 1 > end) return NULL; - uc = xcalloc(1, sizeof(*uc)); + CALLOC_ARRAY(uc, 1); strbuf_init(&uc->ident, ident_len); strbuf_add(&uc->ident, ident, ident_len); load_oid_stat(&uc->ss_info_exclude, @@ -420,7 +420,8 @@ int hashmap_contains_parent(struct hashmap *map, struct pattern_list *add_pattern_list(struct dir_struct *dir, int group_type, const char *src); int add_patterns_from_file_to_list(const char *fname, const char *base, int baselen, - struct pattern_list *pl, struct index_state *istate); + struct pattern_list *pl, struct index_state *istate, + unsigned flags); void add_patterns_from_file(struct dir_struct *, const char *fname); int add_patterns_from_blob_to_list(struct object_id *oid, const char *base, int baselen, diff --git a/ewah/bitmap.c b/ewah/bitmap.c index 0d31cdc866..38a47c44db 100644 --- a/ewah/bitmap.c +++ b/ewah/bitmap.c @@ -25,7 +25,7 @@ struct bitmap *bitmap_word_alloc(size_t word_alloc) { struct bitmap *bitmap = xmalloc(sizeof(struct bitmap)); - bitmap->words = xcalloc(word_alloc, sizeof(eword_t)); + CALLOC_ARRAY(bitmap->words, word_alloc); bitmap->word_alloc = word_alloc; return bitmap; } diff --git a/fetch-pack.c b/fetch-pack.c index 6a61a46428..fb04a76ca2 100644 --- a/fetch-pack.c +++ b/fetch-pack.c @@ -1916,7 +1916,7 @@ static void update_shallow(struct fetch_pack_args *args, * remote is also shallow, check what ref is safe to update * without updating .git/shallow */ - status = xcalloc(nr_sought, sizeof(*status)); + CALLOC_ARRAY(status, nr_sought); assign_shallow_commits_to_refs(si, NULL, status); if (si->nr_ours || si->nr_theirs) { for (i = 0; i < nr_sought; i++) diff --git a/fmt-merge-msg.c b/fmt-merge-msg.c index 1e51492a05..0f66818e0f 100644 --- a/fmt-merge-msg.c +++ b/fmt-merge-msg.c @@ -130,7 +130,7 @@ static int handle_line(char *line, struct merge_parents *merge_parents) if (!find_merge_parent(merge_parents, &oid, NULL)) return 0; /* subsumed by other parents */ - origin_data = xcalloc(1, sizeof(struct origin_data)); + CALLOC_ARRAY(origin_data, 1); oidcpy(&origin_data->oid, &oid); if (line[len - 1] == '\n') diff --git a/fsmonitor.c b/fsmonitor.c index 23f8a0c97e..ab9bfc60b3 100644 --- a/fsmonitor.c +++ b/fsmonitor.c @@ -185,10 +185,10 @@ static int query_fsmonitor(int version, const char *last_update, struct strbuf * int fsmonitor_is_trivial_response(const struct strbuf *query_result) { static char trivial_response[3] = { '\0', '/', '\0' }; - int is_trivial = !memcmp(trivial_response, - &query_result->buf[query_result->len - 3], 3); - return is_trivial; + return query_result->len >= 3 && + !memcmp(trivial_response, + &query_result->buf[query_result->len - 3], 3); } static void fsmonitor_refresh_callback(struct index_state *istate, char *name) diff --git a/git-compat-util.h b/git-compat-util.h index 3b2738c73d..9ddf9d7044 100644 --- a/git-compat-util.h +++ b/git-compat-util.h @@ -898,7 +898,7 @@ int xstrncmpz(const char *s, const char *t, size_t len); #define FREE_AND_NULL(p) do { free(p); (p) = NULL; } while (0) #define ALLOC_ARRAY(x, alloc) (x) = xmalloc(st_mult(sizeof(*(x)), (alloc))) -#define CALLOC_ARRAY(x, alloc) (x) = xcalloc((alloc), sizeof(*(x))); +#define CALLOC_ARRAY(x, alloc) (x) = xcalloc((alloc), sizeof(*(x))) #define REALLOC_ARRAY(x, alloc) (x) = xrealloc((x), st_mult(sizeof(*(x)), (alloc))) #define COPY_ARRAY(dst, src, n) copy_array((dst), (src), (n), sizeof(*(dst)) + \ @@ -1242,6 +1242,13 @@ int access_or_die(const char *path, int mode, unsigned flag); /* Warn on an inaccessible file if errno indicates this is an error */ int warn_on_fopen_errors(const char *path); +/* + * Open with O_NOFOLLOW, or equivalent. Note that the fallback equivalent + * may be racy. Do not use this as protection against an attacker who can + * simultaneously create paths. + */ +int open_nofollow(const char *path, int flags); + #if !defined(USE_PARENS_AROUND_GETTEXT_N) && defined(__GNUC__) #define USE_PARENS_AROUND_GETTEXT_N 1 #endif diff --git a/git-filter-branch.sh b/git-filter-branch.sh index fea7964617..cb89372813 100755 --- a/git-filter-branch.sh +++ b/git-filter-branch.sh @@ -492,14 +492,12 @@ then sha1=$(git rev-parse "$ref"^0) test -f "$workdir"/../map/$sha1 && continue ancestor=$(git rev-list --simplify-merges -1 "$ref" "$@") - test "$ancestor" && echo $(map $ancestor) >> "$workdir"/../map/$sha1 + test "$ancestor" && echo $(map $ancestor) >"$workdir"/../map/$sha1 done < "$tempdir"/heads fi # Finally update the refs -_x40='[0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f]' -_x40="$_x40$_x40$_x40$_x40$_x40$_x40$_x40$_x40" echo while read ref do @@ -519,7 +517,7 @@ do git update-ref -m "filter-branch: delete" -d "$ref" $sha1 || die "Could not delete $ref" ;; - $_x40) + *) echo "Ref '$ref' was rewritten" if ! git update-ref -m "filter-branch: rewrite" \ "$ref" $rewritten $sha1 2>/dev/null; then @@ -533,16 +531,6 @@ do fi fi ;; - *) - # NEEDSWORK: possibly add -Werror, making this an error - warn "WARNING: '$ref' was rewritten into multiple commits:" - warn "$rewritten" - warn "WARNING: Ref '$ref' points to the first one now." - rewritten=$(echo "$rewritten" | head -n 1) - git update-ref -m "filter-branch: rewrite to first" \ - "$ref" $rewritten $sha1 || - die "Could not rewrite $ref" - ;; esac git update-ref -m "filter-branch: backup" "$orig_namespace$ref" $sha1 || exit @@ -40,20 +40,6 @@ static struct grep_opt grep_defaults = { .output = std_output, }; -#ifdef USE_LIBPCRE2 -static pcre2_general_context *pcre2_global_context; - -static void *pcre2_malloc(PCRE2_SIZE size, MAYBE_UNUSED void *memory_data) -{ - return malloc(size); -} - -static void pcre2_free(void *pointer, MAYBE_UNUSED void *memory_data) -{ - free(pointer); -} -#endif - static const char *color_grep_slots[] = { [GREP_COLOR_CONTEXT] = "context", [GREP_COLOR_FILENAME] = "filename", @@ -152,20 +138,9 @@ int grep_config(const char *var, const char *value, void *cb) * Initialize one instance of grep_opt and copy the * default values from the template we read the configuration * information in an earlier call to git_config(grep_config). - * - * If using PCRE, make sure that the library is configured - * to use the same allocator as Git (e.g. nedmalloc on Windows). - * - * Any allocated memory needs to be released in grep_destroy(). */ void grep_init(struct grep_opt *opt, struct repository *repo, const char *prefix) { -#if defined(USE_LIBPCRE2) - if (!pcre2_global_context) - pcre2_global_context = pcre2_general_context_create( - pcre2_malloc, pcre2_free, NULL); -#endif - *opt = grep_defaults; opt->repo = repo; @@ -175,13 +150,6 @@ void grep_init(struct grep_opt *opt, struct repository *repo, const char *prefix opt->header_tail = &opt->header_list; } -void grep_destroy(void) -{ -#ifdef USE_LIBPCRE2 - pcre2_general_context_free(pcre2_global_context); -#endif -} - static void grep_set_pattern_type_option(enum grep_pattern_type pattern_type, struct grep_opt *opt) { /* @@ -363,6 +331,28 @@ static int is_fixed(const char *s, size_t len) } #ifdef USE_LIBPCRE2 +#define GREP_PCRE2_DEBUG_MALLOC 0 + +static void *pcre2_malloc(PCRE2_SIZE size, MAYBE_UNUSED void *memory_data) +{ + void *pointer = malloc(size); +#if GREP_PCRE2_DEBUG_MALLOC + static int count = 1; + fprintf(stderr, "PCRE2:%p -> #%02d: alloc(%lu)\n", pointer, count++, size); +#endif + return pointer; +} + +static void pcre2_free(void *pointer, MAYBE_UNUSED void *memory_data) +{ +#if GREP_PCRE2_DEBUG_MALLOC + static int count = 1; + if (pointer) + fprintf(stderr, "PCRE2:%p -> #%02d: free()\n", pointer, count++); +#endif + free(pointer); +} + static void compile_pcre2_pattern(struct grep_pat *p, const struct grep_opt *opt) { int error; @@ -373,17 +363,20 @@ static void compile_pcre2_pattern(struct grep_pat *p, const struct grep_opt *opt int patinforet; size_t jitsizearg; - assert(opt->pcre2); - - p->pcre2_compile_context = NULL; + /* + * Call pcre2_general_context_create() before calling any + * other pcre2_*(). It sets up our malloc()/free() functions + * with which everything else is allocated. + */ + p->pcre2_general_context = pcre2_general_context_create( + pcre2_malloc, pcre2_free, NULL); + if (!p->pcre2_general_context) + die("Couldn't allocate PCRE2 general context"); - /* pcre2_global_context is initialized in append_grep_pattern */ if (opt->ignore_case) { if (!opt->ignore_locale && has_non_ascii(p->pattern)) { - if (!pcre2_global_context) - BUG("pcre2_global_context uninitialized"); - p->pcre2_tables = pcre2_maketables(pcre2_global_context); - p->pcre2_compile_context = pcre2_compile_context_create(NULL); + p->pcre2_tables = pcre2_maketables(p->pcre2_general_context); + p->pcre2_compile_context = pcre2_compile_context_create(p->pcre2_general_context); pcre2_set_character_tables(p->pcre2_compile_context, p->pcre2_tables); } @@ -393,28 +386,18 @@ static void compile_pcre2_pattern(struct grep_pat *p, const struct grep_opt *opt !(!opt->ignore_case && (p->fixed || p->is_fixed))) options |= (PCRE2_UTF | PCRE2_MATCH_INVALID_UTF); +#ifdef GIT_PCRE2_VERSION_10_36_OR_HIGHER /* Work around https://bugs.exim.org/show_bug.cgi?id=2642 fixed in 10.36 */ - if (PCRE2_MATCH_INVALID_UTF && options & (PCRE2_UTF | PCRE2_CASELESS)) { - struct strbuf buf; - int len; - int err; - - if ((len = pcre2_config(PCRE2_CONFIG_VERSION, NULL)) < 0) - BUG("pcre2_config(..., NULL) failed: %d", len); - strbuf_init(&buf, len + 1); - if ((err = pcre2_config(PCRE2_CONFIG_VERSION, buf.buf)) < 0) - BUG("pcre2_config(..., buf.buf) failed: %d", err); - if (versioncmp(buf.buf, "10.36") < 0) - options |= PCRE2_NO_START_OPTIMIZE; - strbuf_release(&buf); - } + if (PCRE2_MATCH_INVALID_UTF && options & (PCRE2_UTF | PCRE2_CASELESS)) + options |= PCRE2_NO_START_OPTIMIZE; +#endif p->pcre2_pattern = pcre2_compile((PCRE2_SPTR)p->pattern, p->patternlen, options, &error, &erroffset, p->pcre2_compile_context); if (p->pcre2_pattern) { - p->pcre2_match_data = pcre2_match_data_create_from_pattern(p->pcre2_pattern, NULL); + p->pcre2_match_data = pcre2_match_data_create_from_pattern(p->pcre2_pattern, p->pcre2_general_context); if (!p->pcre2_match_data) die("Couldn't allocate PCRE2 match data"); } else { @@ -493,7 +476,12 @@ static void free_pcre2_pattern(struct grep_pat *p) pcre2_compile_context_free(p->pcre2_compile_context); pcre2_code_free(p->pcre2_pattern); pcre2_match_data_free(p->pcre2_match_data); +#ifdef GIT_PCRE2_VERSION_10_34_OR_HIGHER + pcre2_maketables_free(p->pcre2_general_context, p->pcre2_tables); +#else free((void *)p->pcre2_tables); +#endif + pcre2_general_context_free(p->pcre2_general_context); } #else /* !USE_LIBPCRE2 */ static void compile_pcre2_pattern(struct grep_pat *p, const struct grep_opt *opt) @@ -555,7 +543,6 @@ static void compile_regexp(struct grep_pat *p, struct grep_opt *opt) #endif if (p->fixed || p->is_fixed) { #ifdef USE_LIBPCRE2 - opt->pcre2 = 1; if (p->is_fixed) { compile_pcre2_pattern(p, opt); } else { @@ -621,7 +608,7 @@ static struct grep_expr *compile_pattern_atom(struct grep_pat **list) case GREP_PATTERN: /* atom */ case GREP_PATTERN_HEAD: case GREP_PATTERN_BODY: - x = xcalloc(1, sizeof (struct grep_expr)); + CALLOC_ARRAY(x, 1); x->node = GREP_NODE_ATOM; x->u.atom = p; *list = p->next; @@ -651,7 +638,7 @@ static struct grep_expr *compile_pattern_not(struct grep_pat **list) if (!p->next) die("--not not followed by pattern expression"); *list = p->next; - x = xcalloc(1, sizeof (struct grep_expr)); + CALLOC_ARRAY(x, 1); x->node = GREP_NODE_NOT; x->u.unary = compile_pattern_not(list); if (!x->u.unary) @@ -676,7 +663,7 @@ static struct grep_expr *compile_pattern_and(struct grep_pat **list) y = compile_pattern_and(list); if (!y) die("--and not followed by pattern expression"); - z = xcalloc(1, sizeof (struct grep_expr)); + CALLOC_ARRAY(z, 1); z->node = GREP_NODE_AND; z->u.binary.left = x; z->u.binary.right = y; @@ -696,7 +683,7 @@ static struct grep_expr *compile_pattern_or(struct grep_pat **list) y = compile_pattern_or(list); if (!y) die("not a pattern expression %s", p->pattern); - z = xcalloc(1, sizeof (struct grep_expr)); + CALLOC_ARRAY(z, 1); z->node = GREP_NODE_OR; z->u.binary.left = x; z->u.binary.right = y; @@ -4,10 +4,17 @@ #ifdef USE_LIBPCRE2 #define PCRE2_CODE_UNIT_WIDTH 8 #include <pcre2.h> +#if (PCRE2_MAJOR >= 10 && PCRE2_MINOR >= 36) || PCRE2_MAJOR >= 11 +#define GIT_PCRE2_VERSION_10_36_OR_HIGHER +#endif +#if (PCRE2_MAJOR >= 10 && PCRE2_MINOR >= 34) || PCRE2_MAJOR >= 11 +#define GIT_PCRE2_VERSION_10_34_OR_HIGHER +#endif #else typedef int pcre2_code; typedef int pcre2_match_data; typedef int pcre2_compile_context; +typedef int pcre2_general_context; #endif #ifndef PCRE2_MATCH_INVALID_UTF /* PCRE2_MATCH_* dummy also with !USE_LIBPCRE2, for test-pcre2-config.c */ @@ -69,6 +76,7 @@ struct grep_pat { pcre2_code *pcre2_pattern; pcre2_match_data *pcre2_match_data; pcre2_compile_context *pcre2_compile_context; + pcre2_general_context *pcre2_general_context; const uint8_t *pcre2_tables; uint32_t pcre2_jit_on; unsigned fixed:1; @@ -161,7 +169,6 @@ struct grep_opt { int grep_config(const char *var, const char *value, void *); void grep_init(struct grep_opt *, struct repository *repo, const char *prefix); -void grep_destroy(void); void grep_commit_pattern_type(enum grep_pattern_type, struct grep_opt *opt); void append_grep_pat(struct grep_opt *opt, const char *pat, size_t patlen, const char *origin, int no, enum grep_pat_token t); @@ -76,7 +76,7 @@ unsigned int memihash_cont(unsigned int hash_seed, const void *buf, size_t len) static void alloc_table(struct hashmap *map, unsigned int size) { map->tablesize = size; - map->table = xcalloc(size, sizeof(struct hashmap_entry *)); + CALLOC_ARRAY(map->table, size); /* calculate resize thresholds for new size */ map->grow_at = (unsigned int) ((uint64_t) size * HASHMAP_LOAD_FACTOR / 100); diff --git a/http-backend.c b/http-backend.c index a03b4bae22..b329bf63f0 100644 --- a/http-backend.c +++ b/http-backend.c @@ -39,7 +39,7 @@ static struct string_list *get_parameters(void) if (!query_params) { const char *query = getenv("QUERY_STRING"); - query_params = xcalloc(1, sizeof(*query_params)); + CALLOC_ARRAY(query_params, 1); while (query && *query) { char *name = url_decode_parameter_name(&query); char *value = url_decode_parameter_value(&query); diff --git a/http-push.c b/http-push.c index 6a4a43e07f..b60d5fcc85 100644 --- a/http-push.c +++ b/http-push.c @@ -896,7 +896,7 @@ static struct remote_lock *lock_remote(const char *path, long timeout) curl_easy_setopt(slot->curl, CURLOPT_HTTPHEADER, dav_headers); curl_easy_setopt(slot->curl, CURLOPT_FILE, &in_buffer); - lock = xcalloc(1, sizeof(*lock)); + CALLOC_ARRAY(lock, 1); lock->timeout = -1; if (start_active_slot(slot)) { @@ -1713,7 +1713,7 @@ int cmd_main(int argc, const char **argv) int new_refs; struct ref *ref, *local_refs; - repo = xcalloc(1, sizeof(*repo)); + CALLOC_ARRAY(repo, 1); argv++; for (i = 1; i < argc; i++, argv++) { @@ -2324,7 +2324,7 @@ struct http_pack_request *new_direct_http_pack_request( off_t prev_posn = 0; struct http_pack_request *preq; - preq = xcalloc(1, sizeof(*preq)); + CALLOC_ARRAY(preq, 1); strbuf_init(&preq->tmpfile, 0); preq->url = url; @@ -2419,7 +2419,7 @@ struct http_object_request *new_http_object_request(const char *base_url, off_t prev_posn = 0; struct http_object_request *freq; - freq = xcalloc(1, sizeof(*freq)); + CALLOC_ARRAY(freq, 1); strbuf_init(&freq->tmpfile, 0); oidcpy(&freq->oid, oid); freq->localfile = -1; diff --git a/imap-send.c b/imap-send.c index d0b94f911e..bb085d66d1 100644 --- a/imap-send.c +++ b/imap-send.c @@ -963,9 +963,9 @@ static struct imap_store *imap_open_store(struct imap_server_conf *srvc, const c char *arg, *rsp; int s = -1, preauth; - ctx = xcalloc(1, sizeof(*ctx)); + CALLOC_ARRAY(ctx, 1); - ctx->imap = imap = xcalloc(1, sizeof(*imap)); + ctx->imap = CALLOC_ARRAY(imap, 1); imap->buf.sock.fd[0] = imap->buf.sock.fd[1] = -1; imap->in_progress_append = &imap->in_progress; diff --git a/line-log.c b/line-log.c index 75c8b1acff..51d93310a4 100644 --- a/line-log.c +++ b/line-log.c @@ -296,7 +296,7 @@ static void line_log_data_insert(struct line_log_data **list, return; } - p = xcalloc(1, sizeof(struct line_log_data)); + CALLOC_ARRAY(p, 1); p->path = path; range_set_append(&p->ranges, begin, end); if (ip) { diff --git a/line-range.c b/line-range.c index 9b50583dc0..955a8a9535 100644 --- a/line-range.c +++ b/line-range.c @@ -202,7 +202,7 @@ static const char *parse_range_funcname( drv = userdiff_find_by_path(istate, path); if (drv && drv->funcname.pattern) { const struct userdiff_funcname *pe = &drv->funcname; - xecfg = xcalloc(1, sizeof(*xecfg)); + CALLOC_ARRAY(xecfg, 1); xdiff_set_find_func(xecfg, pe->pattern, pe->cflags); } diff --git a/list-objects-filter.c b/list-objects-filter.c index 4ec0041cfb..39e2f15333 100644 --- a/list-objects-filter.c +++ b/list-objects-filter.c @@ -186,7 +186,7 @@ static enum list_objects_filter_result filter_trees_depth( seen_info = oidmap_get( &filter_data->seen_at_depth, &obj->oid); if (!seen_info) { - seen_info = xcalloc(1, sizeof(*seen_info)); + CALLOC_ARRAY(seen_info, 1); oidcpy(&seen_info->base.oid, &obj->oid); seen_info->depth = filter_data->current_depth; oidmap_put(&filter_data->seen_at_depth, seen_info); @@ -626,7 +626,7 @@ static void filter_combine__init( size_t sub; d->nr = filter_options->sub_nr; - d->sub = xcalloc(d->nr, sizeof(*d->sub)); + CALLOC_ARRAY(d->sub, d->nr); for (sub = 0; sub < d->nr; sub++) d->sub[sub].filter = list_objects_filter__init( filter->omits ? &d->sub[sub].omits : NULL, @@ -674,7 +674,7 @@ struct filter *list_objects_filter__init( if (!init_fn) return NULL; - filter = xcalloc(1, sizeof(*filter)); + CALLOC_ARRAY(filter, 1); filter->omits = omitted; init_fn(filter_options, filter); return filter; diff --git a/ll-merge.c b/ll-merge.c index 1ec0b959e0..9a8a2c365c 100644 --- a/ll-merge.c +++ b/ll-merge.c @@ -268,7 +268,7 @@ static int read_merge_config(const char *var, const char *value, void *cb) if (!strncmp(fn->name, name, namelen) && !fn->name[namelen]) break; if (!fn) { - fn = xcalloc(1, sizeof(struct ll_merge_driver)); + CALLOC_ARRAY(fn, 1); fn->name = xmemdupz(name, namelen); fn->fn = ll_ext_merge; *ll_user_merge_tail = fn; @@ -83,7 +83,7 @@ static void add_mapping(struct string_list *map, if (item->util) { me = (struct mailmap_entry *)item->util; } else { - me = xcalloc(1, sizeof(struct mailmap_entry)); + CALLOC_ARRAY(me, 1); me->namemap.strdup_strings = 1; me->namemap.cmp = namemap_cmp; item->util = me; @@ -157,20 +157,30 @@ static void read_mailmap_line(struct string_list *map, char *buffer) add_mapping(map, name1, email1, name2, email2); } -static int read_mailmap_file(struct string_list *map, const char *filename) +/* Flags for read_mailmap_file() */ +#define MAILMAP_NOFOLLOW (1<<0) + +static int read_mailmap_file(struct string_list *map, const char *filename, + unsigned flags) { char buffer[1024]; FILE *f; + int fd; if (!filename) return 0; - f = fopen(filename, "r"); - if (!f) { + if (flags & MAILMAP_NOFOLLOW) + fd = open_nofollow(filename, O_RDONLY); + else + fd = open(filename, O_RDONLY); + + if (fd < 0) { if (errno == ENOENT) return 0; return error_errno("unable to open mailmap at %s", filename); } + f = xfdopen(fd, "r"); while (fgets(buffer, sizeof(buffer), f) != NULL) read_mailmap_line(map, buffer); @@ -226,10 +236,12 @@ int read_mailmap(struct string_list *map) git_mailmap_blob = "HEAD:.mailmap"; if (!startup_info->have_repository || !is_bare_repository()) - err |= read_mailmap_file(map, ".mailmap"); + err |= read_mailmap_file(map, ".mailmap", + startup_info->have_repository ? + MAILMAP_NOFOLLOW : 0); if (startup_info->have_repository) err |= read_mailmap_blob(map, git_mailmap_blob); - err |= read_mailmap_file(map, git_mailmap_file); + err |= read_mailmap_file(map, git_mailmap_file, 0); return err; } diff --git a/mem-pool.c b/mem-pool.c index 8401761dda..ccdcad2e3d 100644 --- a/mem-pool.c +++ b/mem-pool.c @@ -5,7 +5,7 @@ #include "cache.h" #include "mem-pool.h" -#define BLOCK_GROWTH_SIZE 1024*1024 - sizeof(struct mp_block); +#define BLOCK_GROWTH_SIZE (1024 * 1024 - sizeof(struct mp_block)) /* * Allocate a new mp_block and insert it after the block specified in diff --git a/merge-ort.c b/merge-ort.c index 603d30c521..ba35600d4e 100644 --- a/merge-ort.c +++ b/merge-ort.c @@ -351,17 +351,11 @@ static void clear_or_reinit_internal_opts(struct merge_options_internal *opti, /* Free memory used by various renames maps */ for (i = MERGE_SIDE1; i <= MERGE_SIDE2; ++i) { - struct hashmap_iter iter; - struct strmap_entry *entry; - strset_func(&renames->dirs_removed[i]); - strmap_for_each_entry(&renames->dir_rename_count[i], - &iter, entry) { - struct strintmap *counts = entry->value; - strintmap_clear(counts); - } - strmap_func(&renames->dir_rename_count[i], 1); + partial_clear_dir_rename_count(&renames->dir_rename_count[i]); + if (!reinitialize) + strmap_clear(&renames->dir_rename_count[i], 1); strmap_func(&renames->dir_renames[i], 0); } @@ -1302,131 +1296,6 @@ static char *handle_path_level_conflicts(struct merge_options *opt, return new_path; } -static void dirname_munge(char *filename) -{ - char *slash = strrchr(filename, '/'); - if (!slash) - slash = filename; - *slash = '\0'; -} - -static void increment_count(struct strmap *dir_rename_count, - char *old_dir, - char *new_dir) -{ - struct strintmap *counts; - struct strmap_entry *e; - - /* Get the {new_dirs -> counts} mapping using old_dir */ - e = strmap_get_entry(dir_rename_count, old_dir); - if (e) { - counts = e->value; - } else { - counts = xmalloc(sizeof(*counts)); - strintmap_init_with_options(counts, 0, NULL, 1); - strmap_put(dir_rename_count, old_dir, counts); - } - - /* Increment the count for new_dir */ - strintmap_incr(counts, new_dir, 1); -} - -static void update_dir_rename_counts(struct strmap *dir_rename_count, - struct strset *dirs_removed, - const char *oldname, - const char *newname) -{ - char *old_dir = xstrdup(oldname); - char *new_dir = xstrdup(newname); - char new_dir_first_char = new_dir[0]; - int first_time_in_loop = 1; - - while (1) { - dirname_munge(old_dir); - dirname_munge(new_dir); - - /* - * When renaming - * "a/b/c/d/e/foo.c" -> "a/b/some/thing/else/e/foo.c" - * then this suggests that both - * a/b/c/d/e/ => a/b/some/thing/else/e/ - * a/b/c/d/ => a/b/some/thing/else/ - * so we want to increment counters for both. We do NOT, - * however, also want to suggest that there was the following - * rename: - * a/b/c/ => a/b/some/thing/ - * so we need to quit at that point. - * - * Note the when first_time_in_loop, we only strip off the - * basename, and we don't care if that's different. - */ - if (!first_time_in_loop) { - char *old_sub_dir = strchr(old_dir, '\0')+1; - char *new_sub_dir = strchr(new_dir, '\0')+1; - if (!*new_dir) { - /* - * Special case when renaming to root directory, - * i.e. when new_dir == "". In this case, we had - * something like - * a/b/subdir => subdir - * and so dirname_munge() sets things up so that - * old_dir = "a/b\0subdir\0" - * new_dir = "\0ubdir\0" - * We didn't have a '/' to overwrite a '\0' onto - * in new_dir, so we have to compare differently. - */ - if (new_dir_first_char != old_sub_dir[0] || - strcmp(old_sub_dir+1, new_sub_dir)) - break; - } else { - if (strcmp(old_sub_dir, new_sub_dir)) - break; - } - } - - if (strset_contains(dirs_removed, old_dir)) - increment_count(dir_rename_count, old_dir, new_dir); - else - break; - - /* If we hit toplevel directory ("") for old or new dir, quit */ - if (!*old_dir || !*new_dir) - break; - - first_time_in_loop = 0; - } - - /* Free resources we don't need anymore */ - free(old_dir); - free(new_dir); -} - -static void compute_rename_counts(struct diff_queue_struct *pairs, - struct strmap *dir_rename_count, - struct strset *dirs_removed) -{ - int i; - - for (i = 0; i < pairs->nr; ++i) { - struct diff_filepair *pair = pairs->queue[i]; - - /* File not part of directory rename if it wasn't renamed */ - if (pair->status != 'R') - continue; - - /* - * Make dir_rename_count contain a map of a map: - * old_directory -> {new_directory -> count} - * In other words, for every pair look at the directories for - * the old filename and the new filename and count how many - * times that pairing occurs. - */ - update_dir_rename_counts(dir_rename_count, dirs_removed, - pair->one->path, - pair->two->path); - } -} - static void get_provisional_directory_renames(struct merge_options *opt, unsigned side, int *clean) @@ -1435,9 +1304,6 @@ static void get_provisional_directory_renames(struct merge_options *opt, struct strmap_entry *entry; struct rename_info *renames = &opt->priv->renames; - compute_rename_counts(&renames->pairs[side], - &renames->dir_rename_count[side], - &renames->dirs_removed[side]); /* * Collapse * dir_rename_count: old_directory -> {new_directory -> count} @@ -1576,8 +1442,7 @@ static void compute_collisions(struct strmap *collisions, if (collision_info) { free(new_path); } else { - collision_info = xcalloc(1, - sizeof(struct collision_info)); + CALLOC_ARRAY(collision_info, 1); string_list_init(&collision_info->source_files, 0); strmap_put(collisions, new_path, collision_info); } @@ -1718,7 +1583,7 @@ static void apply_directory_rename_modifications(struct merge_options *opt, struct conflict_info *dir_ci; char *cur_dir = dirs_to_insert.items[i].string; - dir_ci = xcalloc(1, sizeof(*dir_ci)); + CALLOC_ARRAY(dir_ci, 1); dir_ci->merged.directory_name = parent_name; len = strlen(parent_name); @@ -2162,7 +2027,9 @@ static void detect_regular_renames(struct merge_options *opt, diff_queued_diff = renames->pairs[side_index]; trace2_region_enter("diff", "diffcore_rename", opt->repo); - diffcore_rename(&diff_opts); + diffcore_rename_extended(&diff_opts, + &renames->dirs_removed[side_index], + &renames->dir_rename_count[side_index]); trace2_region_leave("diff", "diffcore_rename", opt->repo); resolve_diffpair_statuses(&diff_queued_diff); @@ -2703,7 +2570,7 @@ static void process_entry(struct merge_options *opt, * the directory to remain here, so we need to move this * path to some new location. */ - new_ci = xcalloc(1, sizeof(*new_ci)); + CALLOC_ARRAY(new_ci, 1); /* We don't really want new_ci->merged.result copied, but it'll * be overwritten below so it doesn't matter. We also don't * want any directory mode/oid values copied, but we'll zero @@ -3083,7 +2950,7 @@ static int checkout(struct merge_options *opt, unpack_opts.verbose_update = (opt->verbosity > 2); unpack_opts.fn = twoway_merge; if (1/* FIXME: opts->overwrite_ignore*/) { - unpack_opts.dir = xcalloc(1, sizeof(*unpack_opts.dir)); + CALLOC_ARRAY(unpack_opts.dir, 1); unpack_opts.dir->flags |= DIR_SHOW_IGNORED; setup_standard_excludes(unpack_opts.dir); } diff --git a/merge-recursive.c b/merge-recursive.c index b052974f19..b69e694d98 100644 --- a/merge-recursive.c +++ b/merge-recursive.c @@ -303,7 +303,7 @@ static inline void setup_rename_conflict_info(enum rename_type rename_type, return; } - ci = xcalloc(1, sizeof(struct rename_conflict_info)); + CALLOC_ARRAY(ci, 1); ci->rename_type = rename_type; ci->ren1 = ren1; ci->ren2 = ren2; @@ -2389,8 +2389,7 @@ static void compute_collisions(struct hashmap *collisions, continue; collision_ent = collision_find_entry(collisions, new_path); if (!collision_ent) { - collision_ent = xcalloc(1, - sizeof(struct collision_entry)); + CALLOC_ARRAY(collision_ent, 1); hashmap_entry_init(&collision_ent->ent, strhash(new_path)); hashmap_put(collisions, &collision_ent->ent); @@ -2594,7 +2593,7 @@ static struct string_list *get_renames(struct merge_options *opt, struct string_list *renames; compute_collisions(&collisions, dir_renames, pairs); - renames = xcalloc(1, sizeof(struct string_list)); + CALLOC_ARRAY(renames, 1); for (i = 0; i < pairs->nr; ++i) { struct string_list_item *item; @@ -3664,7 +3663,7 @@ static int merge_start(struct merge_options *opt, struct tree *head) return -1; } - opt->priv = xcalloc(1, sizeof(*opt->priv)); + CALLOC_ARRAY(opt->priv, 1); string_list_init(&opt->priv->df_conflict_file_set, 1); return 0; } @@ -145,8 +145,8 @@ struct multi_pack_index *load_multi_pack_index(const char *object_dir, int local m->num_objects = ntohl(m->chunk_oid_fanout[255]); - m->pack_names = xcalloc(m->num_packs, sizeof(*m->pack_names)); - m->packs = xcalloc(m->num_packs, sizeof(*m->packs)); + CALLOC_ARRAY(m->pack_names, m->num_packs); + CALLOC_ARRAY(m->packs, m->num_packs); cur_pack_name = (const char *)m->chunk_pack_names; for (i = 0; i < m->num_packs; i++) { @@ -1144,7 +1144,7 @@ int expire_midx_packs(struct repository *r, const char *object_dir, unsigned fla if (!m) return 0; - count = xcalloc(m->num_packs, sizeof(uint32_t)); + CALLOC_ARRAY(count, m->num_packs); if (flags & MIDX_PROGRESS) progress = start_delayed_progress(_("Counting referenced objects"), @@ -1315,7 +1315,7 @@ int midx_repack(struct repository *r, const char *object_dir, size_t batch_size, if (!m) return 0; - include_pack = xcalloc(m->num_packs, sizeof(unsigned char)); + CALLOC_ARRAY(include_pack, m->num_packs); if (batch_size) { if (fill_included_packs_batch(r, m, include_pack, batch_size)) diff --git a/name-hash.c b/name-hash.c index 4e03fac9bb..ce28f3f070 100644 --- a/name-hash.c +++ b/name-hash.c @@ -225,7 +225,7 @@ static void init_dir_mutex(void) { int j; - lazy_dir_mutex_array = xcalloc(LAZY_MAX_MUTEX, sizeof(pthread_mutex_t)); + CALLOC_ARRAY(lazy_dir_mutex_array, LAZY_MAX_MUTEX); for (j = 0; j < LAZY_MAX_MUTEX; j++) init_recursive_mutex(&lazy_dir_mutex_array[j]); @@ -514,9 +514,9 @@ static void threaded_lazy_init_name_hash( k_start = 0; nr_each = DIV_ROUND_UP(istate->cache_nr, lazy_nr_dir_threads); - lazy_entries = xcalloc(istate->cache_nr, sizeof(struct lazy_entry)); - td_dir = xcalloc(lazy_nr_dir_threads, sizeof(struct lazy_dir_thread_data)); - td_name = xcalloc(1, sizeof(struct lazy_name_thread_data)); + CALLOC_ARRAY(lazy_entries, istate->cache_nr); + CALLOC_ARRAY(td_dir, lazy_nr_dir_threads); + CALLOC_ARRAY(td_name, 1); init_dir_mutex(); diff --git a/negotiator/default.c b/negotiator/default.c index 4b78f6bf36..434189ae5d 100644 --- a/negotiator/default.c +++ b/negotiator/default.c @@ -167,7 +167,7 @@ void default_negotiator_init(struct fetch_negotiator *negotiator) negotiator->next = next; negotiator->ack = ack; negotiator->release = release; - negotiator->data = ns = xcalloc(1, sizeof(*ns)); + negotiator->data = CALLOC_ARRAY(ns, 1); ns->rev_list.compare = compare_commits_by_commit_date; if (marked) diff --git a/negotiator/skipping.c b/negotiator/skipping.c index dffbc76c49..1236e79224 100644 --- a/negotiator/skipping.c +++ b/negotiator/skipping.c @@ -62,7 +62,7 @@ static struct entry *rev_list_push(struct data *data, struct commit *commit, int struct entry *entry; commit->object.flags |= mark | SEEN; - entry = xcalloc(1, sizeof(*entry)); + CALLOC_ARRAY(entry, 1); entry->commit = commit; prio_queue_put(&data->rev_list, entry); @@ -241,7 +241,7 @@ void skipping_negotiator_init(struct fetch_negotiator *negotiator) negotiator->next = next; negotiator->ack = ack; negotiator->release = release; - negotiator->data = data = xcalloc(1, sizeof(*data)); + negotiator->data = CALLOC_ARRAY(data, 1); data->rev_list.compare = compare; if (marked) diff --git a/notes-merge.c b/notes-merge.c index 2fe724f1cf..d2771fa3d4 100644 --- a/notes-merge.c +++ b/notes-merge.c @@ -136,7 +136,7 @@ static struct notes_merge_pair *diff_tree_remote(struct notes_merge_options *o, diff_tree_oid(base, remote, "", &opt); diffcore_std(&opt); - changes = xcalloc(diff_queued_diff.nr, sizeof(struct notes_merge_pair)); + CALLOC_ARRAY(changes, diff_queued_diff.nr); for (i = 0; i < diff_queued_diff.nr; i++) { struct diff_filepair *p = diff_queued_diff.queue[i]; diff --git a/notes-utils.c b/notes-utils.c index 4bf4888d8c..d7d18e30f5 100644 --- a/notes-utils.c +++ b/notes-utils.c @@ -129,7 +129,7 @@ struct notes_rewrite_cfg *init_copy_notes_for_rewrite(const char *cmd) c->cmd = cmd; c->enabled = 1; c->combine = combine_notes_concatenate; - c->refs = xcalloc(1, sizeof(struct string_list)); + CALLOC_ARRAY(c->refs, 1); c->refs->strdup_strings = 1; c->refs_from_env = 0; c->mode_from_env = 0; @@ -452,7 +452,7 @@ static void load_subtree(struct notes_tree *t, struct leaf_node *subtree, goto handle_non_note; } - l = xcalloc(1, sizeof(*l)); + CALLOC_ARRAY(l, 1); oidcpy(&l->key_oid, &object_oid); oidcpy(&l->val_oid, &entry.oid); if (note_tree_insert(t, node, n, l, type, diff --git a/object-file.c b/object-file.c index 5bcfde8471..624af408cd 100644 --- a/object-file.c +++ b/object-file.c @@ -546,7 +546,7 @@ static int link_alt_odb_entry(struct repository *r, const char *entry, return -1; } - ent = xcalloc(1, sizeof(*ent)); + CALLOC_ARRAY(ent, 1); ent->path = xstrdup(pathbuf.buf); /* add the alternate entry */ diff --git a/object-store.h b/object-store.h index 541dab0858..ec32c23dcb 100644 --- a/object-store.h +++ b/object-store.h @@ -153,6 +153,11 @@ struct raw_object_store { /* A most-recently-used ordered version of the packed_git list. */ struct list_head packed_git_mru; + struct { + struct packed_git **packs; + unsigned flags; + } kept_pack_cache; + /* * A map of packfiles to packed_git structs for tracking which * packs have been loaded already. @@ -127,7 +127,7 @@ static void grow_object_hash(struct repository *r) int new_hash_size = r->parsed_objects->obj_hash_size < 32 ? 32 : 2 * r->parsed_objects->obj_hash_size; struct object **new_hash; - new_hash = xcalloc(new_hash_size, sizeof(struct object *)); + CALLOC_ARRAY(new_hash, new_hash_size); for (i = 0; i < r->parsed_objects->obj_hash_size; i++) { struct object *obj = r->parsed_objects->obj_hash[i]; @@ -478,7 +478,7 @@ struct parsed_object_pool *parsed_object_pool_new(void) o->object_state = allocate_alloc_state(); o->is_shallow = -1; - o->shallow_stat = xcalloc(1, sizeof(*o->shallow_stat)); + CALLOC_ARRAY(o->shallow_stat, 1); o->buffer_slab = allocate_commit_buffer_slab(); diff --git a/pack-bitmap.c b/pack-bitmap.c index 1f69b5fa85..1ebe0c8162 100644 --- a/pack-bitmap.c +++ b/pack-bitmap.c @@ -978,7 +978,7 @@ struct bitmap_index *prepare_bitmap_walk(struct rev_info *revs, /* try to open a bitmapped pack, but don't parse it yet * because we may not need to use it */ - bitmap_git = xcalloc(1, sizeof(*bitmap_git)); + CALLOC_ARRAY(bitmap_git, 1); if (open_pack_bitmap(revs->repo, bitmap_git) < 0) goto cleanup; @@ -1388,7 +1388,7 @@ uint32_t *create_bitmap_mapping(struct bitmap_index *bitmap_git, uint32_t *reposition; num_objects = bitmap_git->pack->num_objects; - reposition = xcalloc(num_objects, sizeof(uint32_t)); + CALLOC_ARRAY(reposition, num_objects); for (i = 0; i < num_objects; ++i) { struct object_id oid; diff --git a/pack-objects.c b/pack-objects.c index f2a433885a..fe2a4eace9 100644 --- a/pack-objects.c +++ b/pack-objects.c @@ -49,7 +49,7 @@ static void rehash_objects(struct packing_data *pdata) pdata->index_size = 1024; free(pdata->index); - pdata->index = xcalloc(pdata->index_size, sizeof(*pdata->index)); + CALLOC_ARRAY(pdata->index, pdata->index_size); entry = pdata->objects; diff --git a/packfile.c b/packfile.c index 1fec12ac5f..6661f3325a 100644 --- a/packfile.c +++ b/packfile.c @@ -638,7 +638,7 @@ unsigned char *use_pack(struct packed_git *p, if (p->pack_fd == -1 && open_packed_git(p)) die("packfile %s cannot be accessed", p->pack_name); - win = xcalloc(1, sizeof(*win)); + CALLOC_ARRAY(win, 1); win->offset = (offset / window_align) * window_align; len = p->pack_size - win->offset; if (len > packed_git_window_size) @@ -2066,12 +2066,79 @@ int find_pack_entry(struct repository *r, const struct object_id *oid, struct pa return 0; } +static void maybe_invalidate_kept_pack_cache(struct repository *r, + unsigned flags) +{ + if (!r->objects->kept_pack_cache.packs) + return; + if (r->objects->kept_pack_cache.flags == flags) + return; + FREE_AND_NULL(r->objects->kept_pack_cache.packs); + r->objects->kept_pack_cache.flags = 0; +} + +static struct packed_git **kept_pack_cache(struct repository *r, unsigned flags) +{ + maybe_invalidate_kept_pack_cache(r, flags); + + if (!r->objects->kept_pack_cache.packs) { + struct packed_git **packs = NULL; + size_t nr = 0, alloc = 0; + struct packed_git *p; + + /* + * We want "all" packs here, because we need to cover ones that + * are used by a midx, as well. We need to look in every one of + * them (instead of the midx itself) to cover duplicates. It's + * possible that an object is found in two packs that the midx + * covers, one kept and one not kept, but the midx returns only + * the non-kept version. + */ + for (p = get_all_packs(r); p; p = p->next) { + if ((p->pack_keep && (flags & ON_DISK_KEEP_PACKS)) || + (p->pack_keep_in_core && (flags & IN_CORE_KEEP_PACKS))) { + ALLOC_GROW(packs, nr + 1, alloc); + packs[nr++] = p; + } + } + ALLOC_GROW(packs, nr + 1, alloc); + packs[nr] = NULL; + + r->objects->kept_pack_cache.packs = packs; + r->objects->kept_pack_cache.flags = flags; + } + + return r->objects->kept_pack_cache.packs; +} + +int find_kept_pack_entry(struct repository *r, + const struct object_id *oid, + unsigned flags, + struct pack_entry *e) +{ + struct packed_git **cache; + + for (cache = kept_pack_cache(r, flags); *cache; cache++) { + struct packed_git *p = *cache; + if (fill_pack_entry(oid, e, p)) + return 1; + } + + return 0; +} + int has_object_pack(const struct object_id *oid) { struct pack_entry e; return find_pack_entry(the_repository, oid, &e); } +int has_object_kept_pack(const struct object_id *oid, unsigned flags) +{ + struct pack_entry e; + return find_kept_pack_entry(the_repository, oid, flags, &e); +} + int has_pack_index(const unsigned char *sha1) { struct stat st; diff --git a/packfile.h b/packfile.h index 4cfec9e8d3..3ae117a8ae 100644 --- a/packfile.h +++ b/packfile.h @@ -162,13 +162,18 @@ int packed_object_info(struct repository *r, void mark_bad_packed_object(struct packed_git *p, const unsigned char *sha1); const struct packed_git *has_packed_and_bad(struct repository *r, const unsigned char *sha1); +#define ON_DISK_KEEP_PACKS 1 +#define IN_CORE_KEEP_PACKS 2 + /* * Iff a pack file in the given repository contains the object named by sha1, * return true and store its location to e. */ int find_pack_entry(struct repository *r, const struct object_id *oid, struct pack_entry *e); +int find_kept_pack_entry(struct repository *r, const struct object_id *oid, unsigned flags, struct pack_entry *e); int has_object_pack(const struct object_id *oid); +int has_object_kept_pack(const struct object_id *oid, unsigned flags); int has_pack_index(const unsigned char *sha1); diff --git a/patch-ids.c b/patch-ids.c index 3f404e4b0b..8bf425555d 100644 --- a/patch-ids.c +++ b/patch-ids.c @@ -124,7 +124,7 @@ struct patch_id *add_commit_patch_id(struct commit *commit, if (!patch_id_defined(commit)) return NULL; - key = xcalloc(1, sizeof(*key)); + CALLOC_ARRAY(key, 1); if (init_patch_id_entry(key, commit, ids)) { free(key); return NULL; diff --git a/pathspec.c b/pathspec.c index 7a229d8d22..18b3be362a 100644 --- a/pathspec.c +++ b/pathspec.c @@ -154,7 +154,7 @@ static void parse_pathspec_attr_match(struct pathspec_item *item, const char *va string_list_remove_empty_items(&list, 0); item->attr_check = attr_check_alloc(); - item->attr_match = xcalloc(list.nr, sizeof(struct attr_match)); + CALLOC_ARRAY(item->attr_match, list.nr); for_each_string_list_item(si, &list) { size_t attr_len; @@ -561,7 +561,7 @@ void parse_pathspec(struct pathspec *pathspec, if (!(flags & PATHSPEC_PREFER_CWD)) BUG("PATHSPEC_PREFER_CWD requires arguments"); - pathspec->items = item = xcalloc(1, sizeof(*item)); + pathspec->items = CALLOC_ARRAY(item, 1); item->match = xstrdup(prefix); item->original = xstrdup(prefix); item->nowildcard_len = item->len = strlen(prefix); @@ -12,6 +12,7 @@ #include "reflog-walk.h" #include "gpg-interface.h" #include "trailer.h" +#include "run-command.h" static char *user_format; static struct cmt_fmt_map { @@ -678,7 +679,7 @@ static int mailmap_name(const char **email, size_t *email_len, { static struct string_list *mail_map; if (!mail_map) { - mail_map = xcalloc(1, sizeof(*mail_map)); + CALLOC_ARRAY(mail_map, 1); read_mailmap(mail_map); } return mail_map->nr && map_user(mail_map, email, email_len, name, name_len); @@ -1206,6 +1207,34 @@ int format_set_trailers_options(struct process_trailer_options *opts, return 0; } +static size_t parse_describe_args(const char *start, struct strvec *args) +{ + const char *options[] = { "match", "exclude" }; + const char *arg = start; + + for (;;) { + const char *matched = NULL; + const char *argval; + size_t arglen = 0; + int i; + + for (i = 0; i < ARRAY_SIZE(options); i++) { + if (match_placeholder_arg_value(arg, options[i], &arg, + &argval, &arglen)) { + matched = options[i]; + break; + } + } + if (!matched) + break; + + if (!arglen) + return 0; + strvec_pushf(args, "--%s=%.*s", matched, (int)arglen, argval); + } + return arg - start; +} + static size_t format_commit_one(struct strbuf *sb, /* in UTF-8 */ const char *placeholder, void *context) @@ -1271,6 +1300,41 @@ static size_t format_commit_one(struct strbuf *sb, /* in UTF-8 */ return parse_padding_placeholder(placeholder, c); } + if (skip_prefix(placeholder, "(describe", &arg)) { + struct child_process cmd = CHILD_PROCESS_INIT; + struct strbuf out = STRBUF_INIT; + struct strbuf err = STRBUF_INIT; + struct pretty_print_describe_status *describe_status; + + describe_status = c->pretty_ctx->describe_status; + if (describe_status) { + if (!describe_status->max_invocations) + return 0; + describe_status->max_invocations--; + } + + cmd.git_cmd = 1; + strvec_push(&cmd.args, "describe"); + + if (*arg == ':') { + arg++; + arg += parse_describe_args(arg, &cmd.args); + } + + if (*arg != ')') { + child_process_clear(&cmd); + return 0; + } + + strvec_push(&cmd.args, oid_to_hex(&commit->object.oid)); + pipe_command(&cmd, NULL, 0, &out, 0, &err, 0); + strbuf_rtrim(&out); + strbuf_addbuf(sb, &out); + strbuf_release(&out); + strbuf_release(&err); + return arg - placeholder + 1; + } + /* these depend on the commit */ if (!commit->object.parsed) parse_object(the_repository, &commit->object.oid); @@ -24,6 +24,10 @@ enum cmit_fmt { CMIT_FMT_UNSPECIFIED }; +struct pretty_print_describe_status { + unsigned int max_invocations; +}; + struct pretty_print_context { /* * Callers should tweak these to change the behavior of pp_* functions. @@ -45,6 +49,7 @@ struct pretty_print_context { int color; struct ident_split *from_ident; unsigned encode_email_headers:1; + struct pretty_print_describe_status *describe_status; /* * Fields below here are manipulated internally by pp_* functions and diff --git a/progress.c b/progress.c index 31014e6fca..680c6a8bf9 100644 --- a/progress.c +++ b/progress.c @@ -196,7 +196,7 @@ void display_throughput(struct progress *progress, uint64_t total) now_ns = progress_getnanotime(progress); if (!tp) { - progress->throughput = tp = xcalloc(1, sizeof(*tp)); + progress->throughput = CALLOC_ARRAY(tp, 1); tp->prev_total = tp->curr_total = total; tp->prev_ns = now_ns; strbuf_init(&tp->display, 0); diff --git a/promisor-remote.c b/promisor-remote.c index 3c572b1c81..da3f2ca261 100644 --- a/promisor-remote.c +++ b/promisor-remote.c @@ -208,7 +208,7 @@ static int remove_fetched_oids(struct repository *repo, if (remaining_nr) { int j = 0; - new_oids = xcalloc(remaining_nr, sizeof(*new_oids)); + CALLOC_ARRAY(new_oids, remaining_nr); for (i = 0; i < oid_nr; i++) if (remaining[i]) oidcpy(&new_oids[j++], &old_oids[i]); diff --git a/range-diff.c b/range-diff.c index a3cc7c94a3..116fb0735c 100644 --- a/range-diff.c +++ b/range-diff.c @@ -96,7 +96,7 @@ static int read_patches(const char *range, struct string_list *list, string_list_append(list, buf.buf)->util = util; strbuf_reset(&buf); } - util = xcalloc(sizeof(*util), 1); + CALLOC_ARRAY(util, 1); if (get_oid(p, &util->oid)) { error(_("could not parse commit '%s'"), p); free(util); diff --git a/read-cache.c b/read-cache.c index 1e9a50c6c7..5a907af2fb 100644 --- a/read-cache.c +++ b/read-cache.c @@ -2097,7 +2097,7 @@ static unsigned long load_cache_entries_threaded(struct index_state *istate, con /* ensure we have no more threads than we have blocks to process */ if (nr_threads > ieot->nr) nr_threads = ieot->nr; - data = xcalloc(nr_threads, sizeof(*data)); + CALLOC_ARRAY(data, nr_threads); offset = ieot_start = 0; ieot_blocks = DIV_ROUND_UP(ieot->nr, nr_threads); @@ -2199,7 +2199,7 @@ int do_read_index(struct index_state *istate, const char *path, int must_exist) istate->version = ntohl(hdr->hdr_version); istate->cache_nr = ntohl(hdr->hdr_entries); istate->cache_alloc = alloc_nr(istate->cache_nr); - istate->cache = xcalloc(istate->cache_alloc, sizeof(*istate->cache)); + CALLOC_ARRAY(istate->cache, istate->cache_alloc); istate->initialized = 1; p.istate = istate; @@ -2326,7 +2326,7 @@ int read_index_from(struct index_state *istate, const char *path, if (split_index->base) discard_index(split_index->base); else - split_index->base = xcalloc(1, sizeof(*split_index->base)); + CALLOC_ARRAY(split_index->base, 1); base_oid_hex = oid_to_hex(&split_index->base_oid); base_path = xstrfmt("%s/sharedindex.%s", gitdir, base_oid_hex); @@ -2373,6 +2373,7 @@ int discard_index(struct index_state *istate) cache_tree_free(&(istate->cache_tree)); istate->initialized = 0; istate->fsmonitor_has_run_once = 0; + FREE_AND_NULL(istate->fsmonitor_last_update); FREE_AND_NULL(istate->cache); istate->cache_alloc = 0; discard_split_index(istate); @@ -3427,7 +3428,7 @@ void stat_validity_update(struct stat_validity *sv, int fd) stat_validity_clear(sv); else { if (!sv->sd) - sv->sd = xcalloc(1, sizeof(struct stat_data)); + CALLOC_ARRAY(sv->sd, 1); fill_stat_data(sv->sd, &st); } } diff --git a/ref-filter.c b/ref-filter.c index e84efb53db..f0bd32f714 100644 --- a/ref-filter.c +++ b/ref-filter.c @@ -772,7 +772,8 @@ static int if_atom_handler(struct atom_value *atomv, struct ref_formatting_state struct strbuf *unused_err) { struct ref_formatting_stack *new_stack; - struct if_then_else *if_then_else = xcalloc(sizeof(struct if_then_else), 1); + struct if_then_else *if_then_else = xcalloc(1, + sizeof(struct if_then_else)); if_then_else->str = atomv->atom->u.if_then_else.str; if_then_else->cmp_status = atomv->atom->u.if_then_else.cmp_status; @@ -1676,7 +1677,7 @@ static int populate_value(struct ref_array_item *ref, struct strbuf *err) int i; struct object_info empty = OBJECT_INFO_INIT; - ref->value = xcalloc(used_atom_cnt, sizeof(struct atom_value)); + CALLOC_ARRAY(ref->value, used_atom_cnt); if (need_symref && (ref->flag & REF_ISSYMREF) && !ref->symref) { ref->symref = resolve_refdup(ref->refname, RESOLVE_REF_READING, @@ -2185,7 +2186,7 @@ static void reach_filter(struct ref_array *array, if (!check_reachable) return; - to_clear = xcalloc(sizeof(struct commit *), array->nr); + CALLOC_ARRAY(to_clear, array->nr); repo_init_revisions(the_repository, &revs, NULL); @@ -2490,7 +2491,7 @@ void parse_ref_sorting(struct ref_sorting **sorting_tail, const char *arg) { struct ref_sorting *s; - s = xcalloc(1, sizeof(*s)); + CALLOC_ARRAY(s, 1); s->next = *sorting_tail; *sorting_tail = s; diff --git a/reflog-walk.c b/reflog-walk.c index 3a25b27d8f..e9cd328369 100644 --- a/reflog-walk.c +++ b/reflog-walk.c @@ -112,7 +112,7 @@ struct reflog_walk_info { void init_reflog_walk(struct reflog_walk_info **info) { - *info = xcalloc(1, sizeof(struct reflog_walk_info)); + CALLOC_ARRAY(*info, 1); (*info)->complete_reflogs.strdup_strings = 1; } @@ -181,7 +181,7 @@ int add_reflog_for_walk(struct reflog_walk_info *info, } free(branch); - commit_reflog = xcalloc(1, sizeof(struct commit_reflog)); + CALLOC_ARRAY(commit_reflog, 1); if (recno < 0) { commit_reflog->recno = get_reflog_recno_by_time(reflogs, timestamp); if (commit_reflog->recno < 0) { @@ -1007,7 +1007,7 @@ struct ref_transaction *ref_store_transaction_begin(struct ref_store *refs, struct ref_transaction *tr; assert(err); - tr = xcalloc(1, sizeof(struct ref_transaction)); + CALLOC_ARRAY(tr, 1); tr->ref_store = refs; return tr; } @@ -1306,7 +1306,7 @@ int parse_hide_refs_config(const char *var, const char *value, const char *secti while (len && ref[len - 1] == '/') ref[--len] = '\0'; if (!hide_refs) { - hide_refs = xcalloc(1, sizeof(*hide_refs)); + CALLOC_ARRAY(hide_refs, 1); hide_refs->strdup_strings = 1; } string_list_append(hide_refs, ref); diff --git a/refs/files-backend.c b/refs/files-backend.c index 4fdc68810b..119972ee16 100644 --- a/refs/files-backend.c +++ b/refs/files-backend.c @@ -549,7 +549,7 @@ static int lock_raw_ref(struct files_ref_store *refs, /* First lock the file so it can't change out from under us. */ - *lock_p = lock = xcalloc(1, sizeof(*lock)); + *lock_p = CALLOC_ARRAY(lock, 1); lock->ref_name = xstrdup(refname); files_ref_path(refs, &ref_file, refname); @@ -843,7 +843,7 @@ static struct ref_iterator *files_ref_iterator_begin( overlay_iter = overlay_ref_iterator_begin(loose_iter, packed_iter); - iter = xcalloc(1, sizeof(*iter)); + CALLOC_ARRAY(iter, 1); ref_iterator = &iter->base; base_ref_iterator_init(ref_iterator, &files_ref_iterator_vtable, overlay_iter->ordered); @@ -930,7 +930,7 @@ static struct ref_lock *lock_ref_oid_basic(struct files_ref_store *refs, files_assert_main_repository(refs, "lock_ref_oid_basic"); assert(err); - lock = xcalloc(1, sizeof(struct ref_lock)); + CALLOC_ARRAY(lock, 1); if (mustexist) resolve_flags |= RESOLVE_REF_READING; @@ -2152,7 +2152,7 @@ static struct ref_iterator *reflog_iterator_begin(struct ref_store *ref_store, return empty_ref_iterator_begin(); } - iter = xcalloc(1, sizeof(*iter)); + CALLOC_ARRAY(iter, 1); ref_iterator = &iter->base; base_ref_iterator_init(ref_iterator, &files_reflog_iterator_vtable, 0); @@ -2597,7 +2597,7 @@ static int files_transaction_prepare(struct ref_store *ref_store, if (!transaction->nr) goto cleanup; - backend_data = xcalloc(1, sizeof(*backend_data)); + CALLOC_ARRAY(backend_data, 1); transaction->backend_data = backend_data; /* diff --git a/refs/iterator.c b/refs/iterator.c index 629e00a122..a89d132d4f 100644 --- a/refs/iterator.c +++ b/refs/iterator.c @@ -393,7 +393,7 @@ struct ref_iterator *prefix_ref_iterator_begin(struct ref_iterator *iter0, if (!*prefix && !trim) return iter0; /* optimization: no need to wrap iterator */ - iter = xcalloc(1, sizeof(*iter)); + CALLOC_ARRAY(iter, 1); ref_iterator = &iter->base; base_ref_iterator_init(ref_iterator, &prefix_ref_iterator_vtable, iter0->ordered); diff --git a/refs/packed-backend.c b/refs/packed-backend.c index b912f2505f..dfecdbc1db 100644 --- a/refs/packed-backend.c +++ b/refs/packed-backend.c @@ -941,7 +941,7 @@ static struct ref_iterator *packed_ref_iterator_begin( if (start == snapshot->eof) return empty_ref_iterator_begin(); - iter = xcalloc(1, sizeof(*iter)); + CALLOC_ARRAY(iter, 1); ref_iterator = &iter->base; base_ref_iterator_init(ref_iterator, &packed_ref_iterator_vtable, 1); @@ -1424,7 +1424,7 @@ static int packed_transaction_prepare(struct ref_store *ref_store, * do so itself. */ - data = xcalloc(1, sizeof(*data)); + CALLOC_ARRAY(data, 1); string_list_init(&data->updates, 0); transaction->backend_data = data; diff --git a/refs/ref-cache.c b/refs/ref-cache.c index b7052f72e2..46f1e54284 100644 --- a/refs/ref-cache.c +++ b/refs/ref-cache.c @@ -530,7 +530,7 @@ struct ref_iterator *cache_ref_iterator_begin(struct ref_cache *cache, if (prime_dir) prime_ref_dir(dir, prefix); - iter = xcalloc(1, sizeof(*iter)); + CALLOC_ARRAY(iter, 1); ref_iterator = &iter->base; base_ref_iterator_init(ref_iterator, &cache_ref_iterator_vtable, 1); ALLOC_GROW(iter->levels, 10, iter->levels_alloc); @@ -151,7 +151,7 @@ static struct remote *make_remote(const char *name, int len) if (e) return container_of(e, struct remote, ent); - ret = xcalloc(1, sizeof(struct remote)); + CALLOC_ARRAY(ret, 1); ret->prune = -1; /* unspecified */ ret->prune_tags = -1; /* unspecified */ ret->name = xstrndup(name, len); @@ -186,7 +186,7 @@ static struct branch *make_branch(const char *name, size_t len) } ALLOC_GROW(branches, branches_nr + 1, branches_alloc); - ret = xcalloc(1, sizeof(struct branch)); + CALLOC_ARRAY(ret, 1); branches[branches_nr++] = ret; ret->name = xstrndup(name, len); ret->refname = xstrfmt("refs/heads/%s", ret->name); @@ -207,7 +207,7 @@ static struct rewrite *make_rewrite(struct rewrites *r, } ALLOC_GROW(r->rewrite, r->rewrite_nr + 1, r->rewrite_alloc); - ret = xcalloc(1, sizeof(struct rewrite)); + CALLOC_ARRAY(ret, 1); r->rewrite[r->rewrite_nr++] = ret; ret->base = xstrndup(base, len); ret->baselen = len; @@ -1664,7 +1664,7 @@ static void set_merge(struct branch *ret) remote = remote_get(ret->remote_name); - ret->merge = xcalloc(ret->merge_nr, sizeof(*ret->merge)); + CALLOC_ARRAY(ret->merge, ret->merge_nr); for (i = 0; i < ret->merge_nr; i++) { ret->merge[i] = xcalloc(1, sizeof(**ret->merge)); ret->merge[i]->src = xstrdup(ret->merge_name[i]); diff --git a/repository.c b/repository.c index c98298acd0..87b355e7a6 100644 --- a/repository.c +++ b/repository.c @@ -72,7 +72,7 @@ void repo_set_gitdir(struct repository *repo, repo_set_commondir(repo, o->commondir); if (!repo->objects->odb) { - repo->objects->odb = xcalloc(1, sizeof(*repo->objects->odb)); + CALLOC_ARRAY(repo->objects->odb, 1); repo->objects->odb_tail = &repo->objects->odb->next; } expand_base_dir(&repo->objects->odb->path, o->object_dir, @@ -262,7 +262,7 @@ void repo_clear(struct repository *repo) int repo_read_index(struct repository *repo) { if (!repo->index) - repo->index = xcalloc(1, sizeof(*repo->index)); + CALLOC_ARRAY(repo->index, 1); /* Complete the double-reference */ if (!repo->index->repo) diff --git a/resolve-undo.c b/resolve-undo.c index 236320f179..bbd2e57fe4 100644 --- a/resolve-undo.c +++ b/resolve-undo.c @@ -15,7 +15,7 @@ void record_resolve_undo(struct index_state *istate, struct cache_entry *ce) return; if (!istate->resolve_undo) { - resolve_undo = xcalloc(1, sizeof(*resolve_undo)); + CALLOC_ARRAY(resolve_undo, 1); resolve_undo->strdup_strings = 1; istate->resolve_undo = resolve_undo; } @@ -57,7 +57,7 @@ struct string_list *resolve_undo_read(const char *data, unsigned long size) int i; const unsigned rawsz = the_hash_algo->rawsz; - resolve_undo = xcalloc(1, sizeof(*resolve_undo)); + CALLOC_ARRAY(resolve_undo, 1); resolve_undo->strdup_strings = 1; while (size) { diff --git a/revision.c b/revision.c index b78733f508..553c0faa9b 100644 --- a/revision.c +++ b/revision.c @@ -154,7 +154,7 @@ static void paths_and_oids_insert(struct hashmap *map, entry = hashmap_get_entry(map, &key, ent, NULL); if (!entry) { - entry = xcalloc(1, sizeof(struct path_and_oids_entry)); + CALLOC_ARRAY(entry, 1); hashmap_entry_init(&entry->ent, hash); entry->path = xstrdup(key.path); oidset_init(&entry->trees, 16); @@ -1555,7 +1555,7 @@ void clear_ref_exclusion(struct string_list **ref_excludes_p) void add_ref_exclusion(struct string_list **ref_excludes_p, const char *exclude) { if (!*ref_excludes_p) { - *ref_excludes_p = xcalloc(1, sizeof(**ref_excludes_p)); + CALLOC_ARRAY(*ref_excludes_p, 1); (*ref_excludes_p)->strdup_strings = 1; } string_list_append(*ref_excludes_p, exclude); @@ -2336,6 +2336,16 @@ static int handle_revision_opt(struct rev_info *revs, int argc, const char **arg revs->unpacked = 1; } else if (starts_with(arg, "--unpacked=")) { die(_("--unpacked=<packfile> no longer supported")); + } else if (!strcmp(arg, "--no-kept-objects")) { + revs->no_kept_objects = 1; + revs->keep_pack_cache_flags |= IN_CORE_KEEP_PACKS; + revs->keep_pack_cache_flags |= ON_DISK_KEEP_PACKS; + } else if (skip_prefix(arg, "--no-kept-objects=", &optarg)) { + revs->no_kept_objects = 1; + if (!strcmp(optarg, "in-core")) + revs->keep_pack_cache_flags |= IN_CORE_KEEP_PACKS; + if (!strcmp(optarg, "on-disk")) + revs->keep_pack_cache_flags |= ON_DISK_KEEP_PACKS; } else if (!strcmp(arg, "-r")) { revs->diff = 1; revs->diffopt.flags.recursive = 1; @@ -2929,7 +2939,7 @@ static struct merge_simplify_state *locate_simplify_state(struct rev_info *revs, st = lookup_decoration(&revs->merge_simplification, &commit->object); if (!st) { - st = xcalloc(1, sizeof(*st)); + CALLOC_ARRAY(st, 1); add_decoration(&revs->merge_simplification, &commit->object, st); } return st; @@ -3795,6 +3805,11 @@ enum commit_action get_commit_action(struct rev_info *revs, struct commit *commi return commit_ignore; if (revs->unpacked && has_object_pack(&commit->object.oid)) return commit_ignore; + if (revs->no_kept_objects) { + if (has_object_kept_pack(&commit->object.oid, + revs->keep_pack_cache_flags)) + return commit_ignore; + } if (commit->object.flags & UNINTERESTING) return commit_ignore; if (revs->line_level_traverse && !want_ancestry(revs)) { diff --git a/revision.h b/revision.h index e6be3c845e..a20a530d52 100644 --- a/revision.h +++ b/revision.h @@ -148,6 +148,7 @@ struct rev_info { edge_hint_aggressive:1, limited:1, unpacked:1, + no_kept_objects:1, boundary:2, count:1, left_right:1, @@ -317,6 +318,9 @@ struct rev_info { * This is loaded from the commit-graph being used. */ struct bloom_filter_settings *bloom_filter_settings; + + /* misc. flags related to '--no-kept-objects' */ + unsigned keep_pack_cache_flags; }; int ref_excluded(struct string_list *, const char *path); diff --git a/run-command.c b/run-command.c index 4e34623e2e..be6bc128cd 100644 --- a/run-command.c +++ b/run-command.c @@ -1638,8 +1638,8 @@ static void pp_init(struct parallel_processes *pp, pp->nr_processes = 0; pp->output_owner = 0; pp->shutdown = 0; - pp->children = xcalloc(n, sizeof(*pp->children)); - pp->pfd = xcalloc(n, sizeof(*pp->pfd)); + CALLOC_ARRAY(pp->children, n); + CALLOC_ARRAY(pp->pfd, n); strbuf_init(&pp->buffered_output, 0); for (i = 0; i < n; i++) { diff --git a/send-pack.c b/send-pack.c index 9045f8a082..5f215b13c7 100644 --- a/send-pack.c +++ b/send-pack.c @@ -188,13 +188,13 @@ static int receive_status(struct packet_reader *reader, struct ref *refs) } if (new_report) { if (!hint->report) { - hint->report = xcalloc(1, sizeof(struct ref_push_report)); + CALLOC_ARRAY(hint->report, 1); report = hint->report; } else { report = hint->report; while (report->next) report = report->next; - report->next = xcalloc(1, sizeof(struct ref_push_report)); + CALLOC_ARRAY(report->next, 1); report = report->next; } new_report = 0; diff --git a/sequencer.c b/sequencer.c index d2332d3e17..848204d3dc 100644 --- a/sequencer.c +++ b/sequencer.c @@ -5171,7 +5171,7 @@ void todo_list_add_exec_commands(struct todo_list *todo_list, int i, insert, nr = 0, alloc = 0; struct todo_item *items = NULL, *base_items = NULL; - base_items = xcalloc(commands->nr, sizeof(struct todo_item)); + CALLOC_ARRAY(base_items, commands->nr); for (i = 0; i < commands->nr; i++) { size_t command_len = strlen(commands->items[i].string); diff --git a/server-info.c b/server-info.c index bae2cdfd51..de0aa4498c 100644 --- a/server-info.c +++ b/server-info.c @@ -296,7 +296,7 @@ static void init_pack_info(const char *infofile, int force) i = num_pack++; ALLOC_GROW(info, num_pack, alloc); - info[i] = xcalloc(1, sizeof(struct pack_info)); + CALLOC_ARRAY(info[i], 1); info[i]->p = p; info[i]->old_num = -1; } diff --git a/split-index.c b/split-index.c index c0e8ad670d..94937d21a3 100644 --- a/split-index.c +++ b/split-index.c @@ -5,7 +5,7 @@ struct split_index *init_split_index(struct index_state *istate) { if (!istate->split_index) { - istate->split_index = xcalloc(1, sizeof(*istate->split_index)); + CALLOC_ARRAY(istate->split_index, 1); istate->split_index->refcount = 1; } return istate->split_index; @@ -87,7 +87,7 @@ void move_cache_to_base_index(struct index_state *istate) mem_pool_combine(istate->ce_mem_pool, istate->split_index->base->ce_mem_pool); } - si->base = xcalloc(1, sizeof(*si->base)); + CALLOC_ARRAY(si->base, 1); si->base->version = istate->version; /* zero timestamp disables racy test in ce_write_index() */ si->base->timestamp = istate->timestamp; @@ -387,9 +387,6 @@ GIT_TEST_COMMIT_GRAPH=<boolean>, when true, forces the commit-graph to be written after every 'git commit' command, and overrides the 'core.commitGraph' setting to true. -GIT_TEST_COMMIT_GRAPH_NO_GDAT=<boolean>, when true, forces the -commit-graph to be written without generation data chunk. - GIT_TEST_COMMIT_GRAPH_CHANGED_PATHS=<boolean>, when true, forces commit-graph write to compute and write changed path Bloom filters for every 'git commit-graph write', as if the `--changed-paths` option was diff --git a/t/helper/test-bloom.c b/t/helper/test-bloom.c index 46e97b04eb..2a1ae3dae6 100644 --- a/t/helper/test-bloom.c +++ b/t/helper/test-bloom.c @@ -69,7 +69,7 @@ int cmd__bloom(int argc, const char **argv) struct bloom_filter filter; int i = 2; filter.len = (settings.bits_per_entry + BITS_PER_WORD - 1) / BITS_PER_WORD; - filter.data = xcalloc(filter.len, sizeof(unsigned char)); + CALLOC_ARRAY(filter.data, filter.len); if (argc - 1 < i) usage(bloom_usage); diff --git a/t/perf/p5303-many-packs.sh b/t/perf/p5303-many-packs.sh index ce0c42cc9f..35c0cbdf49 100755 --- a/t/perf/p5303-many-packs.sh +++ b/t/perf/p5303-many-packs.sh @@ -28,11 +28,18 @@ repack_into_n () { push @commits, $_ if $. % 5 == 1; } print reverse @commits; - ' "$1" >pushes + ' "$1" >pushes && # create base packfile - head -n 1 pushes | - git pack-objects --delta-base-offset --revs staging/pack + base_pack=$( + head -n 1 pushes | + git pack-objects --delta-base-offset --revs staging/pack + ) && + test_export base_pack && + + # create an empty packfile + empty_pack=$(git pack-objects staging/pack </dev/null) && + test_export empty_pack && # and then incrementals between each pair of commits last= && @@ -49,6 +56,12 @@ repack_into_n () { last=$rev done <pushes && + ( + find staging -type f -name 'pack-*.pack' | + xargs -n 1 basename | grep -v "$base_pack" && + printf "^pack-%s.pack\n" $base_pack + ) >stdin.packs + # and install the whole thing rm -f .git/objects/pack/* && mv staging/* .git/objects/pack/ @@ -91,6 +104,23 @@ do --reflog --indexed-objects --delta-base-offset \ --stdout </dev/null >/dev/null ' + + test_perf "repack with kept ($nr_packs)" ' + git pack-objects --keep-true-parents \ + --keep-pack=pack-$empty_pack.pack \ + --honor-pack-keep --non-empty --all \ + --reflog --indexed-objects --delta-base-offset \ + --stdout </dev/null >/dev/null + ' + + test_perf "repack with --stdin-packs ($nr_packs)" ' + git pack-objects \ + --keep-true-parents \ + --stdin-packs \ + --non-empty \ + --delta-base-offset \ + --stdout <stdin.packs >/dev/null + ' done # Measure pack loading with 10,000 packs. diff --git a/t/perf/perf-lib.sh b/t/perf/perf-lib.sh index e385c6896f..601d9f67dd 100644 --- a/t/perf/perf-lib.sh +++ b/t/perf/perf-lib.sh @@ -70,6 +70,19 @@ test_perf_do_repo_symlink_config_ () { test_have_prereq SYMLINKS || git config core.symlinks false } +test_perf_copy_repo_contents () { + for stuff in "$1"/* + do + case "$stuff" in + */objects|*/hooks|*/config|*/commondir|*/gitdir|*/worktrees) + ;; + *) + cp -R "$stuff" "$repo/.git/" || exit 1 + ;; + esac + done +} + test_perf_create_repo_from () { test "$#" = 2 || BUG "not 2 parameters to test-create-repo" @@ -77,20 +90,20 @@ test_perf_create_repo_from () { source="$2" source_git="$("$MODERN_GIT" -C "$source" rev-parse --git-dir)" objects_dir="$("$MODERN_GIT" -C "$source" rev-parse --git-path objects)" + common_dir="$("$MODERN_GIT" -C "$source" rev-parse --git-common-dir)" mkdir -p "$repo/.git" ( cd "$source" && { cp -Rl "$objects_dir" "$repo/.git/" 2>/dev/null || cp -R "$objects_dir" "$repo/.git/"; } && - for stuff in "$source_git"/*; do - case "$stuff" in - */objects|*/hooks|*/config|*/commondir) - ;; - *) - cp -R "$stuff" "$repo/.git/" || exit 1 - ;; - esac - done + + # common_dir must come first here, since we want source_git to + # take precedence and overwrite any overlapping files + test_perf_copy_repo_contents "$common_dir" + if test "$source_git" != "$common_dir" + then + test_perf_copy_repo_contents "$source_git" + fi ) && ( cd "$repo" && diff --git a/t/t0003-attributes.sh b/t/t0003-attributes.sh index b660593c20..1e4c672b84 100755 --- a/t/t0003-attributes.sh +++ b/t/t0003-attributes.sh @@ -4,12 +4,16 @@ test_description=gitattributes . ./test-lib.sh -attr_check () { +attr_check_basic () { path="$1" expect="$2" git_opts="$3" && git $git_opts check-attr test -- "$path" >actual 2>err && echo "$path: test: $expect" >expect && - test_cmp expect actual && + test_cmp expect actual +} + +attr_check () { + attr_check_basic "$@" && test_must_be_empty err } @@ -331,7 +335,6 @@ test_expect_success 'binary macro expanded by -a' ' test_cmp expect actual ' - test_expect_success 'query binary macro directly' ' echo "file binary" >.gitattributes && echo file: binary: set >expect && @@ -339,4 +342,31 @@ test_expect_success 'query binary macro directly' ' test_cmp expect actual ' +test_expect_success SYMLINKS 'set up symlink tests' ' + echo "* test" >attr && + rm -f .gitattributes +' + +test_expect_success SYMLINKS 'symlinks respected in core.attributesFile' ' + test_when_finished "rm symlink" && + ln -s attr symlink && + test_config core.attributesFile "$(pwd)/symlink" && + attr_check file set +' + +test_expect_success SYMLINKS 'symlinks respected in info/attributes' ' + test_when_finished "rm .git/info/attributes" && + ln -s ../../attr .git/info/attributes && + attr_check file set +' + +test_expect_success SYMLINKS 'symlinks not respected in-tree' ' + test_when_finished "rm -rf .gitattributes subdir" && + ln -s attr .gitattributes && + mkdir subdir && + ln -s ../attr subdir/.gitattributes && + attr_check_basic subdir/file unspecified && + test_i18ngrep "unable to access.*gitattributes" err +' + test_done diff --git a/t/t0008-ignores.sh b/t/t0008-ignores.sh index f7abde62f6..a594b4aa7d 100755 --- a/t/t0008-ignores.sh +++ b/t/t0008-ignores.sh @@ -865,4 +865,38 @@ test_expect_success 'info/exclude trumps core.excludesfile' ' test_cmp expect actual ' +test_expect_success SYMLINKS 'set up ignore file for symlink tests' ' + echo "*" >ignore && + rm -f .gitignore .git/info/exclude +' + +test_expect_success SYMLINKS 'symlinks respected in core.excludesFile' ' + test_when_finished "rm symlink" && + ln -s ignore symlink && + test_config core.excludesFile "$(pwd)/symlink" && + echo file >expect && + git check-ignore file >actual 2>err && + test_cmp expect actual && + test_must_be_empty err +' + +test_expect_success SYMLINKS 'symlinks respected in info/exclude' ' + test_when_finished "rm .git/info/exclude" && + ln -s ../../ignore .git/info/exclude && + echo file >expect && + git check-ignore file >actual 2>err && + test_cmp expect actual && + test_must_be_empty err +' + +test_expect_success SYMLINKS 'symlinks not respected in-tree' ' + test_when_finished "rm .gitignore" && + ln -s ignore .gitignore && + mkdir subdir && + ln -s ignore subdir/.gitignore && + test_must_fail git check-ignore subdir/file >actual 2>err && + test_must_be_empty actual && + test_i18ngrep "unable to access.*gitignore" err +' + test_done diff --git a/t/t0021-conversion.sh b/t/t0021-conversion.sh index a9e10a0c21..b5749f327d 100755 --- a/t/t0021-conversion.sh +++ b/t/t0021-conversion.sh @@ -257,6 +257,30 @@ test_expect_success 'required filter clean failure' ' test_must_fail git add test.fc ' +test_expect_success 'required filter with absent clean field' ' + test_config filter.absentclean.smudge cat && + test_config filter.absentclean.required true && + + echo "*.ac filter=absentclean" >.gitattributes && + + echo test >test.ac && + test_must_fail git add test.ac 2>stderr && + test_i18ngrep "fatal: test.ac: clean filter .absentclean. failed" stderr +' + +test_expect_success 'required filter with absent smudge field' ' + test_config filter.absentsmudge.clean cat && + test_config filter.absentsmudge.required true && + + echo "*.as filter=absentsmudge" >.gitattributes && + + echo test >test.as && + git add test.as && + rm -f test.as && + test_must_fail git checkout -- test.as 2>stderr && + test_i18ngrep "fatal: test.as: smudge filter absentsmudge failed" stderr +' + test_expect_success 'filtering large input to small output should use little memory' ' test_config filter.devnull.clean "cat >/dev/null" && test_config filter.devnull.required true && diff --git a/t/t3905-stash-include-untracked.sh b/t/t3905-stash-include-untracked.sh index 598b17f6d4..8314ab21d4 100755 --- a/t/t3905-stash-include-untracked.sh +++ b/t/t3905-stash-include-untracked.sh @@ -297,4 +297,112 @@ test_expect_success 'stash -u with globs' ' test_path_is_missing untracked.txt ' +test_expect_success 'stash show --include-untracked shows untracked files' ' + git reset --hard && + git clean -xf && + >untracked && + >tracked && + git add tracked && + empty_blob_oid=$(git rev-parse --short :tracked) && + git stash -u && + + cat >expect <<-EOF && + tracked | 0 + untracked | 0 + 2 files changed, 0 insertions(+), 0 deletions(-) + EOF + git stash show --include-untracked >actual && + test_cmp expect actual && + git stash show -u >actual && + test_cmp expect actual && + git stash show --no-include-untracked --include-untracked >actual && + test_cmp expect actual && + git stash show --only-untracked --include-untracked >actual && + test_cmp expect actual && + git -c stash.showIncludeUntracked=true stash show >actual && + test_cmp expect actual && + + cat >expect <<-EOF && + diff --git a/tracked b/tracked + new file mode 100644 + index 0000000..$empty_blob_oid + diff --git a/untracked b/untracked + new file mode 100644 + index 0000000..$empty_blob_oid + EOF + git stash show -p --include-untracked >actual && + test_cmp expect actual && + git stash show --include-untracked -p >actual && + test_cmp expect actual +' + +test_expect_success 'stash show --only-untracked only shows untracked files' ' + git reset --hard && + git clean -xf && + >untracked && + >tracked && + git add tracked && + empty_blob_oid=$(git rev-parse --short :tracked) && + git stash -u && + + cat >expect <<-EOF && + untracked | 0 + 1 file changed, 0 insertions(+), 0 deletions(-) + EOF + git stash show --only-untracked >actual && + test_cmp expect actual && + git stash show --no-include-untracked --only-untracked >actual && + test_cmp expect actual && + git stash show --include-untracked --only-untracked >actual && + test_cmp expect actual && + + cat >expect <<-EOF && + diff --git a/untracked b/untracked + new file mode 100644 + index 0000000..$empty_blob_oid + EOF + git stash show -p --only-untracked >actual && + test_cmp expect actual && + git stash show --only-untracked -p >actual && + test_cmp expect actual +' + +test_expect_success 'stash show --no-include-untracked cancels --{include,show}-untracked' ' + git reset --hard && + git clean -xf && + >untracked && + >tracked && + git add tracked && + git stash -u && + + cat >expect <<-EOF && + tracked | 0 + 1 file changed, 0 insertions(+), 0 deletions(-) + EOF + git stash show --only-untracked --no-include-untracked >actual && + test_cmp expect actual && + git stash show --include-untracked --no-include-untracked >actual && + test_cmp expect actual +' + +test_expect_success 'stash show --include-untracked errors on duplicate files' ' + git reset --hard && + git clean -xf && + >tracked && + git add tracked && + tree=$(git write-tree) && + i_commit=$(git commit-tree -p HEAD -m "index on any-branch" "$tree") && + test_when_finished "rm -f untracked_index" && + u_commit=$( + GIT_INDEX_FILE="untracked_index" && + export GIT_INDEX_FILE && + git update-index --add tracked && + u_tree=$(git write-tree) && + git commit-tree -m "untracked files on any-branch" "$u_tree" + ) && + w_commit=$(git commit-tree -p HEAD -p "$i_commit" -p "$u_commit" -m "WIP on any-branch" "$tree") && + test_must_fail git stash show --include-untracked "$w_commit" 2>err && + test_i18ngrep "worktree and untracked commit have duplicate entries: tracked" err +' + test_done diff --git a/t/t4203-mailmap.sh b/t/t4203-mailmap.sh index 93caf9a46d..d8e7374234 100755 --- a/t/t4203-mailmap.sh +++ b/t/t4203-mailmap.sh @@ -932,4 +932,35 @@ test_expect_success 'find top-level mailmap from subdir' ' test_cmp expect actual ' +test_expect_success SYMLINKS 'set up symlink tests' ' + git commit --allow-empty -m foo --author="Orig <orig@example.com>" && + echo "New <new@example.com> <orig@example.com>" >map && + rm -f .mailmap +' + +test_expect_success SYMLINKS 'symlinks respected in mailmap.file' ' + test_when_finished "rm symlink" && + ln -s map symlink && + git -c mailmap.file="$(pwd)/symlink" log -1 --format=%aE >actual && + echo "new@example.com" >expect && + test_cmp expect actual +' + +test_expect_success SYMLINKS 'symlinks respected in non-repo shortlog' ' + git log -1 >input && + test_when_finished "nongit rm .mailmap" && + nongit ln -sf "$TRASH_DIRECTORY/map" .mailmap && + nongit git shortlog -s <input >actual && + echo " 1 New" >expect && + test_cmp expect actual +' + +test_expect_success SYMLINKS 'symlinks not respected in-tree' ' + test_when_finished "rm .mailmap" && + ln -s map .mailmap && + git log -1 --format=%aE >actual && + echo "orig@example.com" >expect&& + test_cmp expect actual +' + test_done diff --git a/t/t4205-log-pretty-formats.sh b/t/t4205-log-pretty-formats.sh index 85432b80ff..cabdf7d57a 100755 --- a/t/t4205-log-pretty-formats.sh +++ b/t/t4205-log-pretty-formats.sh @@ -962,4 +962,39 @@ test_expect_success 'log --pretty=reference is colored appropriately' ' test_cmp expect actual ' +test_expect_success '%(describe) vs git describe' ' + git log --format="%H" | while read hash + do + if desc=$(git describe $hash) + then + : >expect-contains-good + else + : >expect-contains-bad + fi && + echo "$hash $desc" + done >expect && + test_path_exists expect-contains-good && + test_path_exists expect-contains-bad && + + git log --format="%H %(describe)" >actual 2>err && + test_cmp expect actual && + test_must_be_empty err +' + +test_expect_success '%(describe:match=...) vs git describe --match ...' ' + test_when_finished "git tag -d tag-match" && + git tag -a -m tagged tag-match&& + git describe --match "*-match" >expect && + git log -1 --format="%(describe:match=*-match)" >actual && + test_cmp expect actual +' + +test_expect_success '%(describe:exclude=...) vs git describe --exclude ...' ' + test_when_finished "git tag -d tag-exclude" && + git tag -a -m tagged tag-exclude && + git describe --exclude "*-exclude" >expect && + git log -1 --format="%(describe:exclude=*-exclude)" >actual && + test_cmp expect actual +' + test_done diff --git a/t/t5001-archive-attr.sh b/t/t5001-archive-attr.sh index e9aa97117a..712ae52299 100755 --- a/t/t5001-archive-attr.sh +++ b/t/t5001-archive-attr.sh @@ -128,4 +128,18 @@ test_expect_success 'export-subst' ' test_cmp substfile2 archive/substfile2 ' +test_expect_success 'export-subst expands %(describe) once' ' + echo "\$Format:%(describe)\$" >substfile3 && + echo "\$Format:%(describe)\$" >>substfile3 && + echo "\$Format:%(describe)${LF}%(describe)\$" >substfile4 && + git add substfile[34] && + git commit -m export-subst-describe && + git tag -m export-subst-describe export-subst-describe && + git archive HEAD >archive-describe.tar && + extract_tar_to_dir archive-describe && + desc=$(git describe) && + grep -F "$desc" archive-describe/substfile[34] >substituted && + test_line_count = 1 substituted +' + test_done diff --git a/t/t5300-pack-object.sh b/t/t5300-pack-object.sh index e830a37a38..2fc5e68250 100755 --- a/t/t5300-pack-object.sh +++ b/t/t5300-pack-object.sh @@ -534,4 +534,139 @@ test_expect_success 'prefetch objects' ' test_line_count = 1 donelines ' +test_expect_success 'setup for --stdin-packs tests' ' + git init stdin-packs && + ( + cd stdin-packs && + + test_commit A && + test_commit B && + test_commit C && + + for id in A B C + do + git pack-objects .git/objects/pack/pack-$id \ + --incremental --revs <<-EOF + refs/tags/$id + EOF + done && + + ls -la .git/objects/pack + ) +' + +test_expect_success '--stdin-packs with excluded packs' ' + ( + cd stdin-packs && + + PACK_A="$(basename .git/objects/pack/pack-A-*.pack)" && + PACK_B="$(basename .git/objects/pack/pack-B-*.pack)" && + PACK_C="$(basename .git/objects/pack/pack-C-*.pack)" && + + git pack-objects test --stdin-packs <<-EOF && + $PACK_A + ^$PACK_B + $PACK_C + EOF + + ( + git show-index <$(ls .git/objects/pack/pack-A-*.idx) && + git show-index <$(ls .git/objects/pack/pack-C-*.idx) + ) >expect.raw && + git show-index <$(ls test-*.idx) >actual.raw && + + cut -d" " -f2 <expect.raw | sort >expect && + cut -d" " -f2 <actual.raw | sort >actual && + test_cmp expect actual + ) +' + +test_expect_success '--stdin-packs is incompatible with --filter' ' + ( + cd stdin-packs && + test_must_fail git pack-objects --stdin-packs --stdout \ + --filter=blob:none </dev/null 2>err && + test_i18ngrep "cannot use --filter with --stdin-packs" err + ) +' + +test_expect_success '--stdin-packs is incompatible with --revs' ' + ( + cd stdin-packs && + test_must_fail git pack-objects --stdin-packs --revs out \ + </dev/null 2>err && + test_i18ngrep "cannot use internal rev list with --stdin-packs" err + ) +' + +test_expect_success '--stdin-packs with loose objects' ' + ( + cd stdin-packs && + + PACK_A="$(basename .git/objects/pack/pack-A-*.pack)" && + PACK_B="$(basename .git/objects/pack/pack-B-*.pack)" && + PACK_C="$(basename .git/objects/pack/pack-C-*.pack)" && + + test_commit D && # loose + + git pack-objects test2 --stdin-packs --unpacked <<-EOF && + $PACK_A + ^$PACK_B + $PACK_C + EOF + + ( + git show-index <$(ls .git/objects/pack/pack-A-*.idx) && + git show-index <$(ls .git/objects/pack/pack-C-*.idx) && + git rev-list --objects --no-object-names \ + refs/tags/C..refs/tags/D + + ) >expect.raw && + ls -la . && + git show-index <$(ls test2-*.idx) >actual.raw && + + cut -d" " -f2 <expect.raw | sort >expect && + cut -d" " -f2 <actual.raw | sort >actual && + test_cmp expect actual + ) +' + +test_expect_success '--stdin-packs with broken links' ' + ( + cd stdin-packs && + + # make an unreachable object with a bogus parent + git cat-file -p HEAD >commit && + sed "s/$(git rev-parse HEAD^)/$(test_oid zero)/" <commit | + git hash-object -w -t commit --stdin >in && + + git pack-objects .git/objects/pack/pack-D <in && + + PACK_A="$(basename .git/objects/pack/pack-A-*.pack)" && + PACK_B="$(basename .git/objects/pack/pack-B-*.pack)" && + PACK_C="$(basename .git/objects/pack/pack-C-*.pack)" && + PACK_D="$(basename .git/objects/pack/pack-D-*.pack)" && + + git pack-objects test3 --stdin-packs --unpacked <<-EOF && + $PACK_A + ^$PACK_B + $PACK_C + $PACK_D + EOF + + ( + git show-index <$(ls .git/objects/pack/pack-A-*.idx) && + git show-index <$(ls .git/objects/pack/pack-C-*.idx) && + git show-index <$(ls .git/objects/pack/pack-D-*.idx) && + git rev-list --objects --no-object-names \ + refs/tags/C..refs/tags/D + ) >expect.raw && + git show-index <$(ls test3-*.idx) >actual.raw && + + cut -d" " -f2 <expect.raw | sort >expect && + cut -d" " -f2 <actual.raw | sort >actual && + test_cmp expect actual + ) +' + test_done diff --git a/t/t5318-commit-graph.sh b/t/t5318-commit-graph.sh index edeb6d6d31..af88f805aa 100755 --- a/t/t5318-commit-graph.sh +++ b/t/t5318-commit-graph.sh @@ -475,7 +475,7 @@ test_expect_success 'lower layers have overflow chunk' ' test_expect_success 'git commit-graph verify' ' cd "$TRASH_DIRECTORY/full" && - git rev-parse commits/8 | GIT_TEST_COMMIT_GRAPH_NO_GDAT=1 git commit-graph write --stdin-commits && + git rev-parse commits/8 | git -c commitGraph.generationVersion=1 commit-graph write --stdin-commits && git commit-graph verify >output && graph_read_expect 9 extra_edges ' diff --git a/t/t5324-split-commit-graph.sh b/t/t5324-split-commit-graph.sh index 8e90f3423b..587226ed10 100755 --- a/t/t5324-split-commit-graph.sh +++ b/t/t5324-split-commit-graph.sh @@ -489,7 +489,7 @@ test_expect_success 'setup repo for mixed generation commit-graph-chain' ' test_commit $i && git branch commits/$i || return 1 done && - git commit-graph write --reachable --split && + git -c commitGraph.generationVersion=2 commit-graph write --reachable --split && graph_read_expect $NUM_FIRST_LAYER_COMMITS && test_line_count = 1 $graphdir/commit-graph-chain && for i in $(test_seq $SECOND_LAYER_SEQUENCE_START $SECOND_LAYER_SEQUENCE_END) @@ -497,7 +497,7 @@ test_expect_success 'setup repo for mixed generation commit-graph-chain' ' test_commit $i && git branch commits/$i || return 1 done && - GIT_TEST_COMMIT_GRAPH_NO_GDAT=1 git commit-graph write --reachable --split=no-merge && + git -c commitGraph.generationVersion=1 commit-graph write --reachable --split=no-merge && test_line_count = 2 $graphdir/commit-graph-chain && test-tool read-graph >output && cat >expect <<-EOF && diff --git a/t/t5505-remote.sh b/t/t5505-remote.sh index 8c462f20ae..c7b392794b 100755 --- a/t/t5505-remote.sh +++ b/t/t5505-remote.sh @@ -594,6 +594,7 @@ test_expect_success 'add --no-tags' ' cd add-no-tags && git init && git remote add -f --no-tags origin ../one && + grep tagOpt .git/config && git tag -l some-tag >../test/output && git tag -l foobar-tag >../test/output && git config remote.origin.tagopt >>../test/output @@ -756,6 +757,7 @@ test_expect_success 'rename a remote' ' cd four && git config branch.main.pushRemote origin && git remote rename origin upstream && + grep "pushRemote" .git/config && test -z "$(git for-each-ref refs/remotes/origin)" && test "$(git symbolic-ref refs/remotes/upstream/HEAD)" = "refs/remotes/upstream/main" && test "$(git rev-parse upstream/main)" = "$(git rev-parse main)" && @@ -772,6 +774,7 @@ test_expect_success 'rename a remote renames repo remote.pushDefault' ' cd four.1 && git config remote.pushDefault origin && git remote rename origin upstream && + grep pushDefault .git/config && test "$(git config --local remote.pushDefault)" = "upstream" ) ' diff --git a/t/t5606-clone-options.sh b/t/t5606-clone-options.sh index 52e5789fb0..428b0aac93 100755 --- a/t/t5606-clone-options.sh +++ b/t/t5606-clone-options.sh @@ -104,12 +104,20 @@ test_expect_success 'redirected clone -v does show progress' ' ' +test_expect_success 'clone does not segfault with --bare and core.bare=false' ' + test_config_global core.bare false && + git clone --bare parent clone-bare && + echo true >expect && + git -C clone-bare rev-parse --is-bare-repository >actual && + test_cmp expect actual +' + test_expect_success 'chooses correct default initial branch name' ' GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME= \ git -c init.defaultBranch=foo init --bare empty && test_config -C empty lsrefs.unborn advertise && GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME= \ - git -c init.defaultBranch=up clone empty whats-up && + git -c init.defaultBranch=up -c protocol.version=2 clone empty whats-up && test refs/heads/foo = $(git -C whats-up symbolic-ref HEAD) && test refs/heads/foo = $(git -C whats-up config branch.foo.merge) ' diff --git a/t/t5612-clone-refspec.sh b/t/t5612-clone-refspec.sh index 6a6af7449c..3126cfd7e9 100755 --- a/t/t5612-clone-refspec.sh +++ b/t/t5612-clone-refspec.sh @@ -97,6 +97,7 @@ test_expect_success 'by default no tags will be kept updated' ' test_expect_success 'clone with --no-tags' ' ( cd dir_all_no_tags && + grep tagOpt .git/config && git fetch && git for-each-ref refs/tags >../actual ) && diff --git a/t/t6030-bisect-porcelain.sh b/t/t6030-bisect-porcelain.sh index 0ba5a91b4e..32bb66e1ed 100755 --- a/t/t6030-bisect-porcelain.sh +++ b/t/t6030-bisect-porcelain.sh @@ -939,4 +939,16 @@ test_expect_success 'git bisect reset cleans bisection state properly' ' test_path_is_missing ".git/BISECT_START" ' +test_expect_success 'bisect handles annotated tags' ' + test_commit commit-one && + git tag -m foo tag-one && + test_commit commit-two && + git tag -m foo tag-two && + git bisect start && + git bisect good tag-one && + git bisect bad tag-two >output && + bad=$(git rev-parse --verify tag-two^{commit}) && + grep "$bad is the first bad commit" output +' + test_done diff --git a/t/t6114-keep-packs.sh b/t/t6114-keep-packs.sh new file mode 100755 index 0000000000..9239d8aa46 --- /dev/null +++ b/t/t6114-keep-packs.sh @@ -0,0 +1,69 @@ +#!/bin/sh + +test_description='rev-list with .keep packs' +. ./test-lib.sh + +test_expect_success 'setup' ' + test_commit loose && + test_commit packed && + test_commit kept && + + KEPT_PACK=$(git pack-objects --revs .git/objects/pack/pack <<-EOF + refs/tags/kept + ^refs/tags/packed + EOF + ) && + MISC_PACK=$(git pack-objects --revs .git/objects/pack/pack <<-EOF + refs/tags/packed + ^refs/tags/loose + EOF + ) && + + touch .git/objects/pack/pack-$KEPT_PACK.keep +' + +rev_list_objects () { + git rev-list "$@" >out && + sort out +} + +idx_objects () { + git show-index <$1 >expect-idx && + cut -d" " -f2 <expect-idx | sort +} + +test_expect_success '--no-kept-objects excludes trees and blobs in .keep packs' ' + rev_list_objects --objects --all --no-object-names >kept && + rev_list_objects --objects --all --no-object-names --no-kept-objects >no-kept && + + idx_objects .git/objects/pack/pack-$KEPT_PACK.idx >expect && + comm -3 kept no-kept >actual && + + test_cmp expect actual +' + +test_expect_success '--no-kept-objects excludes kept non-MIDX object' ' + test_config core.multiPackIndex true && + + # Create a pack with just the commit object in pack, and do not mark it + # as kept (even though it appears in $KEPT_PACK, which does have a .keep + # file). + MIDX_PACK=$(git pack-objects .git/objects/pack/pack <<-EOF + $(git rev-parse kept) + EOF + ) && + + # Write a MIDX containing all packs, but use the version of the commit + # at "kept" in a non-kept pack by touching $MIDX_PACK. + touch .git/objects/pack/pack-$MIDX_PACK.pack && + git multi-pack-index write && + + rev_list_objects --objects --no-object-names --no-kept-objects HEAD >actual && + ( + idx_objects .git/objects/pack/pack-$MISC_PACK.idx && + git rev-list --objects --no-object-names refs/tags/loose + ) | sort >expect && + test_cmp expect actual +' + +test_done diff --git a/t/t6600-test-reach.sh b/t/t6600-test-reach.sh index e2d33a8a4c..3d7a62ddab 100755 --- a/t/t6600-test-reach.sh +++ b/t/t6600-test-reach.sh @@ -55,7 +55,7 @@ test_expect_success 'setup' ' git show-ref -s commit-5-5 | git commit-graph write --stdin-commits && mv .git/objects/info/commit-graph commit-graph-half && chmod u+w commit-graph-half && - GIT_TEST_COMMIT_GRAPH_NO_GDAT=1 git commit-graph write --reachable && + git -c commitGraph.generationVersion=1 commit-graph write --reachable && mv .git/objects/info/commit-graph commit-graph-no-gdat && chmod u+w commit-graph-no-gdat && git config core.commitGraph true diff --git a/t/t7003-filter-branch.sh b/t/t7003-filter-branch.sh index 1c55695034..1349e5b232 100755 --- a/t/t7003-filter-branch.sh +++ b/t/t7003-filter-branch.sh @@ -506,4 +506,35 @@ test_expect_success 'rewrite repository including refs that point at non-commit ! fgrep fatal filter-output ' +test_expect_success 'filter-branch handles ref deletion' ' + git switch --orphan empty-commit && + git commit --allow-empty -m "empty commit" && + git tag empty && + git branch to-delete && + git filter-branch -f --prune-empty to-delete >out 2>&1 && + grep "to-delete.*was deleted" out && + test_must_fail git rev-parse --verify to-delete +' + +test_expect_success 'filter-branch handles ref rewrite' ' + git checkout empty && + test_commit to-drop && + git branch rewrite && + git filter-branch -f \ + --index-filter "git rm --ignore-unmatch --cached to-drop.t" \ + rewrite >out 2>&1 && + grep "rewrite.*was rewritten" out && + ! grep -i warning out && + git diff-tree empty rewrite +' + +test_expect_success 'filter-branch handles ancestor rewrite' ' + test_commit to-exclude && + git branch ancestor && + git filter-branch -f ancestor -- :^to-exclude.t >out 2>&1 && + grep "ancestor.*was rewritten" out && + ! grep -i warning out && + git diff-tree HEAD^ ancestor +' + test_done diff --git a/t/t7703-repack-geometric.sh b/t/t7703-repack-geometric.sh new file mode 100755 index 0000000000..5ccaa440e0 --- /dev/null +++ b/t/t7703-repack-geometric.sh @@ -0,0 +1,183 @@ +#!/bin/sh + +test_description='git repack --geometric works correctly' + +. ./test-lib.sh + +GIT_TEST_MULTI_PACK_INDEX=0 + +objdir=.git/objects +midx=$objdir/pack/multi-pack-index + +test_expect_success '--geometric with no packs' ' + git init geometric && + test_when_finished "rm -fr geometric" && + ( + cd geometric && + + git repack --geometric 2 >out && + test_i18ngrep "Nothing new to pack" out + ) +' + +test_expect_success '--geometric with one pack' ' + git init geometric && + test_when_finished "rm -fr geometric" && + ( + cd geometric && + + test_commit "base" && + git repack -d && + + git repack --geometric 2 >out && + + test_i18ngrep "Nothing new to pack" out + ) +' + +test_expect_success '--geometric with an intact progression' ' + git init geometric && + test_when_finished "rm -fr geometric" && + ( + cd geometric && + + # These packs already form a geometric progression. + test_commit_bulk --start=1 1 && # 3 objects + test_commit_bulk --start=2 2 && # 6 objects + test_commit_bulk --start=4 4 && # 12 objects + + find $objdir/pack -name "*.pack" | sort >expect && + git repack --geometric 2 -d && + find $objdir/pack -name "*.pack" | sort >actual && + + test_cmp expect actual + ) +' + +test_expect_success '--geometric with loose objects' ' + git init geometric && + test_when_finished "rm -fr geometric" && + ( + cd geometric && + + # These packs already form a geometric progression. + test_commit_bulk --start=1 1 && # 3 objects + test_commit_bulk --start=2 2 && # 6 objects + # The loose objects are packed together, breaking the + # progression. + test_commit loose && # 3 objects + + find $objdir/pack -name "*.pack" | sort >before && + git repack --geometric 2 -d && + find $objdir/pack -name "*.pack" | sort >after && + + comm -13 before after >new && + comm -23 before after >removed && + + test_line_count = 1 new && + test_must_be_empty removed && + + git repack --geometric 2 -d && + find $objdir/pack -name "*.pack" | sort >after && + + # The progression (3, 3, 6) is combined into one new pack. + test_line_count = 1 after + ) +' + +test_expect_success '--geometric with small-pack rollup' ' + git init geometric && + test_when_finished "rm -fr geometric" && + ( + cd geometric && + + test_commit_bulk --start=1 1 && # 3 objects + test_commit_bulk --start=2 1 && # 3 objects + find $objdir/pack -name "*.pack" | sort >small && + test_commit_bulk --start=3 4 && # 12 objects + test_commit_bulk --start=7 8 && # 24 objects + find $objdir/pack -name "*.pack" | sort >before && + + git repack --geometric 2 -d && + + # Three packs in total; two of the existing large ones, and one + # new one. + find $objdir/pack -name "*.pack" | sort >after && + test_line_count = 3 after && + comm -3 small before | tr -d "\t" >large && + grep -qFf large after + ) +' + +test_expect_success '--geometric with small- and large-pack rollup' ' + git init geometric && + test_when_finished "rm -fr geometric" && + ( + cd geometric && + + # size(small1) + size(small2) > size(medium) / 2 + test_commit_bulk --start=1 1 && # 3 objects + test_commit_bulk --start=2 1 && # 3 objects + test_commit_bulk --start=2 3 && # 7 objects + test_commit_bulk --start=6 9 && # 27 objects && + + find $objdir/pack -name "*.pack" | sort >before && + + git repack --geometric 2 -d && + + find $objdir/pack -name "*.pack" | sort >after && + comm -12 before after >untouched && + + # Two packs in total; the largest pack from before running "git + # repack", and one new one. + test_line_count = 1 untouched && + test_line_count = 2 after + ) +' + +test_expect_success '--geometric ignores kept packs' ' + git init geometric && + test_when_finished "rm -fr geometric" && + ( + cd geometric && + + test_commit kept && # 3 objects + test_commit pack && # 3 objects + + KEPT=$(git pack-objects --revs $objdir/pack/pack <<-EOF + refs/tags/kept + EOF + ) && + PACK=$(git pack-objects --revs $objdir/pack/pack <<-EOF + refs/tags/pack + ^refs/tags/kept + EOF + ) && + + # neither pack contains more than twice the number of objects in + # the other, so they should be combined. but, marking one as + # .kept on disk will "freeze" it, so the pack structure should + # remain unchanged. + touch $objdir/pack/pack-$KEPT.keep && + + find $objdir/pack -name "*.pack" | sort >before && + git repack --geometric 2 -d && + find $objdir/pack -name "*.pack" | sort >after && + + # both packs should still exist + test_path_is_file $objdir/pack/pack-$KEPT.pack && + test_path_is_file $objdir/pack/pack-$PACK.pack && + + # and no new packs should be created + test_cmp before after && + + # Passing --pack-kept-objects causes packs with a .keep file to + # be repacked, too. + git repack --geometric 2 -d --pack-kept-objects && + + find $objdir/pack -name "*.pack" >after && + test_line_count = 1 after + ) +' + +test_done diff --git a/t/t9801-git-p4-branch.sh b/t/t9801-git-p4-branch.sh index 56e64697a8..ff94c3f17d 100755 --- a/t/t9801-git-p4-branch.sh +++ b/t/t9801-git-p4-branch.sh @@ -203,19 +203,19 @@ test_expect_success 'git p4 clone simple branches' ' git p4 clone --dest=. --detect-branches //depot@all && git log --all --graph --decorate --stat && git reset --hard p4/depot/branch1 && - test -f file1 && - test -f file2 && - test -f file3 && + test_path_is_file file1 && + test_path_is_file file2 && + test_path_is_file file3 && grep update file2 && git reset --hard p4/depot/branch2 && - test -f file1 && - test -f file2 && + test_path_is_file file1 && + test_path_is_file file2 && test ! -f file3 && ! grep update file2 && git reset --hard p4/depot/branch3 && - test -f file1 && - test -f file2 && - test -f file3 && + test_path_is_file file1 && + test_path_is_file file2 && + test_path_is_file file3 && grep update file2 && cd "$cli" && cd branch1 && @@ -606,22 +606,22 @@ test_expect_success 'git p4 clone simple branches with base folder on server sid git p4 clone --dest=. --use-client-spec --detect-branches //depot@all && git log --all --graph --decorate --stat && git reset --hard p4/depot/branch1 && - test -f file1 && - test -f file2 && - test -f file3 && - test -f sub_file1 && + test_path_is_file file1 && + test_path_is_file file2 && + test_path_is_file file3 && + test_path_is_file sub_file1 && grep update file2 && git reset --hard p4/depot/branch2 && - test -f file1 && - test -f file2 && + test_path_is_file file1 && + test_path_is_file file2 && test ! -f file3 && - test -f sub_file1 && + test_path_is_file sub_file1 && ! grep update file2 && git reset --hard p4/depot/branch3 && - test -f file1 && - test -f file2 && - test -f file3 && - test -f sub_file1 && + test_path_is_file file1 && + test_path_is_file file2 && + test_path_is_file file3 && + test_path_is_file sub_file1 && grep update file2 && cd "$cli" && cd branch1 && @@ -174,7 +174,7 @@ static void print_all(FILE *outfile, struct list_head *head, static struct trailer_item *trailer_from_arg(struct arg_item *arg_tok) { - struct trailer_item *new_item = xcalloc(sizeof(*new_item), 1); + struct trailer_item *new_item = xcalloc(1, sizeof(*new_item)); new_item->token = arg_tok->token; new_item->value = arg_tok->value; arg_tok->token = arg_tok->value = NULL; @@ -445,7 +445,7 @@ static struct arg_item *get_conf_item(const char *name) } /* Item does not already exists, create it */ - item = xcalloc(sizeof(*item), 1); + CALLOC_ARRAY(item, 1); duplicate_conf(&item->conf, &default_conf_info); item->conf.name = xstrdup(name); @@ -664,7 +664,7 @@ static void parse_trailer(struct strbuf *tok, struct strbuf *val, static struct trailer_item *add_trailer_item(struct list_head *head, char *tok, char *val) { - struct trailer_item *new_item = xcalloc(sizeof(*new_item), 1); + struct trailer_item *new_item = xcalloc(1, sizeof(*new_item)); new_item->token = tok; new_item->value = val; list_add_tail(&new_item->list, head); @@ -675,7 +675,7 @@ static void add_arg_item(struct list_head *arg_head, char *tok, char *val, const struct conf_info *conf, const struct new_trailer_item *new_trailer_item) { - struct arg_item *new_item = xcalloc(sizeof(*new_item), 1); + struct arg_item *new_item = xcalloc(1, sizeof(*new_item)); new_item->token = tok; new_item->value = val; duplicate_conf(&new_item->conf, conf); diff --git a/transport-helper.c b/transport-helper.c index 49b7fb4dcb..4cd76366fa 100644 --- a/transport-helper.c +++ b/transport-helper.c @@ -745,13 +745,13 @@ static int push_update_ref_status(struct strbuf *buf, die(_("'option' without a matching 'ok/error' directive")); if (state->new_report) { if (!state->hint->report) { - state->hint->report = xcalloc(1, sizeof(struct ref_push_report)); + CALLOC_ARRAY(state->hint->report, 1); state->report = state->hint->report; } else { state->report = state->hint->report; while (state->report->next) state->report = state->report->next; - state->report->next = xcalloc(1, sizeof(struct ref_push_report)); + CALLOC_ARRAY(state->report->next, 1); state->report = state->report->next; } state->new_report = 0; diff --git a/transport.c b/transport.c index b13fab5dc3..1c4ab676d1 100644 --- a/transport.c +++ b/transport.c @@ -871,7 +871,7 @@ void transport_take_over(struct transport *transport, BUG("taking over transport requires non-NULL " "smart_options field."); - data = xcalloc(1, sizeof(*data)); + CALLOC_ARRAY(data, 1); data->options = *transport->smart_options; data->conn = child; data->fd[0] = data->conn->out; diff --git a/unpack-trees.c b/unpack-trees.c index eb8fcda31b..9af8e796b3 100644 --- a/unpack-trees.c +++ b/unpack-trees.c @@ -1612,8 +1612,8 @@ int unpack_trees(unsigned len, struct tree_desc *t, struct unpack_trees_options o->merge_size = len; mark_all_ce_unused(o->src_index); - if (o->src_index->fsmonitor_last_update) - o->result.fsmonitor_last_update = o->src_index->fsmonitor_last_update; + o->result.fsmonitor_last_update = + xstrdup_or_null(o->src_index->fsmonitor_last_update); /* * Sparse checkout loop #1: set NEW_SKIP_WORKTREE on existing entries @@ -2563,3 +2563,25 @@ int oneway_merge(const struct cache_entry * const *src, } return merged_entry(a, old, o); } + +/* + * Merge worktree and untracked entries in a stash entry. + * + * Ignore all index entries. Collapse remaining trees but make sure that they + * don't have any conflicting files. + */ +int stash_worktree_untracked_merge(const struct cache_entry * const *src, + struct unpack_trees_options *o) +{ + const struct cache_entry *worktree = src[1]; + const struct cache_entry *untracked = src[2]; + + if (o->merge_size != 2) + BUG("invalid merge_size: %d", o->merge_size); + + if (worktree && untracked) + return error(_("worktree and untracked commit have duplicate entries: %s"), + super_prefixed(worktree->name)); + + return merged_entry(worktree ? worktree : untracked, NULL, o); +} diff --git a/unpack-trees.h b/unpack-trees.h index 2e87875b15..2d88b19dca 100644 --- a/unpack-trees.h +++ b/unpack-trees.h @@ -114,5 +114,7 @@ int bind_merge(const struct cache_entry * const *src, struct unpack_trees_options *o); int oneway_merge(const struct cache_entry * const *src, struct unpack_trees_options *o); +int stash_worktree_untracked_merge(const struct cache_entry * const *src, + struct unpack_trees_options *o); #endif diff --git a/vcs-svn/fast_export.h b/vcs-svn/fast_export.h deleted file mode 100644 index 9dcf9337c1..0000000000 --- a/vcs-svn/fast_export.h +++ /dev/null @@ -1,34 +0,0 @@ -#ifndef FAST_EXPORT_H -#define FAST_EXPORT_H - -struct strbuf; -struct line_buffer; - -void fast_export_init(int fd); -void fast_export_deinit(void); - -void fast_export_delete(const char *path); -void fast_export_modify(const char *path, uint32_t mode, const char *dataref); -void fast_export_note(const char *committish, const char *dataref); -void fast_export_begin_note(uint32_t revision, const char *author, - const char *log, timestamp_t timestamp, const char *note_ref); -void fast_export_begin_commit(uint32_t revision, const char *author, - const struct strbuf *log, const char *uuid,const char *url, - timestamp_t timestamp, const char *local_ref); -void fast_export_end_commit(uint32_t revision); -void fast_export_data(uint32_t mode, off_t len, struct line_buffer *input); -void fast_export_buf_to_data(const struct strbuf *data); -void fast_export_blob_delta(uint32_t mode, - uint32_t old_mode, const char *old_data, - off_t len, struct line_buffer *input); - -/* If there is no such file at that rev, returns -1, errno == ENOENT. */ -int fast_export_ls_rev(uint32_t rev, const char *path, - uint32_t *mode_out, struct strbuf *dataref_out); -int fast_export_ls(const char *path, - uint32_t *mode_out, struct strbuf *dataref_out); - -void fast_export_copy(uint32_t revision, const char *src, const char *dst); -const char *fast_export_read_path(const char *path, uint32_t *mode_out); - -#endif diff --git a/vcs-svn/line_buffer.h b/vcs-svn/line_buffer.h deleted file mode 100644 index e192aedea2..0000000000 --- a/vcs-svn/line_buffer.h +++ /dev/null @@ -1,30 +0,0 @@ -#ifndef LINE_BUFFER_H -#define LINE_BUFFER_H - -#include "strbuf.h" - -#define LINE_BUFFER_LEN 10000 - -struct line_buffer { - char line_buffer[LINE_BUFFER_LEN]; - FILE *infile; -}; -#define LINE_BUFFER_INIT { "", NULL } - -int buffer_init(struct line_buffer *buf, const char *filename); -int buffer_fdinit(struct line_buffer *buf, int fd); -int buffer_deinit(struct line_buffer *buf); - -int buffer_tmpfile_init(struct line_buffer *buf); -FILE *buffer_tmpfile_rewind(struct line_buffer *buf); /* prepare to write. */ -long buffer_tmpfile_prepare_to_read(struct line_buffer *buf); - -int buffer_ferror(struct line_buffer *buf); -char *buffer_read_line(struct line_buffer *buf); -int buffer_read_char(struct line_buffer *buf); -size_t buffer_read_binary(struct line_buffer *buf, struct strbuf *sb, size_t len); -/* Returns number of bytes read (not necessarily written). */ -off_t buffer_copy_bytes(struct line_buffer *buf, off_t len); -off_t buffer_skip_bytes(struct line_buffer *buf, off_t len); - -#endif diff --git a/vcs-svn/sliding_window.h b/vcs-svn/sliding_window.h deleted file mode 100644 index a7fc0999cb..0000000000 --- a/vcs-svn/sliding_window.h +++ /dev/null @@ -1,18 +0,0 @@ -#ifndef SLIDING_WINDOW_H -#define SLIDING_WINDOW_H - -#include "strbuf.h" - -struct sliding_view { - struct line_buffer *file; - off_t off; - size_t width; - off_t max_off; /* -1 means unlimited */ - struct strbuf buf; -}; - -#define SLIDING_VIEW_INIT(input, len) { (input), 0, 0, (len), STRBUF_INIT } - -int move_window(struct sliding_view *view, off_t off, size_t width); - -#endif diff --git a/vcs-svn/svndiff.h b/vcs-svn/svndiff.h deleted file mode 100644 index 625d950bb8..0000000000 --- a/vcs-svn/svndiff.h +++ /dev/null @@ -1,10 +0,0 @@ -#ifndef SVNDIFF_H -#define SVNDIFF_H - -struct line_buffer; -struct sliding_view; - -int svndiff0_apply(struct line_buffer *delta, off_t delta_len, - struct sliding_view *preimage, FILE *postimage); - -#endif diff --git a/vcs-svn/svndump.h b/vcs-svn/svndump.h deleted file mode 100644 index 26faed5968..0000000000 --- a/vcs-svn/svndump.h +++ /dev/null @@ -1,10 +0,0 @@ -#ifndef SVNDUMP_H -#define SVNDUMP_H - -int svndump_init(const char *filename); -int svndump_init_fd(int in_fd, int back_fd); -void svndump_read(const char *url, const char *local_ref, const char *notes_ref); -void svndump_deinit(void); -void svndump_reset(void); - -#endif diff --git a/worktree.c b/worktree.c index e00858540e..f35ac40a84 100644 --- a/worktree.c +++ b/worktree.c @@ -53,7 +53,7 @@ static struct worktree *get_main_worktree(void) strbuf_add_real_path(&worktree_path, get_git_common_dir()); strbuf_strip_suffix(&worktree_path, "/.git"); - worktree = xcalloc(1, sizeof(*worktree)); + CALLOC_ARRAY(worktree, 1); worktree->path = strbuf_detach(&worktree_path, NULL); /* * NEEDSWORK: If this function is called from a secondary worktree and @@ -84,7 +84,7 @@ static struct worktree *get_linked_worktree(const char *id) strbuf_rtrim(&worktree_path); strbuf_strip_suffix(&worktree_path, "/.git"); - worktree = xcalloc(1, sizeof(*worktree)); + CALLOC_ARRAY(worktree, 1); worktree->path = strbuf_detach(&worktree_path, NULL); worktree->id = xstrdup(id); add_head_info(worktree); @@ -678,3 +678,19 @@ int is_empty_or_missing_file(const char *filename) return !st.st_size; } + +int open_nofollow(const char *path, int flags) +{ +#ifdef O_NOFOLLOW + return open(path, flags | O_NOFOLLOW); +#else + struct stat st; + if (lstat(path, &st) < 0) + return -1; + if (S_ISLNK(st.st_mode)) { + errno = ELOOP; + return -1; + } + return open(path, flags); +#endif +} diff --git a/wt-status.c b/wt-status.c index 0c8287a023..1aed68c43c 100644 --- a/wt-status.c +++ b/wt-status.c @@ -456,7 +456,7 @@ static void wt_status_collect_changed_cb(struct diff_queue_struct *q, it = string_list_insert(&s->change, p->two->path); d = it->util; if (!d) { - d = xcalloc(1, sizeof(*d)); + CALLOC_ARRAY(d, 1); it->util = d; } if (!d->worktree_status) @@ -540,7 +540,7 @@ static void wt_status_collect_updated_cb(struct diff_queue_struct *q, it = string_list_insert(&s->change, p->two->path); d = it->util; if (!d) { - d = xcalloc(1, sizeof(*d)); + CALLOC_ARRAY(d, 1); it->util = d; } if (!d->index_status) @@ -671,7 +671,7 @@ static void wt_status_collect_changes_initial(struct wt_status *s) it = string_list_insert(&s->change, ce->name); d = it->util; if (!d) { - d = xcalloc(1, sizeof(*d)); + CALLOC_ARRAY(d, 1); it->util = d; } if (ce_stage(ce)) { |