diff options
61 files changed, 1241 insertions, 722 deletions
diff --git a/.clang-format b/.clang-format index 41969eca4b..9547fe1b77 100644 --- a/.clang-format +++ b/.clang-format @@ -32,6 +32,9 @@ AlignConsecutiveAssignments: false # double b = 3.14; AlignConsecutiveDeclarations: false +# Align consecutive macro definitions. +AlignConsecutiveMacros: true + # Align escaped newlines as far left as possible # #define A \ # int aaaa; \ @@ -209,13 +212,14 @@ KeepEmptyLinesAtTheStartOfBlocks: false # Penalties # This decides what order things should be done if a line is too long -PenaltyBreakAssignment: 10 -PenaltyBreakBeforeFirstCallParameter: 30 -PenaltyBreakComment: 10 +PenaltyBreakAssignment: 5 +PenaltyBreakBeforeFirstCallParameter: 5 +PenaltyBreakComment: 5 PenaltyBreakFirstLessLess: 0 -PenaltyBreakString: 10 -PenaltyExcessCharacter: 100 -PenaltyReturnTypeOnItsOwnLine: 60 +PenaltyBreakOpenParenthesis: 300 +PenaltyBreakString: 5 +PenaltyExcessCharacter: 10 +PenaltyReturnTypeOnItsOwnLine: 300 # Don't sort #include's SortIncludes: false diff --git a/Documentation/CodingGuidelines b/Documentation/CodingGuidelines index 30fda4142c..87904791cb 100644 --- a/Documentation/CodingGuidelines +++ b/Documentation/CodingGuidelines @@ -621,6 +621,20 @@ For C programs: - `S_free()` releases a structure's contents and frees the structure. + - Function names should be clear and descriptive, accurately reflecting + their purpose or behavior. Arbitrary suffixes that do not add meaningful + context can lead to confusion, particularly for newcomers to the codebase. + + Historically, the '_1' suffix has been used in situations where: + + - A function handles one element among a group that requires similar + processing. + - A recursive function has been separated from its setup phase. + + The '_1' suffix can be used as a concise way to indicate these specific + cases. However, it is recommended to find a more descriptive name wherever + possible to improve the readability and maintainability of the code. + For Perl programs: - Most of the C guidelines above apply. diff --git a/Documentation/RelNotes/2.47.1.txt b/Documentation/RelNotes/2.47.1.txt new file mode 100644 index 0000000000..39206c09fd --- /dev/null +++ b/Documentation/RelNotes/2.47.1.txt @@ -0,0 +1,31 @@ +Git 2.47.1 Release Notes +======================== + +This is to flush accumulated fixes since 2.47.0 on the 'master' +front down to the maintenance track. + + +Fixes since Git 2.47 +-------------------- + + * Use after free and double freeing at the end in "git log -L... -p" + had been identified and fixed. + + * On macOS, fsmonitor can fall into a race condition that results in + a client waiting forever to be notified for an event that have + already happened. This problem has been corrected. + + * "git maintenance start" crashed due to an uninitialized variable + reference, which has been corrected. + + * Fail gracefully instead of crashing when attempting to write the + contents of a corrupt in-core index as a tree object. + + * A "git fetch" from the superproject going down to a submodule used + a wrong remote when the default remote names are set differently + between them. + + * The "gitk" project tree has been synchronized again with its new + maintainer, Johannes Sixt. + +Also contains minor documentation updates and code clean-ups. diff --git a/Documentation/RelNotes/2.48.0.txt b/Documentation/RelNotes/2.48.0.txt index 75698862c7..b885050c34 100644 --- a/Documentation/RelNotes/2.48.0.txt +++ b/Documentation/RelNotes/2.48.0.txt @@ -16,6 +16,7 @@ UI, Workflows & Features * Teach 'git notes add' and 'git notes append' a new '-e' flag, instructing them to open the note in $GIT_EDITOR before saving. + Performance, Internal Implementation, Development Support etc. -------------------------------------------------------------- @@ -68,6 +69,20 @@ Performance, Internal Implementation, Development Support etc. * Documentation updates to 'git-update-ref(1)'. + * Update the project's CodingGuidelines to discourage naming functions + with a "_1()" suffix. + + * Updates the '.clang-format' to match project conventions. + + * Centralize documentation for repository extensions into a single place. + + * Buildfix and upgrade of Clar to a newer version. + + * Documentation mark-up updates. + + * Renaming a handful of variables and structure fields. + + Fixes since v2.47 ----------------- @@ -77,45 +92,74 @@ Fixes since v2.47 * Use after free and double freeing at the end in "git log -L... -p" had been identified and fixed. - (merge fc5589d6c1 ds/line-log-asan-fix later to maint). * On macOS, fsmonitor can fall into a race condition that results in a client waiting forever to be notified for an event that have already happened. This problem has been corrected. - (merge 51907f8fee jk/fsmonitor-event-listener-race-fix later to maint). * "git maintenance start" crashed due to an uninitialized variable reference, which has been corrected. - (merge c95547a394 ps/maintenance-start-crash-fix later to maint). * Fail gracefully instead of crashing when attempting to write the contents of a corrupt in-core index as a tree object. - (merge ecb5c4318c ps/cache-tree-w-broken-index-entry later to maint). * A "git fetch" from the superproject going down to a submodule used a wrong remote when the default remote names are set differently between them. - (merge 0c1a9987da db/submodule-fetch-with-remote-name-fix later to maint). * Fixes compile time warnings with 64-bit MSVC. - (merge 386d372031 sk/msvc-warnings later to maint). * Teaches 'shortlog' to explicitly use SHA-1 when operating outside of a repository. - (merge b33001645e wm/shortlog-hash later to maint). * Fix 'git grep' regression on macOS by disabling lookahead when encountering invalid UTF-8 byte sequences. - (merge ce025ae4f6 rs/grep-lookahead later to maint). * The dumb-http code regressed when the result of re-indexing a pack yielded an *.idx file that differs in content from the *.idx file it downloaded from the remote. This has been corrected by no longer relying on: the *.idx file we got from the remote. - (merge 863f2459a2 jk/dumb-http-finalize later to maint). + + * When called with '--left-right' and '--use-bitmap-index', 'rev-list' + will produce output without any left/right markers, which has been + corrected. + + * More leakfixes. + + * Test modernization. + + * The "--shallow-exclude=<ref>" option to various history transfer + commands takes a ref, not an arbitrary revision. + + * A regression where commit objects missing from a commit-graph can + cause an infinite loop when doing a fetch in a partial clone has + been fixed. + + * The MinGW compatibility layer has been taught to support POSIX + semantics for atomic renames when other process(es) have a file + opened at the destination path. + + * "git gc" discards any objects that are outside promisor packs that + are referred to by an object in a promisor pack, and we do not + refetch them from the promisor at runtime, resulting an unusable + repository. Work it around by including these objects in the + referring promisor pack at the receiving end of the fetch. + + * Avoid build/test breakage on a system without working malloc debug + support dynamic library. + (merge 72ad6dc368 jk/test-malloc-debug-check later to maint). + + * Double-free fix. + (merge fe17a25905 jk/fetch-prefetch-double-free-fix later to maint). + + * Use of some uninitialized variables in "git difftool" has been + corrected. + + * Object reuse code based on multi-pack-index sent an unwanted copy + of object. + (merge e199290592 tb/multi-pack-reuse-dupfix later to maint). * Other code cleanup, docfix, build fix, etc. - (merge 66893a14d0 ps/leakfixes-part-8 later to maint). (merge 1164e270b5 jk/output-prefix-cleanup later to maint). (merge f36b8cbaef jh/config-unset-doc-fix later to maint). (merge 4154ed4108 js/doc-platform-support-link-fix later to maint). @@ -127,14 +171,4 @@ Fixes since v2.47 (merge 6dab49b9fb tc/bundle-uri-leakfix later to maint). (merge f1ed39987b xx/protocol-v2-doc-markup-fix later to maint). (merge 41869f7447 ak/typofixes later to maint). - (merge f1eea0b620 ak/typofix later to maint). (merge dcd590a39d bf/t-readme-mention-reftable later to maint). - (merge 52acf6771b kh/submitting-patches later to maint). - (merge a73070fbd4 ks/t4205-fixup later to maint). - (merge 9e362dd060 co/t6050-pipefix later to maint). - (merge 91687cd13f sk/t7011-cleanup later to maint). - (merge 19c291e5b2 ua/t3404-cleanup later to maint). - (merge c32d4a8cfe ss/duplicate-typos later to maint). - (merge 09bf122507 sk/t9101-cleanup later to maint). - (merge c348191afe ak/t1016-cleanup later to maint). - (merge f56f9d6c0b ak/more-typofixes later to maint). diff --git a/Documentation/config/add.txt b/Documentation/config/add.txt index 4d753f006e..7497533cbc 100644 --- a/Documentation/config/add.txt +++ b/Documentation/config/add.txt @@ -1,7 +1,12 @@ -add.ignoreErrors:: -add.ignore-errors (deprecated):: - Tells 'git add' to continue adding files when some files cannot be - added due to indexing errors. Equivalent to the `--ignore-errors` - option of linkgit:git-add[1]. `add.ignore-errors` is deprecated, - as it does not follow the usual naming convention for configuration - variables. +`add.ignoreErrors`:: +`add.ignore-errors` (deprecated):: + Tells `git add` to continue adding files when some files cannot be + added due to indexing errors. +ifdef::git-add[] + Equivalent to the `--ignore-errors` option. +endif::git-add[] +ifndef::git-add[] + Equivalent to the `--ignore-errors` option of linkgit:git-add[1]. +endif::git-add[] + `add.ignore-errors` is deprecated, as it does not follow the usual + naming convention for configuration variables. diff --git a/Documentation/config/core.txt b/Documentation/config/core.txt index 60ca9f2b68..8f6d8e7754 100644 --- a/Documentation/config/core.txt +++ b/Documentation/config/core.txt @@ -366,7 +366,7 @@ default in a bare repository. core.repositoryFormatVersion:: Internal variable identifying the repository format and layout - version. + version. See linkgit:gitrepository-layout[5]. core.sharedRepository:: When 'group' (or 'true'), the repository is made shareable between diff --git a/Documentation/config/extensions.txt b/Documentation/config/extensions.txt index f0a784447d..5dc569d1c9 100644 --- a/Documentation/config/extensions.txt +++ b/Documentation/config/extensions.txt @@ -1,17 +1,13 @@ -extensions.objectFormat:: - Specify the hash algorithm to use. The acceptable values are `sha1` and - `sha256`. If not specified, `sha1` is assumed. It is an error to specify - this key unless `core.repositoryFormatVersion` is 1. +extensions.*:: + Unless otherwise stated, is an error to specify an extension if + `core.repositoryFormatVersion` is not `1`. See + linkgit:gitrepository-layout[5]. + -Note that this setting should only be set by linkgit:git-init[1] or -linkgit:git-clone[1]. Trying to change it after initialization will not -work and will produce hard-to-diagnose issues. - -extensions.compatObjectFormat:: - +-- +compatObjectFormat:: Specify a compatibility hash algorithm to use. The acceptable values are `sha1` and `sha256`. The value specified must be different from the - value of extensions.objectFormat. This allows client level + value of `extensions.objectFormat`. This allows client level interoperability between git repositories whose objectFormat matches this compatObjectFormat. In particular when fully implemented the pushes and pulls from a repository in whose objectFormat matches @@ -19,18 +15,55 @@ extensions.compatObjectFormat:: compatObjectFormat in addition to oids encoded with objectFormat to locally specify objects. -extensions.refStorage:: +noop:: + This extension does not change git's behavior at all. It is useful only + for testing format-1 compatibility. ++ +For historical reasons, this extension is respected regardless of the +`core.repositoryFormatVersion` setting. + +noop-v1:: + This extension does not change git's behavior at all. It is useful only + for testing format-1 compatibility. + +objectFormat:: + Specify the hash algorithm to use. The acceptable values are `sha1` and + `sha256`. If not specified, `sha1` is assumed. ++ +Note that this setting should only be set by linkgit:git-init[1] or +linkgit:git-clone[1]. Trying to change it after initialization will not +work and will produce hard-to-diagnose issues. + +partialClone:: + When enabled, indicates that the repo was created with a partial clone + (or later performed a partial fetch) and that the remote may have + omitted sending certain unwanted objects. Such a remote is called a + "promisor remote" and it promises that all such omitted objects can + be fetched from it in the future. ++ +The value of this key is the name of the promisor remote. ++ +For historical reasons, this extension is respected regardless of the +`core.repositoryFormatVersion` setting. + +preciousObjects:: + If enabled, indicates that objects in the repository MUST NOT be deleted + (e.g., by `git-prune` or `git repack -d`). ++ +For historical reasons, this extension is respected regardless of the +`core.repositoryFormatVersion` setting. + +refStorage:: Specify the ref storage format to use. The acceptable values are: + include::../ref-storage-format.txt[] -+ -It is an error to specify this key unless `core.repositoryFormatVersion` is 1. + + Note that this setting should only be set by linkgit:git-init[1] or linkgit:git-clone[1]. Trying to change it after initialization will not work and will produce hard-to-diagnose issues. -extensions.worktreeConfig:: +worktreeConfig:: If enabled, then worktrees will load config settings from the `$GIT_DIR/config.worktree` file in addition to the `$GIT_COMMON_DIR/config` file. Note that `$GIT_COMMON_DIR` and @@ -40,7 +73,7 @@ extensions.worktreeConfig:: `config.worktree` file will override settings from any other config files. + -When enabling `extensions.worktreeConfig`, you must be careful to move +When enabling this extension, you must be careful to move certain values from the common config file to the main working tree's `config.worktree` file, if present: + @@ -48,15 +81,17 @@ certain values from the common config file to the main working tree's `$GIT_COMMON_DIR/config.worktree`. * If `core.bare` is true, then it must be moved from `$GIT_COMMON_DIR/config` to `$GIT_COMMON_DIR/config.worktree`. + + It may also be beneficial to adjust the locations of `core.sparseCheckout` and `core.sparseCheckoutCone` depending on your desire for customizable sparse-checkout settings for each worktree. By default, the `git -sparse-checkout` builtin enables `extensions.worktreeConfig`, assigns +sparse-checkout` builtin enables this extension, assigns these config values on a per-worktree basis, and uses the `$GIT_DIR/info/sparse-checkout` file to specify the sparsity for each worktree independently. See linkgit:git-sparse-checkout[1] for more details. + -For historical reasons, `extensions.worktreeConfig` is respected -regardless of the `core.repositoryFormatVersion` setting. +For historical reasons, this extension is respected regardless of the +`core.repositoryFormatVersion` setting. +-- diff --git a/Documentation/fetch-options.txt b/Documentation/fetch-options.txt index 9dc7ac8dbd..b01372e4b3 100644 --- a/Documentation/fetch-options.txt +++ b/Documentation/fetch-options.txt @@ -29,7 +29,7 @@ Deepen or shorten the history of a shallow repository to include all reachable commits after <date>. ---shallow-exclude=<revision>:: +--shallow-exclude=<ref>:: Deepen or shorten the history of a shallow repository to exclude commits reachable from a specified remote branch or tag. This option can be specified multiple times. diff --git a/Documentation/git-add.txt b/Documentation/git-add.txt index aceaa025e3..5f2c3592b8 100644 --- a/Documentation/git-add.txt +++ b/Documentation/git-add.txt @@ -7,12 +7,12 @@ git-add - Add file contents to the index SYNOPSIS -------- -[verse] -'git add' [--verbose | -v] [--dry-run | -n] [--force | -f] [--interactive | -i] [--patch | -p] - [--edit | -e] [--[no-]all | -A | --[no-]ignore-removal | [--update | -u]] [--sparse] - [--intent-to-add | -N] [--refresh] [--ignore-errors] [--ignore-missing] [--renormalize] - [--chmod=(+|-)x] [--pathspec-from-file=<file> [--pathspec-file-nul]] - [--] [<pathspec>...] +[synopsis] +git add [--verbose | -v] [--dry-run | -n] [--force | -f] [--interactive | -i] [--patch | -p] + [--edit | -e] [--[no-]all | -A | --[no-]ignore-removal | [--update | -u]] [--sparse] + [--intent-to-add | -N] [--refresh] [--ignore-errors] [--ignore-missing] [--renormalize] + [--chmod=(+|-)x] [--pathspec-from-file=<file> [--pathspec-file-nul]] + [--] [<pathspec>...] DESCRIPTION ----------- @@ -41,7 +41,7 @@ The `git add` command will not add ignored files by default. If any ignored files were explicitly specified on the command line, `git add` will fail with a list of ignored files. Ignored files reached by directory recursion or filename globbing performed by Git (quote your -globs before the shell) will be silently ignored. The 'git add' command can +globs before the shell) will be silently ignored. The `git add` command can be used to add ignored files with the `-f` (force) option. Please see linkgit:git-commit[1] for alternative ways to add content to a @@ -50,7 +50,7 @@ commit. OPTIONS ------- -<pathspec>...:: +`<pathspec>...`:: Files to add content from. Fileglobs (e.g. `*.c`) can be given to add all matching files. Also a leading directory name (e.g. `dir` to add `dir/file1` @@ -66,35 +66,35 @@ OPTIONS For more details about the _<pathspec>_ syntax, see the 'pathspec' entry in linkgit:gitglossary[7]. --n:: ---dry-run:: +`-n`:: +`--dry-run`:: Don't actually add the file(s), just show if they exist and/or will be ignored. --v:: ---verbose:: +`-v`:: +`--verbose`:: Be verbose. --f:: ---force:: +`-f`:: +`--force`:: Allow adding otherwise ignored files. ---sparse:: +`--sparse`:: Allow updating index entries outside of the sparse-checkout cone. Normally, `git add` refuses to update index entries whose paths do not fit within the sparse-checkout cone, since those files might be removed from the working tree without warning. See linkgit:git-sparse-checkout[1] for more details. --i:: ---interactive:: +`-i`:: +`--interactive`:: Add modified contents in the working tree interactively to the index. Optional path arguments may be supplied to limit operation to a subset of the working tree. See ``Interactive mode'' for details. --p:: ---patch:: +`-p`:: +`--patch`:: Interactively choose hunks of patch between the index and the work tree and add them to the index. This gives the user a chance to review the difference before adding modified contents to the @@ -104,8 +104,8 @@ This effectively runs `add --interactive`, but bypasses the initial command menu and directly jumps to the `patch` subcommand. See ``Interactive mode'' for details. --e:: ---edit:: +`-e`:: +`--edit`:: Open the diff vs. the index in an editor and let the user edit it. After the editor was closed, adjust the hunk headers and apply the patch to the index. @@ -116,8 +116,8 @@ quicker and more flexible than using the interactive hunk selector. However, it is easy to confuse oneself and create a patch that does not apply to the index. See EDITING PATCHES below. --u:: ---update:: +`-u`:: +`--update`:: Update the index just where it already has an entry matching _<pathspec>_. This removes as well as modifies index entries to match the working tree, but adds no new files. @@ -127,9 +127,9 @@ tracked files in the entire working tree are updated (old versions of Git used to limit the update to the current directory and its subdirectories). --A:: ---all:: ---no-ignore-removal:: +`-A`:: +`--all`:: +`--no-ignore-removal`:: Update the index not only where the working tree has a file matching _<pathspec>_ but also where the index already has an entry. This adds, modifies, and removes index entries to @@ -140,77 +140,77 @@ files in the entire working tree are updated (old versions of Git used to limit the update to the current directory and its subdirectories). ---no-all:: ---ignore-removal:: +`--no-all`:: +`--ignore-removal`:: Update the index by adding new files that are unknown to the index and files modified in the working tree, but ignore files that have been removed from the working tree. This option is a no-op when no _<pathspec>_ is used. + This option is primarily to help users who are used to older -versions of Git, whose "git add _<pathspec>_..." was a synonym -for "git add --no-all _<pathspec>_...", i.e. ignored removed files. +versions of Git, whose `git add <pathspec>...` was a synonym +for `git add --no-all <pathspec>...`, i.e. ignored removed files. --N:: ---intent-to-add:: +`-N`:: +`--intent-to-add`:: Record only the fact that the path will be added later. An entry for the path is placed in the index with no content. This is useful for, among other things, showing the unstaged content of such files with `git diff` and committing them with `git commit -a`. ---refresh:: +`--refresh`:: Don't add the file(s), but only refresh their stat() information in the index. ---ignore-errors:: +`--ignore-errors`:: If some files could not be added because of errors indexing them, do not abort the operation, but continue adding the others. The command shall still exit with non-zero status. The configuration variable `add.ignoreErrors` can be set to true to make this the default behaviour. ---ignore-missing:: - This option can only be used together with --dry-run. By using +`--ignore-missing`:: + This option can only be used together with `--dry-run`. By using this option the user can check if any of the given files would be ignored, no matter if they are already present in the work tree or not. ---no-warn-embedded-repo:: +`--no-warn-embedded-repo`:: By default, `git add` will warn when adding an embedded repository to the index without using `git submodule add` to create an entry in `.gitmodules`. This option will suppress the warning (e.g., if you are manually performing operations on submodules). ---renormalize:: +`--renormalize`:: Apply the "clean" process freshly to all tracked files to forcibly add them again to the index. This is useful after changing `core.autocrlf` configuration or the `text` attribute - in order to correct files added with wrong CRLF/LF line endings. + in order to correct files added with wrong _CRLF/LF_ line endings. This option implies `-u`. Lone CR characters are untouched, thus - while a CRLF cleans to LF, a CRCRLF sequence is only partially - cleaned to CRLF. + while a _CRLF_ cleans to _LF_, a _CRCRLF_ sequence is only partially + cleaned to _CRLF_. ---chmod=(+|-)x:: +`--chmod=(+|-)x`:: Override the executable bit of the added files. The executable bit is only changed in the index, the files on disk are left unchanged. ---pathspec-from-file=<file>:: +`--pathspec-from-file=<file>`:: Pathspec is passed in _<file>_ instead of commandline args. If _<file>_ is exactly `-` then standard input is used. Pathspec - elements are separated by LF or CR/LF. Pathspec elements can be + elements are separated by _LF_ or _CR/LF_. Pathspec elements can be quoted as explained for the configuration variable `core.quotePath` (see linkgit:git-config[1]). See also `--pathspec-file-nul` and global `--literal-pathspecs`. ---pathspec-file-nul:: +`--pathspec-file-nul`:: Only meaningful with `--pathspec-from-file`. Pathspec elements are - separated with NUL character and all other characters are taken + separated with _NUL_ character and all other characters are taken literally (including newlines and quotes). -\--:: +`--`:: This option can be used to separate command-line options from the list of files, (useful when filenames might be mistaken for command-line options). @@ -219,18 +219,18 @@ for "git add --no-all _<pathspec>_...", i.e. ignored removed files. EXAMPLES -------- -* Adds content from all `*.txt` files under `Documentation` directory +* Adds content from all ++*.txt++ files under `Documentation` directory and its subdirectories: + ------------ $ git add Documentation/\*.txt ------------ + -Note that the asterisk `*` is quoted from the shell in this +Note that the asterisk ++*++ is quoted from the shell in this example; this lets the command include the files from subdirectories of `Documentation/` directory. -* Considers adding content from all git-*.sh scripts: +* Considers adding content from all ++git-*.sh++ scripts: + ------------ $ git add git-*.sh @@ -265,7 +265,7 @@ The main command loop has 6 subcommands (plus help and quit). status:: - This shows the change between HEAD and index (i.e. what will be + This shows the change between `HEAD` and index (i.e. what will be committed if you say `git commit`), and between index and working tree files (i.e. what you could stage further before `git commit` using `git add`) for each path. A sample output @@ -277,12 +277,12 @@ status:: 2: +403/-35 +1/-1 add-interactive.c ------------ + -It shows that foo.png has differences from HEAD (but that is +It shows that `foo.png` has differences from `HEAD` (but that is binary so line count cannot be shown) and there is no difference between indexed copy and the working tree version (if the working tree version were also different, 'binary' would have been shown in place of 'nothing'). The -other file, add-interactive.c, has 403 lines added +other file, `add-interactive.c`, has 403 lines added and 35 lines deleted if you commit what is in the index, but working tree file has further modifications (one addition and one deletion). @@ -360,7 +360,7 @@ variable `interactive.singleKey` to `true`. diff:: This lets you review what will be committed (i.e. between - HEAD and index). + `HEAD` and index). EDITING PATCHES @@ -399,7 +399,7 @@ There are also more complex operations that can be performed. But beware that because the patch is applied only to the index and not the working tree, the working tree will appear to "undo" the change in the index. For example, introducing a new line into the index that is in neither -the HEAD nor the working tree will stage the new line for commit, but +the `HEAD` nor the working tree will stage the new line for commit, but the line will appear to be reverted in the working tree. Avoid using these constructs, or do so with extreme caution. @@ -439,6 +439,7 @@ CONFIGURATION include::includes/cmd-config-section-all.txt[] +:git-add: 1 include::config/add.txt[] SEE ALSO diff --git a/Documentation/git-clone.txt b/Documentation/git-clone.txt index 116ad64820..7acb4cb176 100644 --- a/Documentation/git-clone.txt +++ b/Documentation/git-clone.txt @@ -255,7 +255,7 @@ corresponding `--mirror` and `--no-tags` options instead. `--shallow-since=<date>`:: Create a shallow clone with a history after the specified time. -`--shallow-exclude=<revision>`:: +`--shallow-exclude=<ref>`:: Create a shallow clone with a history, excluding commits reachable from a specified remote branch or tag. This option can be specified multiple times. diff --git a/Documentation/git-fetch-pack.txt b/Documentation/git-fetch-pack.txt index b3467664d3..b5223576a7 100644 --- a/Documentation/git-fetch-pack.txt +++ b/Documentation/git-fetch-pack.txt @@ -91,7 +91,7 @@ be in a separate packet, and the list must end with a flush packet. Deepen or shorten the history of a shallow repository to include all reachable commits after <date>. ---shallow-exclude=<revision>:: +--shallow-exclude=<ref>:: Deepen or shorten the history of a shallow repository to exclude commits reachable from a specified remote branch or tag. This option can be specified multiple times. diff --git a/Documentation/git-index-pack.txt b/Documentation/git-index-pack.txt index 5a20deefd5..4be09e58e7 100644 --- a/Documentation/git-index-pack.txt +++ b/Documentation/git-index-pack.txt @@ -139,6 +139,11 @@ include::object-format-disclaimer.txt[] written. If a `<message>` is provided, then that content will be written to the .promisor file for future reference. See link:technical/partial-clone.html[partial clone] for more information. ++ +Also, if there are objects in the given pack that references non-promisor +objects (in the repo), repacks those non-promisor objects into a promisor +pack. This avoids a situation in which a repo has non-promisor objects that are +accessible through promisor objects. NOTES ----- diff --git a/Documentation/gitrepository-layout.txt b/Documentation/gitrepository-layout.txt index 949cd8a31e..fa8b51daf0 100644 --- a/Documentation/gitrepository-layout.txt +++ b/Documentation/gitrepository-layout.txt @@ -298,6 +298,7 @@ SEE ALSO -------- linkgit:git-init[1], linkgit:git-clone[1], +linkgit:git-config[1], linkgit:git-fetch[1], linkgit:git-pack-refs[1], linkgit:git-gc[1], diff --git a/Documentation/technical/hash-function-transition.txt b/Documentation/technical/hash-function-transition.txt index ed57481089..7102c7c8f5 100644 --- a/Documentation/technical/hash-function-transition.txt +++ b/Documentation/technical/hash-function-transition.txt @@ -148,8 +148,8 @@ Detailed Design Repository format extension ~~~~~~~~~~~~~~~~~~~~~~~~~~~ A SHA-256 repository uses repository format version `1` (see -Documentation/technical/repository-version.txt) with extensions -`objectFormat` and `compatObjectFormat`: +linkgit:gitrepository-layout[5]) with `extensions.objectFormat` and +`extensions.compatObjectFormat` (see linkgit:git-config[1]) set to: [core] repositoryFormatVersion = 1 diff --git a/Documentation/technical/partial-clone.txt b/Documentation/technical/partial-clone.txt index cd948b0072..bf5ec5c82d 100644 --- a/Documentation/technical/partial-clone.txt +++ b/Documentation/technical/partial-clone.txt @@ -102,7 +102,7 @@ or commits that reference missing trees. - On the client a repository extension is added to the local config to prevent older versions of git from failing mid-operation because of missing objects that they cannot handle. - See "extensions.partialClone" in Documentation/technical/repository-version.txt" + See `extensions.partialClone` in linkgit:git-config[1]. Handling Missing Objects diff --git a/Documentation/technical/repository-version.txt b/Documentation/technical/repository-version.txt index 47281420fc..b9bb81a81f 100644 --- a/Documentation/technical/repository-version.txt +++ b/Documentation/technical/repository-version.txt @@ -65,44 +65,6 @@ Note that if no extensions are specified in the config file, then provides no benefit, and makes the repository incompatible with older implementations of git). -This document will serve as the master list for extensions. Any -implementation wishing to define a new extension should make a note of -it here, in order to claim the name. - -The defined extensions are: - -==== `noop` - -This extension does not change git's behavior at all. It is useful only -for testing format-1 compatibility. - -==== `preciousObjects` - -When the config key `extensions.preciousObjects` is set to `true`, -objects in the repository MUST NOT be deleted (e.g., by `git-prune` or -`git repack -d`). - -==== `partialClone` - -When the config key `extensions.partialClone` is set, it indicates -that the repo was created with a partial clone (or later performed -a partial fetch) and that the remote may have omitted sending -certain unwanted objects. Such a remote is called a "promisor remote" -and it promises that all such omitted objects can be fetched from it -in the future. - -The value of this key is the name of the promisor remote. - -==== `worktreeConfig` - -If set, by default "git config" reads from both "config" and -"config.worktree" files from GIT_DIR in that order. In -multiple working directory mode, "config" file is shared while -"config.worktree" is per-working directory (i.e., it's in -GIT_COMMON_DIR/worktrees/<id>/config.worktree) - -==== `refStorage` - -Specifies the file format for the ref database. The valid values are -`files` (loose references with a packed-refs file) and `reftable` (see -Documentation/technical/reftable.txt). +The defined extensions are given in the `extensions.*` section of +linkgit:git-config[1]. Any implementation wishing to define a new +extension should make a note of it there, in order to claim the name. @@ -3905,9 +3905,7 @@ GIT-TEST-SUITES: FORCE fi $(UNIT_TEST_DIR)/clar-decls.h: $(patsubst %,$(UNIT_TEST_DIR)/%.c,$(CLAR_TEST_SUITES)) GIT-TEST-SUITES - $(QUIET_GEN)for suite in $(CLAR_TEST_SUITES); do \ - sed -ne "s/^\(void test_$${suite}__[a-zA-Z_0-9][a-zA-Z_0-9]*(void)$$\)/extern \1;/p" $(UNIT_TEST_DIR)/$$suite.c; \ - done >$@ + $(QUIET_GEN)$(SHELL_PATH) $(UNIT_TEST_DIR)/generate-clar-decls.sh "$@" $(filter %.c,$^) $(UNIT_TEST_DIR)/clar.suite: $(UNIT_TEST_DIR)/clar-decls.h $(QUIET_GEN)awk -f $(UNIT_TEST_DIR)/clar-generate.awk $< >$(UNIT_TEST_DIR)/clar.suite $(UNIT_TEST_DIR)/clar/clar.o: $(UNIT_TEST_DIR)/clar.suite diff --git a/builtin/clone.c b/builtin/clone.c index e851b1475d..d3022a185a 100644 --- a/builtin/clone.c +++ b/builtin/clone.c @@ -147,8 +147,8 @@ static struct option builtin_clone_options[] = { N_("create a shallow clone of that depth")), OPT_STRING(0, "shallow-since", &option_since, N_("time"), N_("create a shallow clone since a specific time")), - OPT_STRING_LIST(0, "shallow-exclude", &option_not, N_("revision"), - N_("deepen history of shallow clone, excluding rev")), + OPT_STRING_LIST(0, "shallow-exclude", &option_not, N_("ref"), + N_("deepen history of shallow clone, excluding ref")), OPT_BOOL(0, "single-branch", &option_single_branch, N_("clone only one branch, HEAD or --branch")), OPT_BOOL(0, "no-tags", &option_no_tags, diff --git a/builtin/difftool.c b/builtin/difftool.c index ca1b089065..40e971e225 100644 --- a/builtin/difftool.c +++ b/builtin/difftool.c @@ -376,7 +376,8 @@ static int run_dir_diff(const char *extcmd, int symlinks, const char *prefix, struct checkout lstate, rstate; int err = 0; struct child_process cmd = CHILD_PROCESS_INIT; - struct hashmap wt_modified, tmp_modified; + struct hashmap wt_modified = HASHMAP_INIT(path_entry_cmp, NULL); + struct hashmap tmp_modified = HASHMAP_INIT(path_entry_cmp, NULL); int indices_loaded = 0; workdir = repo_get_work_tree(the_repository); @@ -601,9 +602,6 @@ static int run_dir_diff(const char *extcmd, int symlinks, const char *prefix, * in the common case of --symlinks and the difftool updating * files through the symlink. */ - hashmap_init(&wt_modified, path_entry_cmp, NULL, wtindex.cache_nr); - hashmap_init(&tmp_modified, path_entry_cmp, NULL, wtindex.cache_nr); - for (i = 0; i < wtindex.cache_nr; i++) { struct hashmap_entry dummy; const char *name = wtindex.cache[i]->name; diff --git a/builtin/fetch.c b/builtin/fetch.c index d9027e4dc9..06b4611958 100644 --- a/builtin/fetch.c +++ b/builtin/fetch.c @@ -454,14 +454,10 @@ static void filter_prefetch_refspec(struct refspec *rs) ref_namespace[NAMESPACE_TAGS].ref))) { int j; - free(rs->items[i].src); - free(rs->items[i].dst); - free(rs->raw[i]); + refspec_item_clear(&rs->items[i]); - for (j = i + 1; j < rs->nr; j++) { + for (j = i + 1; j < rs->nr; j++) rs->items[j - 1] = rs->items[j]; - rs->raw[j - 1] = rs->raw[j]; - } rs->nr--; i--; continue; @@ -2216,8 +2212,8 @@ int cmd_fetch(int argc, N_("deepen history of shallow clone")), OPT_STRING(0, "shallow-since", &deepen_since, N_("time"), N_("deepen history of shallow repository based on time")), - OPT_STRING_LIST(0, "shallow-exclude", &deepen_not, N_("revision"), - N_("deepen history of shallow clone, excluding rev")), + OPT_STRING_LIST(0, "shallow-exclude", &deepen_not, N_("ref"), + N_("deepen history of shallow clone, excluding ref")), OPT_INTEGER(0, "deepen", &deepen_relative, N_("deepen history of shallow clone")), OPT_SET_INT_F(0, "unshallow", &unshallow, diff --git a/builtin/index-pack.c b/builtin/index-pack.c index 9d23b41b3a..08b340552f 100644 --- a/builtin/index-pack.c +++ b/builtin/index-pack.c @@ -9,6 +9,7 @@ #include "csum-file.h" #include "blob.h" #include "commit.h" +#include "tag.h" #include "tree.h" #include "progress.h" #include "fsck.h" @@ -20,9 +21,14 @@ #include "object-file.h" #include "object-store-ll.h" #include "oid-array.h" +#include "oidset.h" +#include "path.h" #include "replace-object.h" +#include "tree-walk.h" #include "promisor-remote.h" +#include "run-command.h" #include "setup.h" +#include "strvec.h" static const char index_pack_usage[] = "git index-pack [-v] [-o <index-file>] [--keep | --keep=<msg>] [--[no-]rev-index] [--verify] [--strict[=<msg-id>=<severity>...]] [--fsck-objects[=<msg-id>=<severity>...]] (<pack-file> | --stdin [--fix-thin] [<pack-file>])"; @@ -148,6 +154,13 @@ static uint32_t input_crc32; static int input_fd, output_fd; static const char *curr_pack; +/* + * local_links is guarded by read_mutex, and record_local_links is read-only in + * a thread. + */ +static struct oidset local_links = OIDSET_INIT; +static int record_local_links; + static struct thread_local *thread_data; static int nr_dispatched; static int threads_active; @@ -799,6 +812,44 @@ static int check_collison(struct object_entry *entry) return 0; } +static void record_if_local_object(const struct object_id *oid) +{ + struct object_info info = OBJECT_INFO_INIT; + if (oid_object_info_extended(the_repository, oid, &info, 0)) + /* Missing; assume it is a promisor object */ + return; + if (info.whence == OI_PACKED && info.u.packed.pack->pack_promisor) + return; + oidset_insert(&local_links, oid); +} + +static void do_record_local_links(struct object *obj) +{ + if (obj->type == OBJ_TREE) { + struct tree *tree = (struct tree *)obj; + struct tree_desc desc; + struct name_entry entry; + if (init_tree_desc_gently(&desc, &tree->object.oid, + tree->buffer, tree->size, 0)) + /* + * Error messages are given when packs are + * verified, so do not print any here. + */ + return; + while (tree_entry_gently(&desc, &entry)) + record_if_local_object(&entry.oid); + } else if (obj->type == OBJ_COMMIT) { + struct commit *commit = (struct commit *) obj; + struct commit_list *parents = commit->parents; + + for (; parents; parents = parents->next) + record_if_local_object(&parents->item->object.oid); + } else if (obj->type == OBJ_TAG) { + struct tag *tag = (struct tag *) obj; + record_if_local_object(get_tagged_oid(tag)); + } +} + static void sha1_object(const void *data, struct object_entry *obj_entry, unsigned long size, enum object_type type, const struct object_id *oid) @@ -845,7 +896,7 @@ static void sha1_object(const void *data, struct object_entry *obj_entry, free(has_data); } - if (strict || do_fsck_object) { + if (strict || do_fsck_object || record_local_links) { read_lock(); if (type == OBJ_BLOB) { struct blob *blob = lookup_blob(the_repository, oid); @@ -877,6 +928,8 @@ static void sha1_object(const void *data, struct object_entry *obj_entry, die(_("fsck error in packed object")); if (strict && fsck_walk(obj, NULL, &fsck_options)) die(_("Not all child objects of %s are reachable"), oid_to_hex(&obj->oid)); + if (record_local_links) + do_record_local_links(obj); if (obj->type == OBJ_TREE) { struct tree *item = (struct tree *) obj; @@ -1719,6 +1772,58 @@ static void show_pack_info(int stat_only) free(chain_histogram); } +static void repack_local_links(void) +{ + struct child_process cmd = CHILD_PROCESS_INIT; + FILE *out; + struct strbuf line = STRBUF_INIT; + struct oidset_iter iter; + struct object_id *oid; + char *base_name; + + if (!oidset_size(&local_links)) + return; + + base_name = mkpathdup("%s/pack/pack", repo_get_object_directory(the_repository)); + + strvec_push(&cmd.args, "pack-objects"); + strvec_push(&cmd.args, "--exclude-promisor-objects-best-effort"); + strvec_push(&cmd.args, base_name); + cmd.git_cmd = 1; + cmd.in = -1; + cmd.out = -1; + if (start_command(&cmd)) + die(_("could not start pack-objects to repack local links")); + + oidset_iter_init(&local_links, &iter); + while ((oid = oidset_iter_next(&iter))) { + if (write_in_full(cmd.in, oid_to_hex(oid), the_hash_algo->hexsz) < 0 || + write_in_full(cmd.in, "\n", 1) < 0) + die(_("failed to feed local object to pack-objects")); + } + close(cmd.in); + + out = xfdopen(cmd.out, "r"); + while (strbuf_getline_lf(&line, out) != EOF) { + unsigned char binary[GIT_MAX_RAWSZ]; + if (line.len != the_hash_algo->hexsz || + !hex_to_bytes(binary, line.buf, line.len)) + die(_("index-pack: Expecting full hex object ID lines only from pack-objects.")); + + /* + * pack-objects creates the .pack and .idx files, but not the + * .promisor file. Create the .promisor file, which is empty. + */ + write_special_file("promisor", "", NULL, binary, NULL); + } + + fclose(out); + if (finish_command(&cmd)) + die(_("could not finish pack-objects to repack local links")); + strbuf_release(&line); + free(base_name); +} + int cmd_index_pack(int argc, const char **argv, const char *prefix, @@ -1794,7 +1899,7 @@ int cmd_index_pack(int argc, } else if (skip_to_optional_arg(arg, "--keep", &keep_msg)) { ; /* nothing to do */ } else if (skip_to_optional_arg(arg, "--promisor", &promisor_msg)) { - ; /* already parsed */ + record_local_links = 1; } else if (starts_with(arg, "--threads=")) { char *end; nr_threads = strtoul(arg+10, &end, 0); @@ -1970,6 +2075,8 @@ int cmd_index_pack(int argc, free((void *) curr_index); free(curr_rev_index); + repack_local_links(); + /* * Let the caller know this pack is not self contained */ diff --git a/builtin/interpret-trailers.c b/builtin/interpret-trailers.c index c5e56e2cd3..44d8ccddc9 100644 --- a/builtin/interpret-trailers.c +++ b/builtin/interpret-trailers.c @@ -141,8 +141,8 @@ static void interpret_trailers(const struct process_trailer_options *opts, { LIST_HEAD(head); struct strbuf sb = STRBUF_INIT; - struct strbuf trailer_block = STRBUF_INIT; - struct trailer_info *info; + struct strbuf trailer_block_sb = STRBUF_INIT; + struct trailer_block *trailer_block; FILE *outfile = stdout; trailer_config_init(); @@ -152,13 +152,13 @@ static void interpret_trailers(const struct process_trailer_options *opts, if (opts->in_place) outfile = create_in_place_tempfile(file); - info = parse_trailers(opts, sb.buf, &head); + trailer_block = parse_trailers(opts, sb.buf, &head); - /* Print the lines before the trailers */ + /* Print the lines before the trailer block */ if (!opts->only_trailers) - fwrite(sb.buf, 1, trailer_block_start(info), outfile); + fwrite(sb.buf, 1, trailer_block_start(trailer_block), outfile); - if (!opts->only_trailers && !blank_line_before_trailer_block(info)) + if (!opts->only_trailers && !blank_line_before_trailer_block(trailer_block)) fprintf(outfile, "\n"); @@ -172,15 +172,16 @@ static void interpret_trailers(const struct process_trailer_options *opts, } /* Print trailer block. */ - format_trailers(opts, &head, &trailer_block); + format_trailers(opts, &head, &trailer_block_sb); free_trailers(&head); - fwrite(trailer_block.buf, 1, trailer_block.len, outfile); - strbuf_release(&trailer_block); + fwrite(trailer_block_sb.buf, 1, trailer_block_sb.len, outfile); + strbuf_release(&trailer_block_sb); - /* Print the lines after the trailers as is */ + /* Print the lines after the trailer block as is. */ if (!opts->only_trailers) - fwrite(sb.buf + trailer_block_end(info), 1, sb.len - trailer_block_end(info), outfile); - trailer_info_release(info); + fwrite(sb.buf + trailer_block_end(trailer_block), 1, + sb.len - trailer_block_end(trailer_block), outfile); + trailer_block_release(trailer_block); if (opts->in_place) if (rename_tempfile(&trailers_tempfile, file)) diff --git a/builtin/pack-objects.c b/builtin/pack-objects.c index 0800714267..676a586709 100644 --- a/builtin/pack-objects.c +++ b/builtin/pack-objects.c @@ -239,6 +239,7 @@ static enum { static uint16_t write_bitmap_options = BITMAP_OPT_HASH_CACHE; static int exclude_promisor_objects; +static int exclude_promisor_objects_best_effort; static int use_delta_islands; @@ -1100,78 +1101,64 @@ static void write_reused_pack_one(struct packed_git *reuse_packfile, static size_t write_reused_pack_verbatim(struct bitmapped_pack *reuse_packfile, struct hashfile *out, - off_t pack_start, struct pack_window **w_curs) { - size_t pos = reuse_packfile->bitmap_pos; + size_t pos = 0; size_t end; - if (pos % BITS_IN_EWORD) { - size_t word_pos = (pos / BITS_IN_EWORD); - size_t offset = pos % BITS_IN_EWORD; - size_t last; - eword_t word = reuse_packfile_bitmap->words[word_pos]; - - if (offset + reuse_packfile->bitmap_nr < BITS_IN_EWORD) - last = offset + reuse_packfile->bitmap_nr; - else - last = BITS_IN_EWORD; - - for (; offset < last; offset++) { - if (word >> offset == 0) - return word_pos; - if (!bitmap_get(reuse_packfile_bitmap, - word_pos * BITS_IN_EWORD + offset)) - return word_pos; - } - - pos += BITS_IN_EWORD - (pos % BITS_IN_EWORD); + if (reuse_packfile->bitmap_pos) { + /* + * We can't reuse whole chunks verbatim out of + * non-preferred packs since we can't guarantee that + * all duplicate objects were resolved in favor of + * that pack. + * + * Even if we have a whole eword_t worth of bits that + * could be reused, there may be objects between the + * objects corresponding to the first and last bit of + * that word which were selected from a different + * pack, causing us to send duplicate or unwanted + * objects. + * + * Handle non-preferred packs from within + * write_reused_pack(), which inspects and reuses + * individual bits. + */ + return reuse_packfile->bitmap_pos / BITS_IN_EWORD; } /* - * Now we're going to copy as many whole eword_t's as possible. - * "end" is the index of the last whole eword_t we copy, but - * there may be additional bits to process. Those are handled - * individually by write_reused_pack(). + * Only read through the last word whose bits all correspond + * to objects in the given packfile, since we must stop at a + * word boundary. * - * Begin by advancing to the first word boundary in range of the - * bit positions occupied by objects in "reuse_packfile". Then - * pick the last word boundary in the same range. If we have at - * least one word's worth of bits to process, continue on. + * If there is no whole word to read (i.e. the packfile + * contains fewer than BITS_IN_EWORD objects), then we'll + * inspect bits one-by-one in write_reused_pack(). */ - end = reuse_packfile->bitmap_pos + reuse_packfile->bitmap_nr; - if (end % BITS_IN_EWORD) - end -= end % BITS_IN_EWORD; - if (pos >= end) - return reuse_packfile->bitmap_pos / BITS_IN_EWORD; + end = reuse_packfile->bitmap_nr / BITS_IN_EWORD; + if (reuse_packfile_bitmap->word_alloc < end) + BUG("fewer words than expected in reuse_packfile_bitmap"); - while (pos < end && - reuse_packfile_bitmap->words[pos / BITS_IN_EWORD] == (eword_t)~0) - pos += BITS_IN_EWORD; + while (pos < end && reuse_packfile_bitmap->words[pos] == (eword_t)~0) + pos++; - if (pos > end) - pos = end; + if (pos) { + off_t to_write; - if (reuse_packfile->bitmap_pos < pos) { - off_t pack_start_off = pack_pos_to_offset(reuse_packfile->p, 0); - off_t pack_end_off = pack_pos_to_offset(reuse_packfile->p, - pos - reuse_packfile->bitmap_pos); - - written += pos - reuse_packfile->bitmap_pos; + written = (pos * BITS_IN_EWORD); + to_write = pack_pos_to_offset(reuse_packfile->p, written) + - sizeof(struct pack_header); /* We're recording one chunk, not one object. */ - record_reused_object(pack_start_off, - pack_start_off - (hashfile_total(out) - pack_start)); + record_reused_object(sizeof(struct pack_header), 0); hashflush(out); copy_pack_data(out, reuse_packfile->p, w_curs, - pack_start_off, pack_end_off - pack_start_off); + sizeof(struct pack_header), to_write); display_progress(progress_state, written); } - if (pos % BITS_IN_EWORD) - BUG("attempted to jump past a word boundary to %"PRIuMAX, - (uintmax_t)pos); - return pos / BITS_IN_EWORD; + return pos; } static void write_reused_pack(struct bitmapped_pack *reuse_packfile, @@ -1183,8 +1170,7 @@ static void write_reused_pack(struct bitmapped_pack *reuse_packfile, struct pack_window *w_curs = NULL; if (allow_ofs_delta) - i = write_reused_pack_verbatim(reuse_packfile, f, pack_start, - &w_curs); + i = write_reused_pack_verbatim(reuse_packfile, f, &w_curs); for (; i < reuse_packfile_bitmap->word_alloc; ++i) { eword_t word = reuse_packfile_bitmap->words[i]; @@ -4312,6 +4298,18 @@ static int option_parse_cruft_expiration(const struct option *opt UNUSED, return 0; } +static int is_not_in_promisor_pack_obj(struct object *obj, void *data UNUSED) +{ + struct object_info info = OBJECT_INFO_INIT; + if (oid_object_info_extended(the_repository, &obj->oid, &info, 0)) + BUG("should_include_obj should only be called on existing objects"); + return info.whence != OI_PACKED || !info.u.packed.pack->pack_promisor; +} + +static int is_not_in_promisor_pack(struct commit *commit, void *data) { + return is_not_in_promisor_pack_obj((struct object *) commit, data); +} + int cmd_pack_objects(int argc, const char **argv, const char *prefix, @@ -4424,6 +4422,9 @@ int cmd_pack_objects(int argc, option_parse_missing_action), OPT_BOOL(0, "exclude-promisor-objects", &exclude_promisor_objects, N_("do not pack objects in promisor packfiles")), + OPT_BOOL(0, "exclude-promisor-objects-best-effort", + &exclude_promisor_objects_best_effort, + N_("implies --missing=allow-any")), OPT_BOOL(0, "delta-islands", &use_delta_islands, N_("respect islands during delta compression")), OPT_STRING_LIST(0, "uri-protocol", &uri_protocols, @@ -4504,10 +4505,18 @@ int cmd_pack_objects(int argc, strvec_push(&rp, "--unpacked"); } + if (exclude_promisor_objects && exclude_promisor_objects_best_effort) + die(_("options '%s' and '%s' cannot be used together"), + "--exclude-promisor-objects", "--exclude-promisor-objects-best-effort"); if (exclude_promisor_objects) { use_internal_rev_list = 1; fetch_if_missing = 0; strvec_push(&rp, "--exclude-promisor-objects"); + } else if (exclude_promisor_objects_best_effort) { + use_internal_rev_list = 1; + fetch_if_missing = 0; + option_parse_missing_action(NULL, "allow-any", 0); + /* revs configured below */ } if (unpack_unreachable || keep_unreachable || pack_loose_unreachable) use_internal_rev_list = 1; @@ -4627,6 +4636,10 @@ int cmd_pack_objects(int argc, repo_init_revisions(the_repository, &revs, NULL); list_objects_filter_copy(&revs.filter, &filter_options); + if (exclude_promisor_objects_best_effort) { + revs.include_check = is_not_in_promisor_pack; + revs.include_check_obj = is_not_in_promisor_pack_obj; + } get_object_list(&revs, rp.nr, rp.v); release_revisions(&revs); } diff --git a/builtin/pull.c b/builtin/pull.c index 388ef3d130..edc56907aa 100644 --- a/builtin/pull.c +++ b/builtin/pull.c @@ -218,8 +218,8 @@ static struct option pull_options[] = { OPT_PASSTHRU_ARGV(0, "shallow-since", &opt_fetch, N_("time"), N_("deepen history of shallow repository based on time"), 0), - OPT_PASSTHRU_ARGV(0, "shallow-exclude", &opt_fetch, N_("revision"), - N_("deepen history of shallow clone, excluding rev"), + OPT_PASSTHRU_ARGV(0, "shallow-exclude", &opt_fetch, N_("ref"), + N_("deepen history of shallow clone, excluding ref"), 0), OPT_PASSTHRU_ARGV(0, "deepen", &opt_fetch, N_("n"), N_("deepen history of shallow clone"), diff --git a/builtin/remote.c b/builtin/remote.c index 76670ddd8b..9093600965 100644 --- a/builtin/remote.c +++ b/builtin/remote.c @@ -377,7 +377,7 @@ static int get_ref_states(const struct ref *remote_refs, struct ref_states *stat for (i = 0; i < states->remote->fetch.nr; i++) if (get_fetch_map(remote_refs, &states->remote->fetch.items[i], &tail, 1)) die(_("Could not get fetch map for refspec %s"), - states->remote->fetch.raw[i]); + states->remote->fetch.items[i].raw); for (ref = fetch_map; ref; ref = ref->next) { if (omit_name_by_refspec(ref->name, &states->remote->fetch)) @@ -633,12 +633,12 @@ static int migrate_file(struct remote *remote) git_config_set_multivar(buf.buf, remote->url.v[i], "^$", 0); strbuf_reset(&buf); strbuf_addf(&buf, "remote.%s.push", remote->name); - for (i = 0; i < remote->push.raw_nr; i++) - git_config_set_multivar(buf.buf, remote->push.raw[i], "^$", 0); + for (i = 0; i < remote->push.nr; i++) + git_config_set_multivar(buf.buf, remote->push.items[i].raw, "^$", 0); strbuf_reset(&buf); strbuf_addf(&buf, "remote.%s.fetch", remote->name); - for (i = 0; i < remote->fetch.raw_nr; i++) - git_config_set_multivar(buf.buf, remote->fetch.raw[i], "^$", 0); + for (i = 0; i < remote->fetch.nr; i++) + git_config_set_multivar(buf.buf, remote->fetch.items[i].raw, "^$", 0); if (remote->origin == REMOTE_REMOTES) unlink_or_warn(git_path("remotes/%s", remote->name)); else if (remote->origin == REMOTE_BRANCHES) @@ -759,16 +759,16 @@ static int mv(int argc, const char **argv, const char *prefix) goto out; } - if (oldremote->fetch.raw_nr) { + if (oldremote->fetch.nr) { strbuf_reset(&buf); strbuf_addf(&buf, "remote.%s.fetch", rename.new_name); git_config_set_multivar(buf.buf, NULL, NULL, CONFIG_FLAGS_MULTI_REPLACE); strbuf_addf(&old_remote_context, ":refs/remotes/%s/", rename.old_name); - for (i = 0; i < oldremote->fetch.raw_nr; i++) { + for (i = 0; i < oldremote->fetch.nr; i++) { char *ptr; strbuf_reset(&buf2); - strbuf_addstr(&buf2, oldremote->fetch.raw[i]); + strbuf_addstr(&buf2, oldremote->fetch.items[i].raw); ptr = strstr(buf2.buf, old_remote_context.buf); if (ptr) { refspec_updated = 1; diff --git a/builtin/rev-list.c b/builtin/rev-list.c index f62bcbf2b1..3078787115 100644 --- a/builtin/rev-list.c +++ b/builtin/rev-list.c @@ -485,6 +485,13 @@ static int try_bitmap_traversal(struct rev_info *revs, if (revs->max_count >= 0) return -1; + /* + * We can't know which commits were left/right in a single traversal, + * and we don't yet know how to traverse them separately. + */ + if (revs->left_right) + return -1; + bitmap_git = prepare_bitmap_walk(revs, filter_provided_objects); if (!bitmap_git) return -1; diff --git a/compat/mingw.c b/compat/mingw.c index 0ff550cef3..63f36c893b 100644 --- a/compat/mingw.c +++ b/compat/mingw.c @@ -502,7 +502,7 @@ static int mingw_open_append(wchar_t const *wfilename, int oflags, ...) * to append to the file. */ handle = CreateFileW(wfilename, FILE_APPEND_DATA, - FILE_SHARE_WRITE | FILE_SHARE_READ, + FILE_SHARE_WRITE | FILE_SHARE_READ | FILE_SHARE_DELETE, NULL, create, FILE_ATTRIBUTE_NORMAL, NULL); if (handle == INVALID_HANDLE_VALUE) { DWORD err = GetLastError(); @@ -533,6 +533,70 @@ static int mingw_open_append(wchar_t const *wfilename, int oflags, ...) } /* + * Ideally, we'd use `_wopen()` to implement this functionality so that we + * don't have to reimplement it, but unfortunately we do not have tight control + * over the share mode there. And while `_wsopen()` and friends exist that give + * us _some_ control over the share mode, this family of functions doesn't give + * us the ability to enable FILE_SHARE_DELETE, either. But this is a strict + * requirement for us though so that we can unlink or rename over files that + * are held open by another process. + * + * We are thus forced to implement our own emulation of `open()`. To make our + * life simpler we only implement basic support for this, namely opening + * existing files for reading and/or writing. This means that newly created + * files won't have their sharing mode set up correctly, but for now I couldn't + * find any case where this matters. We may have to revisit that in the future + * though based on our needs. + */ +static int mingw_open_existing(const wchar_t *filename, int oflags, ...) +{ + SECURITY_ATTRIBUTES security_attributes = { + .nLength = sizeof(security_attributes), + .bInheritHandle = !(oflags & O_NOINHERIT), + }; + HANDLE handle; + DWORD access; + int fd; + + /* We only support basic flags. */ + if (oflags & ~(O_ACCMODE | O_NOINHERIT)) { + errno = ENOSYS; + return -1; + } + + switch (oflags & O_ACCMODE) { + case O_RDWR: + access = GENERIC_READ | GENERIC_WRITE; + break; + case O_WRONLY: + access = GENERIC_WRITE; + break; + default: + access = GENERIC_READ; + break; + } + + handle = CreateFileW(filename, access, + FILE_SHARE_WRITE | FILE_SHARE_READ | FILE_SHARE_DELETE, + &security_attributes, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); + if (handle == INVALID_HANDLE_VALUE) { + DWORD err = GetLastError(); + + /* See `mingw_open_append()` for why we have this conversion. */ + if (err == ERROR_INVALID_PARAMETER) + err = ERROR_PATH_NOT_FOUND; + + errno = err_win_to_posix(err); + return -1; + } + + fd = _open_osfhandle((intptr_t)handle, oflags | O_BINARY); + if (fd < 0) + CloseHandle(handle); + return fd; +} + +/* * Does the pathname map to the local named pipe filesystem? * That is, does it have a "//./pipe/" prefix? */ @@ -567,6 +631,8 @@ int mingw_open (const char *filename, int oflags, ...) if ((oflags & O_APPEND) && !is_local_named_pipe_path(filename)) open_fn = mingw_open_append; + else if (!(oflags & ~(O_ACCMODE | O_NOINHERIT))) + open_fn = mingw_open_existing; else open_fn = _wopen; @@ -1006,7 +1072,7 @@ int mingw_utime (const char *file_name, const struct utimbuf *times) osfilehandle = CreateFileW(wfilename, FILE_WRITE_ATTRIBUTES, - 0 /*FileShare.None*/, + FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL, OPEN_EXISTING, (attrs != INVALID_FILE_ATTRIBUTES && @@ -2155,10 +2221,16 @@ int mingw_accept(int sockfd1, struct sockaddr *sa, socklen_t *sz) #undef rename int mingw_rename(const char *pold, const char *pnew) { + static int supports_file_rename_info_ex = 1; DWORD attrs, gle; int tries = 0; wchar_t wpold[MAX_PATH], wpnew[MAX_PATH]; - if (xutftowcs_path(wpold, pold) < 0 || xutftowcs_path(wpnew, pnew) < 0) + int wpnew_len; + + if (xutftowcs_path(wpold, pold) < 0) + return -1; + wpnew_len = xutftowcs_path(wpnew, pnew); + if (wpnew_len < 0) return -1; /* @@ -2169,11 +2241,84 @@ int mingw_rename(const char *pold, const char *pnew) return 0; if (errno != EEXIST) return -1; + repeat: - if (MoveFileExW(wpold, wpnew, MOVEFILE_REPLACE_EXISTING)) - return 0; + if (supports_file_rename_info_ex) { + /* + * Our minimum required Windows version is still set to Windows + * Vista. We thus have to declare required infrastructure for + * FileRenameInfoEx ourselves until we bump _WIN32_WINNT to + * 0x0A00. Furthermore, we have to handle cases where the + * FileRenameInfoEx call isn't supported yet. + */ +#define FILE_RENAME_FLAG_REPLACE_IF_EXISTS 0x00000001 +#define FILE_RENAME_FLAG_POSIX_SEMANTICS 0x00000002 + FILE_INFO_BY_HANDLE_CLASS FileRenameInfoEx = 22; + struct { + /* + * This is usually an unnamed union, but that is not + * part of ISO C99. We thus inline the field, as we + * really only care for the Flags field anyway. + */ + DWORD Flags; + HANDLE RootDirectory; + DWORD FileNameLength; + /* + * The actual structure is defined with a single-character + * flex array so that the structure has to be allocated on + * the heap. As we declare this structure ourselves though + * we can avoid the allocation and define FileName to have + * MAX_PATH bytes. + */ + WCHAR FileName[MAX_PATH]; + } rename_info = { 0 }; + HANDLE old_handle = INVALID_HANDLE_VALUE; + BOOL success; + + old_handle = CreateFileW(wpold, DELETE, + FILE_SHARE_WRITE | FILE_SHARE_READ | FILE_SHARE_DELETE, + NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); + if (old_handle == INVALID_HANDLE_VALUE) { + errno = err_win_to_posix(GetLastError()); + return -1; + } + + rename_info.Flags = FILE_RENAME_FLAG_REPLACE_IF_EXISTS | + FILE_RENAME_FLAG_POSIX_SEMANTICS; + rename_info.FileNameLength = wpnew_len * sizeof(WCHAR); + memcpy(rename_info.FileName, wpnew, wpnew_len * sizeof(WCHAR)); + + success = SetFileInformationByHandle(old_handle, FileRenameInfoEx, + &rename_info, sizeof(rename_info)); + gle = GetLastError(); + CloseHandle(old_handle); + if (success) + return 0; + + /* + * When we see ERROR_INVALID_PARAMETER we can assume that the + * current system doesn't support FileRenameInfoEx. Keep us + * from using it in future calls and retry. + */ + if (gle == ERROR_INVALID_PARAMETER) { + supports_file_rename_info_ex = 0; + goto repeat; + } + + /* + * In theory, we shouldn't get ERROR_ACCESS_DENIED because we + * always open files with FILE_SHARE_DELETE But in practice we + * cannot assume that Git is the only one accessing files, and + * other applications may not set FILE_SHARE_DELETE. So we have + * to retry. + */ + } else { + if (MoveFileExW(wpold, wpnew, MOVEFILE_REPLACE_EXISTING)) + return 0; + gle = GetLastError(); + } + /* TODO: translate more errors */ - gle = GetLastError(); if (gle == ERROR_ACCESS_DENIED && (attrs = GetFileAttributesW(wpnew)) != INVALID_FILE_ATTRIBUTES) { if (attrs & FILE_ATTRIBUTE_DIRECTORY) { diff --git a/contrib/buildsystems/CMakeLists.txt b/contrib/buildsystems/CMakeLists.txt index 1384c0eb6d..8974bb9fa2 100644 --- a/contrib/buildsystems/CMakeLists.txt +++ b/contrib/buildsystems/CMakeLists.txt @@ -1002,47 +1002,21 @@ foreach(unit_test ${unit_test_PROGRAMS}) endforeach() parse_makefile_for_scripts(clar_test_SUITES "CLAR_TEST_SUITES" "") - -set(clar_decls "") -set(clar_cbs "") -set(clar_cbs_count 0) -set(clar_suites "static struct clar_suite _clar_suites[] = {\n") -list(LENGTH clar_test_SUITES clar_suites_count) -foreach(suite ${clar_test_SUITES}) - file(STRINGS "${CMAKE_SOURCE_DIR}/t/unit-tests/${suite}.c" decls - REGEX "^void test_${suite}__[a-zA-Z_0-9][a-zA-Z_0-9]*\\(void\\)$") - - list(LENGTH decls decls_count) - string(REGEX REPLACE "void (test_${suite}__([a-zA-Z_0-9]*))\\(void\\)" " { \"\\2\", &\\1 },\n" cbs ${decls}) - string(JOIN "" cbs ${cbs}) - list(TRANSFORM decls PREPEND "extern ") - string(JOIN ";\n" decls ${decls}) - - string(APPEND clar_decls "${decls};\n") - string(APPEND clar_cbs - "static const struct clar_func _clar_cb_${suite}[] = {\n" - ${cbs} - "};\n") - string(APPEND clar_suites - " {\n" - " \"${suite}\",\n" - " { NULL, NULL },\n" - " { NULL, NULL },\n" - " _clar_cb_${suite}, ${decls_count}, 1\n" - " },\n") - math(EXPR clar_cbs_count "${clar_cbs_count}+${decls_count}") -endforeach() -string(APPEND clar_suites - "};\n" - "static const size_t _clar_suite_count = ${clar_suites_count};\n" - "static const size_t _clar_callback_count = ${clar_cbs_count};\n") -file(WRITE "${CMAKE_BINARY_DIR}/t/unit-tests/clar-decls.h" "${clar_decls}") -file(WRITE "${CMAKE_BINARY_DIR}/t/unit-tests/clar.suite" "${clar_decls}" "${clar_cbs}" "${clar_suites}") - list(TRANSFORM clar_test_SUITES PREPEND "${CMAKE_SOURCE_DIR}/t/unit-tests/") list(TRANSFORM clar_test_SUITES APPEND ".c") -add_library(unit-tests-lib ${clar_test_SUITES} "${CMAKE_SOURCE_DIR}/t/unit-tests/clar/clar.c") -target_include_directories(unit-tests-lib PRIVATE "${CMAKE_SOURCE_DIR}/t/unit-tests") +add_custom_command(OUTPUT "${CMAKE_BINARY_DIR}/t/unit-tests/clar-decls.h" + COMMAND ${CMAKE_SOURCE_DIR}/t/unit-tests/generate-clar-decls.sh "${CMAKE_BINARY_DIR}/t/unit-tests/clar-decls.h" ${clar_test_SUITES} + DEPENDS ${CMAKE_SOURCE_DIR}/t/unit-tests/generate-clar-decls.sh ${clar_test_SUITES}) +add_custom_command(OUTPUT "${CMAKE_BINARY_DIR}/t/unit-tests/clar.suite" + COMMAND awk -f "${CMAKE_SOURCE_DIR}/t/unit-tests/clar-generate.awk" "${CMAKE_BINARY_DIR}/t/unit-tests/clar-decls.h" > "${CMAKE_BINARY_DIR}/t/unit-tests/clar.suite" + DEPENDS "${CMAKE_SOURCE_DIR}/t/unit-tests/clar-generate.awk" "${CMAKE_BINARY_DIR}/t/unit-tests/clar-decls.h") + +add_library(unit-tests-lib ${clar_test_SUITES} + "${CMAKE_SOURCE_DIR}/t/unit-tests/clar/clar.c" + "${CMAKE_BINARY_DIR}/t/unit-tests/clar-decls.h" + "${CMAKE_BINARY_DIR}/t/unit-tests/clar.suite" +) +target_include_directories(unit-tests-lib PUBLIC "${CMAKE_BINARY_DIR}/t/unit-tests") add_executable(unit-tests "${CMAKE_SOURCE_DIR}/t/unit-tests/unit-test.c") target_link_libraries(unit-tests unit-tests-lib common-main) set_target_properties(unit-tests diff --git a/fetch-pack.c b/fetch-pack.c index f752da93a8..fe1fb3c1b7 100644 --- a/fetch-pack.c +++ b/fetch-pack.c @@ -122,29 +122,41 @@ static void for_each_cached_alternate(struct fetch_negotiator *negotiator, cb(negotiator, cache.items[i]); } -static struct commit *deref_without_lazy_fetch_extended(const struct object_id *oid, - int mark_tags_complete, - enum object_type *type, - unsigned int oi_flags) +static void die_in_commit_graph_only(const struct object_id *oid) { - struct object_info info = { .typep = type }; + die(_("You are attempting to fetch %s, which is in the commit graph file but not in the object database.\n" + "This is probably due to repo corruption.\n" + "If you are attempting to repair this repo corruption by refetching the missing object, use 'git fetch --refetch' with the missing object."), + oid_to_hex(oid)); +} + +static struct commit *deref_without_lazy_fetch(const struct object_id *oid, + int mark_tags_complete_and_check_obj_db) +{ + enum object_type type; + struct object_info info = { .typep = &type }; struct commit *commit; commit = lookup_commit_in_graph(the_repository, oid); - if (commit) + if (commit) { + if (mark_tags_complete_and_check_obj_db) { + if (!has_object(the_repository, oid, 0)) + die_in_commit_graph_only(oid); + } return commit; + } while (1) { if (oid_object_info_extended(the_repository, oid, &info, - oi_flags)) + OBJECT_INFO_SKIP_FETCH_OBJECT | OBJECT_INFO_QUICK)) return NULL; - if (*type == OBJ_TAG) { + if (type == OBJ_TAG) { struct tag *tag = (struct tag *) parse_object(the_repository, oid); if (!tag->tagged) return NULL; - if (mark_tags_complete) + if (mark_tags_complete_and_check_obj_db) tag->object.flags |= COMPLETE; oid = &tag->tagged->oid; } else { @@ -152,7 +164,7 @@ static struct commit *deref_without_lazy_fetch_extended(const struct object_id * } } - if (*type == OBJ_COMMIT) { + if (type == OBJ_COMMIT) { struct commit *commit = lookup_commit(the_repository, oid); if (!commit || repo_parse_commit(the_repository, commit)) return NULL; @@ -162,16 +174,6 @@ static struct commit *deref_without_lazy_fetch_extended(const struct object_id * return NULL; } - -static struct commit *deref_without_lazy_fetch(const struct object_id *oid, - int mark_tags_complete) -{ - enum object_type type; - unsigned flags = OBJECT_INFO_SKIP_FETCH_OBJECT | OBJECT_INFO_QUICK; - return deref_without_lazy_fetch_extended(oid, mark_tags_complete, - &type, flags); -} - static int rev_list_insert_ref(struct fetch_negotiator *negotiator, const struct object_id *oid) { diff --git a/git-gui/lib/commit.tcl b/git-gui/lib/commit.tcl index 11379f8ad3..208dc2817c 100644 --- a/git-gui/lib/commit.tcl +++ b/git-gui/lib/commit.tcl @@ -207,8 +207,17 @@ You must stage at least 1 file before you can commit. # -- A message is required. # - set msg [string trim [$ui_comm get 1.0 end]] + set msg [$ui_comm get 1.0 end] + # Strip trailing whitespace regsub -all -line {[ \t\r]+$} $msg {} msg + # Strip comment lines + regsub -all {(^|\n)#[^\n]*} $msg {\1} msg + # Strip leading empty lines + regsub {^\n*} $msg {} msg + # Compress consecutive empty lines + regsub -all {\n{3,}} $msg "\n\n" msg + # Strip trailing empty line + regsub {\n\n$} $msg "\n" msg if {$msg eq {}} { error_popup [mc "Please supply a commit message. diff --git a/git-gui/lib/mergetool.tcl b/git-gui/lib/mergetool.tcl index e688b016ef..8b8c16b1d6 100644 --- a/git-gui/lib/mergetool.tcl +++ b/git-gui/lib/mergetool.tcl @@ -272,8 +272,25 @@ proc merge_resolve_tool2 {} { } } default { - error_popup [mc "Unsupported merge tool '%s'" $tool] - return + set tool_cmd [get_config mergetool.$tool.cmd] + if {$tool_cmd ne {}} { + if {([string first {[} $tool_cmd] != -1) || ([string first {]} $tool_cmd] != -1)} { + error_popup [mc "Unable to process square brackets in \"mergetool.%s.cmd\" configuration option. + +Please remove the square brackets." $tool] + return + } else { + set cmdline {} + foreach command_part $tool_cmd { + lappend cmdline [subst -nobackslashes -nocommands $command_part] + } + } + } else { + error_popup [mc "Unsupported merge tool '%s'. + +To use this tool, configure \"mergetool.%s.cmd\" as shown in the git-config manual page." $tool $tool] + return + } } } @@ -153,6 +153,7 @@ static int parse_refspec(struct refspec_item *item, const char *refspec, int fet int refspec_item_init(struct refspec_item *item, const char *refspec, int fetch) { memset(item, 0, sizeof(*item)); + item->raw = xstrdup(refspec); return parse_refspec(item, refspec, fetch); } @@ -167,6 +168,7 @@ void refspec_item_clear(struct refspec_item *item) { FREE_AND_NULL(item->src); FREE_AND_NULL(item->dst); + FREE_AND_NULL(item->raw); item->force = 0; item->pattern = 0; item->matching = 0; @@ -179,31 +181,29 @@ void refspec_init(struct refspec *rs, int fetch) rs->fetch = fetch; } -static void refspec_append_nodup(struct refspec *rs, char *refspec) +void refspec_append(struct refspec *rs, const char *refspec) { struct refspec_item item; refspec_item_init_or_die(&item, refspec, rs->fetch); ALLOC_GROW(rs->items, rs->nr + 1, rs->alloc); - rs->items[rs->nr++] = item; + rs->items[rs->nr] = item; - ALLOC_GROW(rs->raw, rs->raw_nr + 1, rs->raw_alloc); - rs->raw[rs->raw_nr++] = refspec; -} - -void refspec_append(struct refspec *rs, const char *refspec) -{ - refspec_append_nodup(rs, xstrdup(refspec)); + rs->nr++; } void refspec_appendf(struct refspec *rs, const char *fmt, ...) { va_list ap; + char *buf; va_start(ap, fmt); - refspec_append_nodup(rs, xstrvfmt(fmt, ap)); + buf = xstrvfmt(fmt, ap); va_end(ap); + + refspec_append(rs, buf); + free(buf); } void refspec_appendn(struct refspec *rs, const char **refspecs, int nr) @@ -224,12 +224,6 @@ void refspec_clear(struct refspec *rs) rs->alloc = 0; rs->nr = 0; - for (i = 0; i < rs->raw_nr; i++) - free(rs->raw[i]); - FREE_AND_NULL(rs->raw); - rs->raw_alloc = 0; - rs->raw_nr = 0; - rs->fetch = 0; } @@ -26,6 +26,8 @@ struct refspec_item { char *src; char *dst; + + char *raw; }; #define REFSPEC_FETCH 1 @@ -43,10 +45,6 @@ struct refspec { int alloc; int nr; - char **raw; - int raw_alloc; - int raw_nr; - int fetch; }; diff --git a/submodule.c b/submodule.c index 74d5766f07..7ec564854d 100644 --- a/submodule.c +++ b/submodule.c @@ -1174,8 +1174,8 @@ static int push_submodule(const char *path, if (remote->origin != REMOTE_UNCONFIGURED) { int i; strvec_push(&cp.args, remote->name); - for (i = 0; i < rs->raw_nr; i++) - strvec_push(&cp.args, rs->raw[i]); + for (i = 0; i < rs->nr; i++) + strvec_push(&cp.args, rs->items[i].raw); } prepare_submodule_repo_env(&cp.env); @@ -1209,8 +1209,8 @@ static void submodule_push_check(const char *path, const char *head, strvec_push(&cp.args, head); strvec_push(&cp.args, remote->name); - for (i = 0; i < rs->raw_nr; i++) - strvec_push(&cp.args, rs->raw[i]); + for (i = 0; i < rs->nr; i++) + strvec_push(&cp.args, rs->items[i].raw); prepare_submodule_repo_env(&cp.env); cp.git_cmd = 1; diff --git a/t/t0410-partial-clone.sh b/t/t0410-partial-clone.sh index 34bdb3ab1f..2a5bdbeeb8 100755 --- a/t/t0410-partial-clone.sh +++ b/t/t0410-partial-clone.sh @@ -240,7 +240,7 @@ test_expect_success 'fetching of missing objects works with ref-in-want enabled' grep "fetch< fetch=.*ref-in-want" trace ' -test_expect_success 'fetching of missing objects from another promisor remote' ' +test_expect_success 'fetching from another promisor remote' ' git clone "file://$(pwd)/server" server2 && test_commit -C server2 bar && git -C server2 repack -a -d --write-bitmap-index && @@ -263,8 +263,8 @@ test_expect_success 'fetching of missing objects from another promisor remote' ' grep "$HASH2" out ' -test_expect_success 'fetching of missing objects configures a promisor remote' ' - git clone "file://$(pwd)/server" server3 && +test_expect_success 'fetching with --filter configures a promisor remote' ' + test_create_repo server3 && test_commit -C server3 baz && git -C server3 repack -a -d --write-bitmap-index && HASH3=$(git -C server3 rev-parse baz) && diff --git a/t/t0610-reftable-basics.sh b/t/t0610-reftable-basics.sh index 67bf3a82f6..4618ffc108 100755 --- a/t/t0610-reftable-basics.sh +++ b/t/t0610-reftable-basics.sh @@ -449,10 +449,12 @@ test_expect_success 'ref transaction: retry acquiring tables.list lock' ' ) ' -# This test fails most of the time on Windows systems. The root cause is +# This test fails most of the time on Cygwin systems. The root cause is # that Windows does not allow us to rename the "tables.list.lock" file into -# place when "tables.list" is open for reading by a concurrent process. -test_expect_success !WINDOWS 'ref transaction: many concurrent writers' ' +# place when "tables.list" is open for reading by a concurrent process. We have +# worked around that in our MinGW-based rename emulation, but the Cygwin +# emulation seems to be insufficient. +test_expect_success !CYGWIN 'ref transaction: many concurrent writers' ' test_when_finished "rm -rf repo" && git init repo && ( diff --git a/t/t1016-compatObjectFormat.sh b/t/t1016-compatObjectFormat.sh index 5641271b23..e88362fbe4 100755 --- a/t/t1016-compatObjectFormat.sh +++ b/t/t1016-compatObjectFormat.sh @@ -23,84 +23,83 @@ test_description='Test how well compatObjectFormat works' # the commit is identical to the commit in the other repository. compat_hash () { - case "$1" in - "sha1") - echo "sha256" - ;; - "sha256") - echo "sha1" - ;; - esac + case "$1" in + "sha1") + echo "sha256" + ;; + "sha256") + echo "sha1" + ;; + esac } hello_oid () { - case "$1" in - "sha1") - echo "$hello_sha1_oid" - ;; - "sha256") - echo "$hello_sha256_oid" - ;; - esac + case "$1" in + "sha1") + echo "$hello_sha1_oid" + ;; + "sha256") + echo "$hello_sha256_oid" + ;; + esac } tree_oid () { - case "$1" in - "sha1") - echo "$tree_sha1_oid" - ;; - "sha256") - echo "$tree_sha256_oid" - ;; - esac + case "$1" in + "sha1") + echo "$tree_sha1_oid" + ;; + "sha256") + echo "$tree_sha256_oid" + ;; + esac } commit_oid () { - case "$1" in - "sha1") - echo "$commit_sha1_oid" - ;; - "sha256") - echo "$commit_sha256_oid" - ;; - esac + case "$1" in + "sha1") + echo "$commit_sha1_oid" + ;; + "sha256") + echo "$commit_sha256_oid" + ;; + esac } commit2_oid () { - case "$1" in - "sha1") - echo "$commit2_sha1_oid" - ;; - "sha256") - echo "$commit2_sha256_oid" - ;; - esac + case "$1" in + "sha1") + echo "$commit2_sha1_oid" + ;; + "sha256") + echo "$commit2_sha256_oid" + ;; + esac } del_sigcommit () { - local delete="$1" - - if test "$delete" = "sha256" ; then - local pattern="gpgsig-sha256" - else - local pattern="gpgsig" - fi - test-tool delete-gpgsig "$pattern" + local delete="$1" + + if test "$delete" = "sha256" ; then + local pattern="gpgsig-sha256" + else + local pattern="gpgsig" + fi + test-tool delete-gpgsig "$pattern" } - del_sigtag () { - local storage="$1" - local delete="$2" - - if test "$storage" = "$delete" ; then - local pattern="trailer" - elif test "$storage" = "sha256" ; then - local pattern="gpgsig" - else - local pattern="gpgsig-sha256" - fi - test-tool delete-gpgsig "$pattern" + local storage="$1" + local delete="$2" + + if test "$storage" = "$delete" ; then + local pattern="trailer" + elif test "$storage" = "sha256" ; then + local pattern="gpgsig" + else + local pattern="gpgsig-sha256" + fi + test-tool delete-gpgsig "$pattern" } base=$(pwd) @@ -145,9 +144,9 @@ do ' test_expect_success "create a $hash branch" ' git checkout -b branch $(commit_oid $hash) && - echo "More more more give me more!" > more && + echo "More more more give me more!" >more && eval more_${hash}_oid=$(git hash-object more) && - echo "Another and another and another" > another && + echo "Another and another and another" >another && eval another_${hash}_oid=$(git hash-object another) && git update-index --add more another && git commit -m "Add more files!" && @@ -164,15 +163,15 @@ do ' test_expect_success GPG2 "create additional $hash signed commits" ' git commit --gpg-sign --allow-empty -m "This is an additional signed commit" && - git cat-file commit HEAD | del_sigcommit sha256 > "../${hash}_signedcommit3" && - git cat-file commit HEAD | del_sigcommit sha1 > "../${hash}_signedcommit4" && + git cat-file commit HEAD | del_sigcommit sha256 >"../${hash}_signedcommit3" && + git cat-file commit HEAD | del_sigcommit sha1 >"../${hash}_signedcommit4" && eval signedcommit3_${hash}_oid=$(git hash-object -t commit -w ../${hash}_signedcommit3) && eval signedcommit4_${hash}_oid=$(git hash-object -t commit -w ../${hash}_signedcommit4) ' test_expect_success GPG2 "create additional $hash signed tags" ' git tag -s -m "This is an additional signed tag" signedtag34 HEAD && - git cat-file tag signedtag34 | del_sigtag "${hash}" sha256 > ../${hash}_signedtag3 && - git cat-file tag signedtag34 | del_sigtag "${hash}" sha1 > ../${hash}_signedtag4 && + git cat-file tag signedtag34 | del_sigtag "${hash}" sha256 >../${hash}_signedtag3 && + git cat-file tag signedtag34 | del_sigtag "${hash}" sha1 >../${hash}_signedtag4 && eval signedtag3_${hash}_oid=$(git hash-object -t tag -w ../${hash}_signedtag3) && eval signedtag4_${hash}_oid=$(git hash-object -t tag -w ../${hash}_signedtag4) ' @@ -180,81 +179,80 @@ done cd "$base" compare_oids () { - test "$#" = 5 && { local PREREQ="$1"; shift; } || PREREQ= - local type="$1" - local name="$2" - local sha1_oid="$3" - local sha256_oid="$4" - - echo ${sha1_oid} > ${name}_sha1_expected - echo ${sha256_oid} > ${name}_sha256_expected - echo ${type} > ${name}_type_expected - - git --git-dir=repo-sha1/.git rev-parse --output-object-format=sha256 ${sha1_oid} > ${name}_sha1_sha256_found - git --git-dir=repo-sha256/.git rev-parse --output-object-format=sha1 ${sha256_oid} > ${name}_sha256_sha1_found - local sha1_sha256_oid="$(cat ${name}_sha1_sha256_found)" - local sha256_sha1_oid="$(cat ${name}_sha256_sha1_found)" - - test_expect_success $PREREQ "Verify ${type} ${name}'s sha1 oid" ' - git --git-dir=repo-sha256/.git rev-parse --output-object-format=sha1 ${sha256_oid} > ${name}_sha1 && - test_cmp ${name}_sha1 ${name}_sha1_expected -' - - test_expect_success $PREREQ "Verify ${type} ${name}'s sha256 oid" ' - git --git-dir=repo-sha1/.git rev-parse --output-object-format=sha256 ${sha1_oid} > ${name}_sha256 && - test_cmp ${name}_sha256 ${name}_sha256_expected -' + test "$#" = 5 && { local PREREQ="$1"; shift; } || PREREQ= + local type="$1" + local name="$2" + local sha1_oid="$3" + local sha256_oid="$4" + + echo ${sha1_oid} >${name}_sha1_expected + echo ${sha256_oid} >${name}_sha256_expected + echo ${type} >${name}_type_expected + + git --git-dir=repo-sha1/.git rev-parse --output-object-format=sha256 ${sha1_oid} >${name}_sha1_sha256_found + git --git-dir=repo-sha256/.git rev-parse --output-object-format=sha1 ${sha256_oid} >${name}_sha256_sha1_found + local sha1_sha256_oid="$(cat ${name}_sha1_sha256_found)" + local sha256_sha1_oid="$(cat ${name}_sha256_sha1_found)" + + test_expect_success $PREREQ "Verify ${type} ${name}'s sha1 oid" ' + git --git-dir=repo-sha256/.git rev-parse --output-object-format=sha1 ${sha256_oid} >${name}_sha1 && + test_cmp ${name}_sha1 ${name}_sha1_expected + ' - test_expect_success $PREREQ "Verify ${name}'s sha1 type" ' - git --git-dir=repo-sha1/.git cat-file -t ${sha1_oid} > ${name}_type1 && - git --git-dir=repo-sha256/.git cat-file -t ${sha256_sha1_oid} > ${name}_type2 && - test_cmp ${name}_type1 ${name}_type2 && - test_cmp ${name}_type1 ${name}_type_expected -' + test_expect_success $PREREQ "Verify ${type} ${name}'s sha256 oid" ' + git --git-dir=repo-sha1/.git rev-parse --output-object-format=sha256 ${sha1_oid} >${name}_sha256 && + test_cmp ${name}_sha256 ${name}_sha256_expected + ' - test_expect_success $PREREQ "Verify ${name}'s sha256 type" ' - git --git-dir=repo-sha256/.git cat-file -t ${sha256_oid} > ${name}_type3 && - git --git-dir=repo-sha1/.git cat-file -t ${sha1_sha256_oid} > ${name}_type4 && - test_cmp ${name}_type3 ${name}_type4 && - test_cmp ${name}_type3 ${name}_type_expected -' + test_expect_success $PREREQ "Verify ${name}'s sha1 type" ' + git --git-dir=repo-sha1/.git cat-file -t ${sha1_oid} >${name}_type1 && + git --git-dir=repo-sha256/.git cat-file -t ${sha256_sha1_oid} >${name}_type2 && + test_cmp ${name}_type1 ${name}_type2 && + test_cmp ${name}_type1 ${name}_type_expected + ' - test_expect_success $PREREQ "Verify ${name}'s sha1 size" ' - git --git-dir=repo-sha1/.git cat-file -s ${sha1_oid} > ${name}_size1 && - git --git-dir=repo-sha256/.git cat-file -s ${sha256_sha1_oid} > ${name}_size2 && - test_cmp ${name}_size1 ${name}_size2 -' + test_expect_success $PREREQ "Verify ${name}'s sha256 type" ' + git --git-dir=repo-sha256/.git cat-file -t ${sha256_oid} >${name}_type3 && + git --git-dir=repo-sha1/.git cat-file -t ${sha1_sha256_oid} >${name}_type4 && + test_cmp ${name}_type3 ${name}_type4 && + test_cmp ${name}_type3 ${name}_type_expected + ' - test_expect_success $PREREQ "Verify ${name}'s sha256 size" ' - git --git-dir=repo-sha256/.git cat-file -s ${sha256_oid} > ${name}_size3 && - git --git-dir=repo-sha1/.git cat-file -s ${sha1_sha256_oid} > ${name}_size4 && - test_cmp ${name}_size3 ${name}_size4 -' + test_expect_success $PREREQ "Verify ${name}'s sha1 size" ' + git --git-dir=repo-sha1/.git cat-file -s ${sha1_oid} >${name}_size1 && + git --git-dir=repo-sha256/.git cat-file -s ${sha256_sha1_oid} >${name}_size2 && + test_cmp ${name}_size1 ${name}_size2 + ' - test_expect_success $PREREQ "Verify ${name}'s sha1 pretty content" ' - git --git-dir=repo-sha1/.git cat-file -p ${sha1_oid} > ${name}_content1 && - git --git-dir=repo-sha256/.git cat-file -p ${sha256_sha1_oid} > ${name}_content2 && - test_cmp ${name}_content1 ${name}_content2 -' + test_expect_success $PREREQ "Verify ${name}'s sha256 size" ' + git --git-dir=repo-sha256/.git cat-file -s ${sha256_oid} >${name}_size3 && + git --git-dir=repo-sha1/.git cat-file -s ${sha1_sha256_oid} >${name}_size4 && + test_cmp ${name}_size3 ${name}_size4 + ' - test_expect_success $PREREQ "Verify ${name}'s sha256 pretty content" ' - git --git-dir=repo-sha256/.git cat-file -p ${sha256_oid} > ${name}_content3 && - git --git-dir=repo-sha1/.git cat-file -p ${sha1_sha256_oid} > ${name}_content4 && - test_cmp ${name}_content3 ${name}_content4 -' + test_expect_success $PREREQ "Verify ${name}'s sha1 pretty content" ' + git --git-dir=repo-sha1/.git cat-file -p ${sha1_oid} >${name}_content1 && + git --git-dir=repo-sha256/.git cat-file -p ${sha256_sha1_oid} >${name}_content2 && + test_cmp ${name}_content1 ${name}_content2 + ' - test_expect_success $PREREQ "Verify ${name}'s sha1 content" ' - git --git-dir=repo-sha1/.git cat-file ${type} ${sha1_oid} > ${name}_content5 && - git --git-dir=repo-sha256/.git cat-file ${type} ${sha256_sha1_oid} > ${name}_content6 && - test_cmp ${name}_content5 ${name}_content6 -' + test_expect_success $PREREQ "Verify ${name}'s sha256 pretty content" ' + git --git-dir=repo-sha256/.git cat-file -p ${sha256_oid} >${name}_content3 && + git --git-dir=repo-sha1/.git cat-file -p ${sha1_sha256_oid} >${name}_content4 && + test_cmp ${name}_content3 ${name}_content4 + ' - test_expect_success $PREREQ "Verify ${name}'s sha256 content" ' - git --git-dir=repo-sha256/.git cat-file ${type} ${sha256_oid} > ${name}_content7 && - git --git-dir=repo-sha1/.git cat-file ${type} ${sha1_sha256_oid} > ${name}_content8 && - test_cmp ${name}_content7 ${name}_content8 -' + test_expect_success $PREREQ "Verify ${name}'s sha1 content" ' + git --git-dir=repo-sha1/.git cat-file ${type} ${sha1_oid} >${name}_content5 && + git --git-dir=repo-sha256/.git cat-file ${type} ${sha256_sha1_oid} >${name}_content6 && + test_cmp ${name}_content5 ${name}_content6 + ' + test_expect_success $PREREQ "Verify ${name}'s sha256 content" ' + git --git-dir=repo-sha256/.git cat-file ${type} ${sha256_oid} >${name}_content7 && + git --git-dir=repo-sha1/.git cat-file ${type} ${sha1_sha256_oid} >${name}_content8 && + test_cmp ${name}_content7 ${name}_content8 + ' } compare_oids 'blob' hello "$hello_sha1_oid" "$hello_sha256_oid" diff --git a/t/t5300-pack-object.sh b/t/t5300-pack-object.sh index e13f289dd5..999d028a4c 100755 --- a/t/t5300-pack-object.sh +++ b/t/t5300-pack-object.sh @@ -155,6 +155,11 @@ test_expect_success 'pack without delta' ' check_deltas stderr = 0 ' +test_expect_success 'negative window clamps to 0' ' + git pack-objects --progress --window=-1 neg-window <obj-list 2>stderr && + check_deltas stderr = 0 +' + test_expect_success 'pack-objects with bogus arguments' ' test_must_fail git pack-objects --window=0 test-1 blah blah <obj-list ' @@ -629,11 +634,6 @@ test_expect_success 'prefetch objects' ' test_line_count = 1 donelines ' -test_expect_success 'negative window clamps to 0' ' - git pack-objects --progress --window=-1 neg-window <obj-list 2>stderr && - check_deltas stderr = 0 -' - for hash in sha1 sha256 do test_expect_success "verify-pack with $hash packfile" ' diff --git a/t/t5310-pack-bitmaps.sh b/t/t5310-pack-bitmaps.sh index a6de7c5764..eabfcd7ff6 100755 --- a/t/t5310-pack-bitmaps.sh +++ b/t/t5310-pack-bitmaps.sh @@ -502,6 +502,18 @@ test_expect_success 'boundary-based traversal is used when requested' ' done ' +test_expect_success 'left-right not confused by bitmap index' ' + git rev-list --left-right other...HEAD >expect && + git rev-list --use-bitmap-index --left-right other...HEAD >actual && + test_cmp expect actual +' + +test_expect_success 'left-right count not confused by bitmap-index' ' + git rev-list --left-right --count other...HEAD >expect && + git rev-list --use-bitmap-index --left-right --count other...HEAD >actual && + test_cmp expect actual +' + test_bitmap_cases "pack.writeBitmapLookupTable" test_expect_success 'verify writing bitmap lookup table when enabled' ' diff --git a/t/t5330-no-lazy-fetch-with-commit-graph.sh b/t/t5330-no-lazy-fetch-with-commit-graph.sh index 2cc7fd7a47..313eadba98 100755 --- a/t/t5330-no-lazy-fetch-with-commit-graph.sh +++ b/t/t5330-no-lazy-fetch-with-commit-graph.sh @@ -37,9 +37,9 @@ test_expect_success 'fetch any commit from promisor with the usage of the commit git -C with-commit-graph config remote.origin.partialclonefilter blob:none && test_commit -C with-commit any-commit && anycommit=$(git -C with-commit rev-parse HEAD) && - GIT_TRACE="$(pwd)/trace.txt" \ + test_must_fail env GIT_TRACE="$(pwd)/trace.txt" \ git -C with-commit-graph fetch origin $anycommit 2>err && - ! grep "fatal: promisor-remote: unable to fork off fetch subprocess" err && + test_grep ! "fatal: promisor-remote: unable to fork off fetch subprocess" err && grep "git fetch origin" trace.txt >actual && test_line_count = 1 actual ' diff --git a/t/t5332-multi-pack-reuse.sh b/t/t5332-multi-pack-reuse.sh index a1b547b90c..57cad7708f 100755 --- a/t/t5332-multi-pack-reuse.sh +++ b/t/t5332-multi-pack-reuse.sh @@ -258,4 +258,26 @@ test_expect_success 'duplicate objects' ' ) ' +test_expect_success 'duplicate objects with verbatim reuse' ' + git init duplicate-objects-verbatim && + ( + cd duplicate-objects-verbatim && + + git config pack.allowPackReuse multi && + + test_commit_bulk 64 && + + # take the first object from the main pack... + git show-index <$(ls $packdir/pack-*.idx) >obj.raw && + sort -nk1 <obj.raw | head -n1 | cut -d" " -f2 >in && + + # ...and create a separate pack containing just that object + p="$(git pack-objects $packdir/pack <in)" && + + git multi-pack-index write --bitmap --preferred-pack=pack-$p.idx && + + test_pack_objects_reused_all 192 2 + ) +' + test_done diff --git a/t/t5500-fetch-pack.sh b/t/t5500-fetch-pack.sh index 509133c1f9..2677cd5faa 100755 --- a/t/t5500-fetch-pack.sh +++ b/t/t5500-fetch-pack.sh @@ -925,6 +925,13 @@ test_expect_success 'fetch exclude tag one' ' test_cmp expected actual ' +test_expect_success 'fetch exclude tag one as revision' ' + test_when_finished rm -f rev err && + git -C shallow-exclude rev-parse one >rev && + test_must_fail git -C shallow12 fetch --shallow-exclude $(cat rev) origin 2>err && + grep "deepen-not is not a ref:" err +' + test_expect_success 'fetching deepen' ' test_create_repo shallow-deepen && ( diff --git a/t/t5582-fetch-negative-refspec.sh b/t/t5582-fetch-negative-refspec.sh index 7a80e47c2b..ae32f8178a 100755 --- a/t/t5582-fetch-negative-refspec.sh +++ b/t/t5582-fetch-negative-refspec.sh @@ -282,4 +282,8 @@ test_expect_success '--prefetch succeeds when refspec becomes empty' ' git -C one fetch --prefetch ' +test_expect_success '--prefetch succeeds with empty command line refspec' ' + git -C one fetch --prefetch origin +refs/tags/extra +' + test_done diff --git a/t/t5616-partial-clone.sh b/t/t5616-partial-clone.sh index 8415884754..4650451964 100755 --- a/t/t5616-partial-clone.sh +++ b/t/t5616-partial-clone.sh @@ -693,6 +693,36 @@ test_expect_success 'lazy-fetch in submodule succeeds' ' git -C client restore --recurse-submodules --source=HEAD^ :/ ' +test_expect_success 'after fetching descendants of non-promisor commits, gc works' ' + # Setup + git init full && + git -C full config uploadpack.allowfilter 1 && + git -C full config uploadpack.allowanysha1inwant 1 && + touch full/foo && + git -C full add foo && + git -C full commit -m "commit 1" && + git -C full checkout --detach && + + # Partial clone and push commit to remote + git clone "file://$(pwd)/full" --filter=blob:none partial && + echo "hello" > partial/foo && + git -C partial commit -a -m "commit 2" && + git -C partial push && + + # gc in partial repo + git -C partial gc --prune=now && + + # Create another commit in normal repo + git -C full checkout main && + echo " world" >> full/foo && + git -C full commit -a -m "commit 3" && + + # Pull from remote in partial repo, and run gc again + git -C partial pull && + git -C partial gc --prune=now +' + + . "$TEST_DIRECTORY"/lib-httpd.sh start_httpd diff --git a/t/t7800-difftool.sh b/t/t7800-difftool.sh index cc917b257e..9b74db5563 100755 --- a/t/t7800-difftool.sh +++ b/t/t7800-difftool.sh @@ -665,6 +665,10 @@ run_dir_diff_test 'difftool --dir-diff syncs worktree without unstaged change' ' test_cmp expect file ' +run_dir_diff_test 'difftool --dir-diff with no diff' ' + git difftool -d main main +' + write_script modify-file <<\EOF echo "new content" >file EOF diff --git a/t/test-lib.sh b/t/test-lib.sh index 508b5fe1f5..426036b33a 100644 --- a/t/test-lib.sh +++ b/t/test-lib.sh @@ -577,53 +577,6 @@ case $GIT_TEST_FSYNC in ;; esac -# Add libc MALLOC and MALLOC_PERTURB test only if we are not executing -# the test with valgrind and have not compiled with conflict SANITIZE -# options. -if test -n "$valgrind" || - test -n "$SANITIZE_ADDRESS" || - test -n "$SANITIZE_LEAK" || - test -n "$TEST_NO_MALLOC_CHECK" -then - setup_malloc_check () { - : nothing - } - teardown_malloc_check () { - : nothing - } -else - _USE_GLIBC_TUNABLES= - if _GLIBC_VERSION=$(getconf GNU_LIBC_VERSION 2>/dev/null) && - _GLIBC_VERSION=${_GLIBC_VERSION#"glibc "} && - expr 2.34 \<= "$_GLIBC_VERSION" >/dev/null - then - _USE_GLIBC_TUNABLES=YesPlease - fi - setup_malloc_check () { - local g - local t - MALLOC_CHECK_=3 MALLOC_PERTURB_=165 - export MALLOC_CHECK_ MALLOC_PERTURB_ - if test -n "$_USE_GLIBC_TUNABLES" - then - g= - LD_PRELOAD="libc_malloc_debug.so.0" - for t in \ - glibc.malloc.check=1 \ - glibc.malloc.perturb=165 - do - g="${g#:}:$t" - done - GLIBC_TUNABLES=$g - export LD_PRELOAD GLIBC_TUNABLES - fi - } - teardown_malloc_check () { - unset MALLOC_CHECK_ MALLOC_PERTURB_ - unset LD_PRELOAD GLIBC_TUNABLES - } -fi - # Protect ourselves from common misconfiguration to export # CDPATH into the environment unset CDPATH @@ -1462,6 +1415,56 @@ GIT_ATTR_NOSYSTEM=1 GIT_CEILING_DIRECTORIES="$TRASH_DIRECTORY/.." export PATH GIT_EXEC_PATH GIT_TEMPLATE_DIR GIT_CONFIG_NOSYSTEM GIT_ATTR_NOSYSTEM GIT_CEILING_DIRECTORIES +# Add libc MALLOC and MALLOC_PERTURB test only if we are not executing +# the test with valgrind and have not compiled with conflict SANITIZE +# options. +if test -n "$valgrind" || + test -n "$SANITIZE_ADDRESS" || + test -n "$SANITIZE_LEAK" || + test -n "$TEST_NO_MALLOC_CHECK" +then + setup_malloc_check () { + : nothing + } + teardown_malloc_check () { + : nothing + } +else + _USE_GLIBC_TUNABLES= + _USE_GLIBC_PRELOAD=libc_malloc_debug.so.0 + if _GLIBC_VERSION=$(getconf GNU_LIBC_VERSION 2>/dev/null) && + _GLIBC_VERSION=${_GLIBC_VERSION#"glibc "} && + expr 2.34 \<= "$_GLIBC_VERSION" >/dev/null && + stderr=$(LD_PRELOAD=$_USE_GLIBC_PRELOAD git version 2>&1 >/dev/null) && + test -z "$stderr" + then + _USE_GLIBC_TUNABLES=YesPlease + fi + setup_malloc_check () { + local g + local t + MALLOC_CHECK_=3 MALLOC_PERTURB_=165 + export MALLOC_CHECK_ MALLOC_PERTURB_ + if test -n "$_USE_GLIBC_TUNABLES" + then + g= + LD_PRELOAD=$_USE_GLIBC_PRELOAD + for t in \ + glibc.malloc.check=1 \ + glibc.malloc.perturb=165 + do + g="${g#:}:$t" + done + GLIBC_TUNABLES=$g + export LD_PRELOAD GLIBC_TUNABLES + fi + } + teardown_malloc_check () { + unset MALLOC_CHECK_ MALLOC_PERTURB_ + unset LD_PRELOAD GLIBC_TUNABLES + } +fi + if test -z "$GIT_TEST_CMP" then if test -n "$GIT_TEST_CMP_USE_COPIED_CONTEXT" diff --git a/t/unit-tests/clar/.editorconfig b/t/unit-tests/clar/.editorconfig new file mode 100644 index 0000000000..aa343a4288 --- /dev/null +++ b/t/unit-tests/clar/.editorconfig @@ -0,0 +1,13 @@ +root = true + +[*] +charset = utf-8 +insert_final_newline = true + +[*.{c,h}] +indent_style = tab +tab_width = 8 + +[CMakeLists.txt] +indent_style = tab +tab_width = 8 diff --git a/t/unit-tests/clar/.github/workflows/ci.yml b/t/unit-tests/clar/.github/workflows/ci.yml index b1ac2de460..0065843d17 100644 --- a/t/unit-tests/clar/.github/workflows/ci.yml +++ b/t/unit-tests/clar/.github/workflows/ci.yml @@ -10,14 +10,26 @@ jobs: build: strategy: matrix: - os: [ ubuntu-latest, macos-latest ] + platform: + - os: ubuntu-latest + generator: Unix Makefiles + - os: macos-latest + generator: Unix Makefiles + - os: windows-latest + generator: Visual Studio 17 2022 + - os: windows-latest + generator: MSYS Makefiles + - os: windows-latest + generator: MinGW Makefiles - runs-on: ${{ matrix.os }} + runs-on: ${{ matrix.platform.os }} steps: - name: Check out uses: actions/checkout@v2 - name: Build run: | - cd test - make + mkdir build + cd build + cmake .. -G "${{matrix.platform.generator}}" + cmake --build . diff --git a/t/unit-tests/clar/.gitignore b/t/unit-tests/clar/.gitignore new file mode 100644 index 0000000000..84c048a73c --- /dev/null +++ b/t/unit-tests/clar/.gitignore @@ -0,0 +1 @@ +/build/ diff --git a/t/unit-tests/clar/CMakeLists.txt b/t/unit-tests/clar/CMakeLists.txt new file mode 100644 index 0000000000..12d4af114f --- /dev/null +++ b/t/unit-tests/clar/CMakeLists.txt @@ -0,0 +1,28 @@ +cmake_minimum_required(VERSION 3.16..3.29) + +project(clar LANGUAGES C) + +option(BUILD_TESTS "Build test executable" ON) + +add_library(clar INTERFACE) +target_sources(clar INTERFACE + clar.c + clar.h + clar/fixtures.h + clar/fs.h + clar/print.h + clar/sandbox.h + clar/summary.h +) +set_target_properties(clar PROPERTIES + C_STANDARD 90 + C_STANDARD_REQUIRED ON + C_EXTENSIONS OFF +) + +if(CMAKE_PROJECT_NAME STREQUAL PROJECT_NAME) + include(CTest) + if(BUILD_TESTING) + add_subdirectory(test) + endif() +endif() diff --git a/t/unit-tests/clar/clar.c b/t/unit-tests/clar/clar.c index cef0f023c2..d54e455367 100644 --- a/t/unit-tests/clar/clar.c +++ b/t/unit-tests/clar/clar.c @@ -4,7 +4,12 @@ * This file is part of clar, distributed under the ISC license. * For full terms see the included COPYING file. */ -#include <assert.h> + +#define _BSD_SOURCE +#define _DARWIN_C_SOURCE +#define _DEFAULT_SOURCE + +#include <errno.h> #include <setjmp.h> #include <stdlib.h> #include <stdio.h> @@ -13,11 +18,22 @@ #include <stdarg.h> #include <wchar.h> #include <time.h> +#include <inttypes.h> /* required for sandboxing */ #include <sys/types.h> #include <sys/stat.h> +#if defined(__UCLIBC__) && ! defined(__UCLIBC_HAS_WCHAR__) + /* + * uClibc can optionally be built without wchar support, in which case + * the installed <wchar.h> is a stub that only defines the `whar_t` + * type but none of the functions typically declared by it. + */ +#else +# define CLAR_HAVE_WCHAR +#endif + #ifdef _WIN32 # define WIN32_LEAN_AND_MEAN # include <windows.h> @@ -28,6 +44,9 @@ # ifndef stat # define stat(path, st) _stat(path, st) + typedef struct _stat STAT_T; +# else + typedef struct stat STAT_T; # endif # ifndef mkdir # define mkdir(path, mode) _mkdir(path) @@ -60,30 +79,11 @@ # else # define p_snprintf snprintf # endif - -# ifndef PRIuZ -# define PRIuZ "Iu" -# endif -# ifndef PRIxZ -# define PRIxZ "Ix" -# endif - -# if defined(_MSC_VER) || (defined(__MINGW32__) && !defined(__MINGW64_VERSION_MAJOR)) - typedef struct stat STAT_T; -# else - typedef struct _stat STAT_T; -# endif #else # include <sys/wait.h> /* waitpid(2) */ # include <unistd.h> # define _MAIN_CC # define p_snprintf snprintf -# ifndef PRIuZ -# define PRIuZ "zu" -# endif -# ifndef PRIxZ -# define PRIxZ "zx" -# endif typedef struct stat STAT_T; #endif @@ -102,7 +102,7 @@ fixture_path(const char *base, const char *fixture_name); struct clar_error { const char *file; const char *function; - size_t line_number; + uintmax_t line_number; const char *error_msg; char *description; @@ -195,11 +195,12 @@ static void clar_print_shutdown(int test_count, int suite_count, int error_count static void clar_print_error(int num, const struct clar_report *report, const struct clar_error *error); static void clar_print_ontest(const char *suite_name, const char *test_name, int test_number, enum cl_test_status failed); static void clar_print_onsuite(const char *suite_name, int suite_index); +static void clar_print_onabortv(const char *msg, va_list argp); static void clar_print_onabort(const char *msg, ...); /* From clar_sandbox.c */ static void clar_unsandbox(void); -static int clar_sandbox(void); +static void clar_sandbox(void); /* From summary.h */ static struct clar_summary *clar_summary_init(const char *filename); @@ -218,6 +219,15 @@ static int clar_summary_shutdown(struct clar_summary *fp); _clar.trace_payload); \ } while (0) +static void clar_abort(const char *msg, ...) +{ + va_list argp; + va_start(argp, msg); + clar_print_onabortv(msg, argp); + va_end(argp); + exit(-1); +} + void cl_trace_register(cl_trace_cb *cb, void *payload) { _clar.pfn_trace_cb = cb; @@ -271,9 +281,7 @@ static double clar_time_diff(clar_time *start, clar_time *end) static void clar_time_now(clar_time *out) { - struct timezone tz; - - gettimeofday(out, &tz); + gettimeofday(out, NULL); } static double clar_time_diff(clar_time *start, clar_time *end) @@ -386,7 +394,8 @@ clar_run_suite(const struct clar_suite *suite, const char *filter) _clar.active_test = test[i].name; - report = calloc(1, sizeof(struct clar_report)); + if ((report = calloc(1, sizeof(*report))) == NULL) + clar_abort("Failed to allocate report.\n"); report->suite = _clar.active_suite; report->test = _clar.active_test; report->test_number = _clar.tests_ran; @@ -479,9 +488,10 @@ clar_parse_args(int argc, char **argv) switch (action) { case 's': { - struct clar_explicit *explicit = - calloc(1, sizeof(struct clar_explicit)); - assert(explicit); + struct clar_explicit *explicit; + + if ((explicit = calloc(1, sizeof(*explicit))) == NULL) + clar_abort("Failed to allocate explicit test.\n"); explicit->suite_idx = j; explicit->filter = argument; @@ -505,10 +515,8 @@ clar_parse_args(int argc, char **argv) } } - if (!found) { - clar_print_onabort("No suite matching '%s' found.\n", argument); - exit(-1); - } + if (!found) + clar_abort("No suite matching '%s' found.\n", argument); break; } @@ -540,11 +548,17 @@ clar_parse_args(int argc, char **argv) case 'r': _clar.write_summary = 1; free(_clar.summary_filename); - _clar.summary_filename = *(argument + 2) ? strdup(argument + 2) : NULL; + if (*(argument + 2)) { + if ((_clar.summary_filename = strdup(argument + 2)) == NULL) + clar_abort("Failed to allocate summary filename.\n"); + } else { + _clar.summary_filename = NULL; + } break; default: - assert(!"Unexpected commandline argument!"); + clar_abort("Unexpected commandline argument '%s'.\n", + argument[1]); } } } @@ -566,22 +580,18 @@ clar_test_init(int argc, char **argv) if (!_clar.summary_filename && (summary_env = getenv("CLAR_SUMMARY")) != NULL) { _clar.write_summary = 1; - _clar.summary_filename = strdup(summary_env); + if ((_clar.summary_filename = strdup(summary_env)) == NULL) + clar_abort("Failed to allocate summary filename.\n"); } if (_clar.write_summary && !_clar.summary_filename) - _clar.summary_filename = strdup("summary.xml"); + if ((_clar.summary_filename = strdup("summary.xml")) == NULL) + clar_abort("Failed to allocate summary filename.\n"); - if (_clar.write_summary && - !(_clar.summary = clar_summary_init(_clar.summary_filename))) { - clar_print_onabort("Failed to open the summary file\n"); - exit(-1); - } + if (_clar.write_summary) + _clar.summary = clar_summary_init(_clar.summary_filename); - if (clar_sandbox() < 0) { - clar_print_onabort("Failed to sandbox the test runner.\n"); - exit(-1); - } + clar_sandbox(); } int @@ -615,10 +625,9 @@ clar_test_shutdown(void) clar_unsandbox(); - if (_clar.write_summary && clar_summary_shutdown(_clar.summary) < 0) { - clar_print_onabort("Failed to write the summary file\n"); - exit(-1); - } + if (_clar.write_summary && clar_summary_shutdown(_clar.summary) < 0) + clar_abort("Failed to write the summary file '%s: %s.\n", + _clar.summary_filename, strerror(errno)); for (explicit = _clar.explicit; explicit; explicit = explicit_next) { explicit_next = explicit->next; @@ -649,7 +658,7 @@ static void abort_test(void) { if (!_clar.trampoline_enabled) { clar_print_onabort( - "Fatal error: a cleanup method raised an exception."); + "Fatal error: a cleanup method raised an exception.\n"); clar_report_errors(_clar.last_report); exit(-1); } @@ -673,7 +682,10 @@ void clar__fail( const char *description, int should_abort) { - struct clar_error *error = calloc(1, sizeof(struct clar_error)); + struct clar_error *error; + + if ((error = calloc(1, sizeof(*error))) == NULL) + clar_abort("Failed to allocate error.\n"); if (_clar.last_report->errors == NULL) _clar.last_report->errors = error; @@ -688,8 +700,9 @@ void clar__fail( error->line_number = line; error->error_msg = error_msg; - if (description != NULL) - error->description = strdup(description); + if (description != NULL && + (error->description = strdup(description)) == NULL) + clar_abort("Failed to allocate description.\n"); _clar.total_errors++; _clar.last_report->status = CL_TEST_FAILURE; @@ -763,6 +776,7 @@ void clar__assert_equal( } } } +#ifdef CLAR_HAVE_WCHAR else if (!strcmp("%ls", fmt)) { const wchar_t *wcs1 = va_arg(args, const wchar_t *); const wchar_t *wcs2 = va_arg(args, const wchar_t *); @@ -798,8 +812,9 @@ void clar__assert_equal( } } } - else if (!strcmp("%"PRIuZ, fmt) || !strcmp("%"PRIxZ, fmt)) { - size_t sz1 = va_arg(args, size_t), sz2 = va_arg(args, size_t); +#endif /* CLAR_HAVE_WCHAR */ + else if (!strcmp("%"PRIuMAX, fmt) || !strcmp("%"PRIxMAX, fmt)) { + uintmax_t sz1 = va_arg(args, uintmax_t), sz2 = va_arg(args, uintmax_t); is_equal = (sz1 == sz2); if (!is_equal) { int offset = p_snprintf(buf, sizeof(buf), fmt, sz1); diff --git a/t/unit-tests/clar/clar/print.h b/t/unit-tests/clar/clar/print.h index c17e2f693b..69d0ee967e 100644 --- a/t/unit-tests/clar/clar/print.h +++ b/t/unit-tests/clar/clar/print.h @@ -21,7 +21,7 @@ static void clar_print_clap_error(int num, const struct clar_report *report, con { printf(" %d) Failure:\n", num); - printf("%s::%s [%s:%"PRIuZ"]\n", + printf("%s::%s [%s:%"PRIuMAX"]\n", report->suite, report->test, error->file, @@ -136,7 +136,7 @@ static void clar_print_tap_ontest(const char *suite_name, const char *test_name, printf(" at:\n"); printf(" file: '"); print_escaped(error->file); printf("'\n"); - printf(" line: %" PRIuZ "\n", error->line_number); + printf(" line: %" PRIuMAX "\n", error->line_number); printf(" function: '%s'\n", error->function); printf(" ---\n"); @@ -202,10 +202,15 @@ static void clar_print_onsuite(const char *suite_name, int suite_index) PRINT(onsuite, suite_name, suite_index); } +static void clar_print_onabortv(const char *msg, va_list argp) +{ + PRINT(onabort, msg, argp); +} + static void clar_print_onabort(const char *msg, ...) { va_list argp; va_start(argp, msg); - PRINT(onabort, msg, argp); + clar_print_onabortv(msg, argp); va_end(argp); } diff --git a/t/unit-tests/clar/clar/sandbox.h b/t/unit-tests/clar/clar/sandbox.h index e25057b7c4..bc960f50e0 100644 --- a/t/unit-tests/clar/clar/sandbox.h +++ b/t/unit-tests/clar/clar/sandbox.h @@ -122,14 +122,14 @@ static int build_sandbox_path(void) if (mkdir(_clar_path, 0700) != 0) return -1; -#elif defined(__TANDEM) - if (mktemp(_clar_path) == NULL) +#elif defined(_WIN32) + if (_mktemp_s(_clar_path, sizeof(_clar_path)) != 0) return -1; if (mkdir(_clar_path, 0700) != 0) return -1; -#elif defined(_WIN32) - if (_mktemp_s(_clar_path, sizeof(_clar_path)) != 0) +#elif defined(__sun) || defined(__TANDEM) + if (mktemp(_clar_path) == NULL) return -1; if (mkdir(_clar_path, 0700) != 0) @@ -142,15 +142,14 @@ static int build_sandbox_path(void) return 0; } -static int clar_sandbox(void) +static void clar_sandbox(void) { if (_clar_path[0] == '\0' && build_sandbox_path() < 0) - return -1; + clar_abort("Failed to build sandbox path.\n"); if (chdir(_clar_path) != 0) - return -1; - - return 0; + clar_abort("Failed to change into sandbox directory '%s': %s.\n", + _clar_path, strerror(errno)); } const char *clar_sandbox_path(void) diff --git a/t/unit-tests/clar/clar/summary.h b/t/unit-tests/clar/clar/summary.h index 4dd352e28b..0d0b646fe7 100644 --- a/t/unit-tests/clar/clar/summary.h +++ b/t/unit-tests/clar/clar/summary.h @@ -66,16 +66,12 @@ struct clar_summary *clar_summary_init(const char *filename) struct clar_summary *summary; FILE *fp; - if ((fp = fopen(filename, "w")) == NULL) { - perror("fopen"); - return NULL; - } + if ((fp = fopen(filename, "w")) == NULL) + clar_abort("Failed to open the summary file '%s': %s.\n", + filename, strerror(errno)); - if ((summary = malloc(sizeof(struct clar_summary))) == NULL) { - perror("malloc"); - fclose(fp); - return NULL; - } + if ((summary = malloc(sizeof(struct clar_summary))) == NULL) + clar_abort("Failed to allocate summary.\n"); summary->filename = filename; summary->fp = fp; diff --git a/t/unit-tests/clar/test/.gitignore b/t/unit-tests/clar/test/.gitignore deleted file mode 100644 index a477d0c40c..0000000000 --- a/t/unit-tests/clar/test/.gitignore +++ /dev/null @@ -1,4 +0,0 @@ -clar.suite -.clarcache -clar_test -*.o diff --git a/t/unit-tests/clar/test/CMakeLists.txt b/t/unit-tests/clar/test/CMakeLists.txt new file mode 100644 index 0000000000..7f2c1dc17a --- /dev/null +++ b/t/unit-tests/clar/test/CMakeLists.txt @@ -0,0 +1,39 @@ +find_package(Python COMPONENTS Interpreter REQUIRED) + +add_custom_command(OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/clar.suite" + COMMAND "${Python_EXECUTABLE}" "${CMAKE_SOURCE_DIR}/generate.py" --output "${CMAKE_CURRENT_BINARY_DIR}" + DEPENDS main.c sample.c clar_test.h + WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}" +) + +add_executable(clar_test) +set_target_properties(clar_test PROPERTIES + C_STANDARD 90 + C_STANDARD_REQUIRED ON + C_EXTENSIONS OFF +) + +# MSVC generates all kinds of warnings. We may want to fix these in the future +# and then unconditionally treat warnings as errors. +if(NOT MSVC) + set_target_properties(clar_test PROPERTIES + COMPILE_WARNING_AS_ERROR ON + ) +endif() + +target_sources(clar_test PRIVATE + main.c + sample.c + "${CMAKE_CURRENT_BINARY_DIR}/clar.suite" +) +target_compile_definitions(clar_test PRIVATE + CLAR_FIXTURE_PATH="${CMAKE_CURRENT_SOURCE_DIR}/resources/" +) +target_compile_options(clar_test PRIVATE + $<IF:$<CXX_COMPILER_ID:MSVC>,/W4,-Wall> +) +target_include_directories(clar_test PRIVATE + "${CMAKE_SOURCE_DIR}" + "${CMAKE_CURRENT_BINARY_DIR}" +) +target_link_libraries(clar_test clar) diff --git a/t/unit-tests/clar/test/Makefile b/t/unit-tests/clar/test/Makefile deleted file mode 100644 index 93c6b2ad32..0000000000 --- a/t/unit-tests/clar/test/Makefile +++ /dev/null @@ -1,39 +0,0 @@ -# -# Copyright (c) Vicent Marti. All rights reserved. -# -# This file is part of clar, distributed under the ISC license. -# For full terms see the included COPYING file. -# - -# -# Set up the path to the clar sources and to the fixtures directory -# -# The fixture path needs to be an absolute path so it can be used -# even after we have chdir'ed into the test directory while testing. -# -CURRENT_MAKEFILE := $(word $(words $(MAKEFILE_LIST)),$(MAKEFILE_LIST)) -TEST_DIRECTORY := $(abspath $(dir $(CURRENT_MAKEFILE))) -CLAR_PATH := $(dir $(TEST_DIRECTORY)) -CLAR_FIXTURE_PATH := $(TEST_DIRECTORY)/resources/ - -CFLAGS=-g -I.. -I. -Wall -DCLAR_FIXTURE_PATH=\"$(CLAR_FIXTURE_PATH)\" - -.PHONY: clean - -# list the objects that go into our test -objects = main.o sample.o - -# build the test executable itself -clar_test: $(objects) clar_test.h clar.suite $(CLAR_PATH)clar.c - $(CC) $(CFLAGS) -o $@ "$(CLAR_PATH)clar.c" $(objects) - -# test object files depend on clar macros -$(objects) : $(CLAR_PATH)clar.h - -# build the clar.suite file of test metadata -clar.suite: - python "$(CLAR_PATH)generate.py" . - -# remove all generated files -clean: - $(RM) -rf *.o clar.suite .clarcache clar_test clar_test.dSYM diff --git a/t/unit-tests/generate-clar-decls.sh b/t/unit-tests/generate-clar-decls.sh new file mode 100755 index 0000000000..688e0885f4 --- /dev/null +++ b/t/unit-tests/generate-clar-decls.sh @@ -0,0 +1,16 @@ +#!/bin/sh + +if test $# -lt 2 +then + echo "USAGE: $0 <OUTPUT> <SUITE>..." 2>&1 + exit 1 +fi + +OUTPUT="$1" +shift + +for suite in "$@" +do + sed -ne "s/^\(void test_$(basename "${suite%.c}")__[a-zA-Z_0-9][a-zA-Z_0-9]*(void)\)$/extern \1;/p" "$suite" || + exit 1 +done >"$OUTPUT" @@ -13,19 +13,20 @@ * Copyright (c) 2013, 2014 Christian Couder <chriscool@tuxfamily.org> */ -struct trailer_info { +struct trailer_block { /* * True if there is a blank line before the location pointed to by - * trailer_block_start. + * "start". */ int blank_line_before_trailer; /* - * Offsets to the trailer block start and end positions in the input - * string. If no trailer block is found, these are both set to the - * "true" end of the input (find_end_of_log_message()). + * The locations of the start and end positions of the trailer block + * found, as offsets from the beginning of the source text from which + * this trailer block was parsed. If no trailer block is found, these + * are both set to 0. */ - size_t trailer_block_start, trailer_block_end; + size_t start, end; /* * Array of trailers found. @@ -981,16 +982,16 @@ static void unfold_value(struct strbuf *val) strbuf_release(&out); } -static struct trailer_info *trailer_info_new(void) +static struct trailer_block *trailer_block_new(void) { - struct trailer_info *info = xcalloc(1, sizeof(*info)); - return info; + struct trailer_block *trailer_block = xcalloc(1, sizeof(*trailer_block)); + return trailer_block; } -static struct trailer_info *trailer_info_get(const struct process_trailer_options *opts, - const char *str) +static struct trailer_block *trailer_block_get(const struct process_trailer_options *opts, + const char *str) { - struct trailer_info *info = trailer_info_new(); + struct trailer_block *trailer_block = trailer_block_new(); size_t end_of_log_message = 0, trailer_block_start = 0; struct strbuf **trailer_lines, **ptr; char **trailer_strings = NULL; @@ -1023,34 +1024,34 @@ static struct trailer_info *trailer_info_get(const struct process_trailer_option } strbuf_list_free(trailer_lines); - info->blank_line_before_trailer = ends_with_blank_line(str, - trailer_block_start); - info->trailer_block_start = trailer_block_start; - info->trailer_block_end = end_of_log_message; - info->trailers = trailer_strings; - info->trailer_nr = nr; + trailer_block->blank_line_before_trailer = ends_with_blank_line(str, + trailer_block_start); + trailer_block->start = trailer_block_start; + trailer_block->end = end_of_log_message; + trailer_block->trailers = trailer_strings; + trailer_block->trailer_nr = nr; - return info; + return trailer_block; } /* - * Parse trailers in "str", populating the trailer info and "trailer_objects" + * Parse trailers in "str", populating the trailer_block and "trailer_objects" * linked list structure. */ -struct trailer_info *parse_trailers(const struct process_trailer_options *opts, - const char *str, - struct list_head *trailer_objects) +struct trailer_block *parse_trailers(const struct process_trailer_options *opts, + const char *str, + struct list_head *trailer_objects) { - struct trailer_info *info; + struct trailer_block *trailer_block; struct strbuf tok = STRBUF_INIT; struct strbuf val = STRBUF_INIT; size_t i; - info = trailer_info_get(opts, str); + trailer_block = trailer_block_get(opts, str); - for (i = 0; i < info->trailer_nr; i++) { + for (i = 0; i < trailer_block->trailer_nr; i++) { int separator_pos; - char *trailer = info->trailers[i]; + char *trailer = trailer_block->trailers[i]; if (starts_with(trailer, comment_line_str)) continue; separator_pos = find_separator(trailer, separators); @@ -1071,7 +1072,7 @@ struct trailer_info *parse_trailers(const struct process_trailer_options *opts, } } - return info; + return trailer_block; } void free_trailers(struct list_head *trailers) @@ -1083,28 +1084,28 @@ void free_trailers(struct list_head *trailers) } } -size_t trailer_block_start(struct trailer_info *info) +size_t trailer_block_start(struct trailer_block *trailer_block) { - return info->trailer_block_start; + return trailer_block->start; } -size_t trailer_block_end(struct trailer_info *info) +size_t trailer_block_end(struct trailer_block *trailer_block) { - return info->trailer_block_end; + return trailer_block->end; } -int blank_line_before_trailer_block(struct trailer_info *info) +int blank_line_before_trailer_block(struct trailer_block *trailer_block) { - return info->blank_line_before_trailer; + return trailer_block->blank_line_before_trailer; } -void trailer_info_release(struct trailer_info *info) +void trailer_block_release(struct trailer_block *trailer_block) { size_t i; - for (i = 0; i < info->trailer_nr; i++) - free(info->trailers[i]); - free(info->trailers); - free(info); + for (i = 0; i < trailer_block->trailer_nr; i++) + free(trailer_block->trailers[i]); + free(trailer_block->trailers); + free(trailer_block); } void format_trailers(const struct process_trailer_options *opts, @@ -1174,19 +1175,19 @@ void format_trailers_from_commit(const struct process_trailer_options *opts, struct strbuf *out) { LIST_HEAD(trailer_objects); - struct trailer_info *info = parse_trailers(opts, msg, &trailer_objects); + struct trailer_block *trailer_block = parse_trailers(opts, msg, &trailer_objects); /* If we want the whole block untouched, we can take the fast path. */ if (!opts->only_trailers && !opts->unfold && !opts->filter && !opts->separator && !opts->key_only && !opts->value_only && !opts->key_value_separator) { - strbuf_add(out, msg + info->trailer_block_start, - info->trailer_block_end - info->trailer_block_start); + strbuf_add(out, msg + trailer_block->start, + trailer_block->end - trailer_block->start); } else format_trailers(opts, &trailer_objects, out); free_trailers(&trailer_objects); - trailer_info_release(info); + trailer_block_release(trailer_block); } void trailer_iterator_init(struct trailer_iterator *iter, const char *msg) @@ -1195,14 +1196,14 @@ void trailer_iterator_init(struct trailer_iterator *iter, const char *msg) strbuf_init(&iter->key, 0); strbuf_init(&iter->val, 0); opts.no_divider = 1; - iter->internal.info = trailer_info_get(&opts, msg); + iter->internal.trailer_block = trailer_block_get(&opts, msg); iter->internal.cur = 0; } int trailer_iterator_advance(struct trailer_iterator *iter) { - if (iter->internal.cur < iter->internal.info->trailer_nr) { - char *line = iter->internal.info->trailers[iter->internal.cur++]; + if (iter->internal.cur < iter->internal.trailer_block->trailer_nr) { + char *line = iter->internal.trailer_block->trailers[iter->internal.cur++]; int separator_pos = find_separator(line, separators); iter->raw = line; @@ -1219,7 +1220,7 @@ int trailer_iterator_advance(struct trailer_iterator *iter) void trailer_iterator_release(struct trailer_iterator *iter) { - trailer_info_release(iter->internal.info); + trailer_block_release(iter->internal.trailer_block); strbuf_release(&iter->val); strbuf_release(&iter->key); } @@ -4,7 +4,7 @@ #include "list.h" #include "strbuf.h" -struct trailer_info; +struct trailer_block; struct strvec; enum trailer_where { @@ -72,12 +72,12 @@ void process_trailers_lists(struct list_head *head, struct list_head *arg_head); /* - * Given some input string "str", return a pointer to an opaque trailer_info + * Given some input string "str", return a pointer to an opaque trailer_block * structure. Also populate the trailer_objects list with parsed trailer * objects. Internally this calls trailer_info_get() to get the opaque pointer, * but does some extra work to populate the trailer_objects linked list. * - * The opaque trailer_info pointer can be used to check the position of the + * The opaque trailer_block pointer can be used to check the position of the * trailer block as offsets relative to the beginning of "str" in * trailer_block_start() and trailer_block_end(). * blank_line_before_trailer_block() returns 1 if there is a blank line just @@ -89,21 +89,21 @@ void process_trailers_lists(struct list_head *head, * For iterating through the parsed trailer block (if you don't care about the * position of the trailer block itself in the context of the larger string text * from which it was parsed), please see trailer_iterator_init() which uses the - * trailer_info struct internally. + * trailer_block struct internally. * * Lastly, callers should call trailer_info_release() when they are done using * the opaque pointer. * - * NOTE: Callers should treat both trailer_info and trailer_objects as - * read-only items, because there is some overlap between the two (trailer_info + * NOTE: Callers should treat both trailer_block and trailer_objects as + * read-only items, because there is some overlap between the two (trailer_block * has "char **trailers" string array, and trailer_objects will have the same * data but as a linked list of trailer_item objects). This API does not perform * any synchronization between the two. In the future we should be able to * reduce the duplication and use just the linked list. */ -struct trailer_info *parse_trailers(const struct process_trailer_options *, - const char *str, - struct list_head *trailer_objects); +struct trailer_block *parse_trailers(const struct process_trailer_options *, + const char *str, + struct list_head *trailer_objects); /* * Return the offset of the start of the trailer block. That is, 0 is the start @@ -111,24 +111,24 @@ struct trailer_info *parse_trailers(const struct process_trailer_options *, * indicates how many bytes we have to skip over before we get to the beginning * of the trailer block. */ -size_t trailer_block_start(struct trailer_info *); +size_t trailer_block_start(struct trailer_block *); /* * Return the end of the trailer block, again relative to the start of the * input. */ -size_t trailer_block_end(struct trailer_info *); +size_t trailer_block_end(struct trailer_block *); /* * Return 1 if the trailer block had an extra newline (blank line) just before * it. */ -int blank_line_before_trailer_block(struct trailer_info *); +int blank_line_before_trailer_block(struct trailer_block *); /* - * Free trailer_info struct. + * Free trailer_block struct. */ -void trailer_info_release(struct trailer_info *info); +void trailer_block_release(struct trailer_block *); void trailer_config_init(void); void format_trailers(const struct process_trailer_options *, @@ -167,7 +167,7 @@ struct trailer_iterator { /* private */ struct { - struct trailer_info *info; + struct trailer_block *trailer_block; size_t cur; } internal; }; diff --git a/upload-pack.c b/upload-pack.c index b4a59c3518..43006c0614 100644 --- a/upload-pack.c +++ b/upload-pack.c @@ -1026,10 +1026,14 @@ static int process_deepen_not(const char *line, struct oidset *deepen_not, int * { const char *arg; if (skip_prefix(line, "deepen-not ", &arg)) { + int cnt; char *ref = NULL; struct object_id oid; - if (expand_ref(the_repository, arg, strlen(arg), &oid, &ref) != 1) + cnt = expand_ref(the_repository, arg, strlen(arg), &oid, &ref); + if (cnt > 1) die("git upload-pack: ambiguous deepen-not: %s", line); + if (cnt < 1) + die("git upload-pack: deepen-not is not a ref: %s", line); oidset_insert(deepen_not, &oid); free(ref); *deepen_rev_list = 1; |
