diff options
69 files changed, 1044 insertions, 593 deletions
diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index bd6f75b8e0..592f9193a8 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -37,14 +37,14 @@ jobs: echo "::set-output name=enabled::$enabled" - name: skip if the commit or tree was already tested id: skip-if-redundant - uses: actions/github-script@v3 + uses: actions/github-script@v6 if: steps.check-ref.outputs.enabled == 'yes' with: github-token: ${{secrets.GITHUB_TOKEN}} script: | try { // Figure out workflow ID, commit and tree - const { data: run } = await github.actions.getWorkflowRun({ + const { data: run } = await github.rest.actions.getWorkflowRun({ owner: context.repo.owner, repo: context.repo.repo, run_id: context.runId, @@ -54,7 +54,7 @@ jobs: const tree_id = run.head_commit.tree_id; // See whether there is a successful run for that commit or tree - const { data: runs } = await github.actions.listWorkflowRuns({ + const { data: runs } = await github.rest.actions.listWorkflowRuns({ owner: context.repo.owner, repo: context.repo.repo, per_page: 500, diff --git a/Documentation/MyFirstContribution.txt b/Documentation/MyFirstContribution.txt index 1a4be8ee0a..ccfd0cb5f3 100644 --- a/Documentation/MyFirstContribution.txt +++ b/Documentation/MyFirstContribution.txt @@ -736,7 +736,7 @@ the {lore}[Git mailing list archive]: 2022-02-21 1:43 ` John Cai 2022-02-21 1:50 ` Taylor Blau 2022-02-23 19:50 ` John Cai -2022-02-18 20:00 ` // other replies ellided +2022-02-18 20:00 ` // other replies elided 2022-02-18 18:40 ` [PATCH 2/3] reflog: call reflog_delete from reflog.c John Cai via GitGitGadget 2022-02-18 19:15 ` Ævar Arnfjörð Bjarmason 2022-02-18 20:26 ` Junio C Hamano diff --git a/Documentation/RelNotes/2.39.0.txt b/Documentation/RelNotes/2.39.0.txt index 7096f07689..f9bc29f7d2 100644 --- a/Documentation/RelNotes/2.39.0.txt +++ b/Documentation/RelNotes/2.39.0.txt @@ -29,6 +29,9 @@ UI, Workflows & Features existing bugs in the internal patch-id logic that did not match what "git patch-id" produces have been corrected. + * Enable gc.cruftpacks by default for those who opt into + feature.experimental setting. + Performance, Internal Implementation, Development Support etc. -------------------------------------------------------------- @@ -84,6 +87,28 @@ Performance, Internal Implementation, Development Support etc. * Make sure generated dependency file is stably sorted to help developers debugging their build issues. + * The glossary entries for "commit-graph file" and "reachability + bitmap" have been added. + + * Various tests exercising the transfer.credentialsInUrl + configuration are taught to avoid making requests which require + resolving localhost to reduce CI-flakiness. + + * A redundant diagnostic message is dropped from test_path_is_missing(). + + * Simplify the run-command API. + + * Update the actions/github-script dependency in CI to avoid a + deprecation warning. + + * Progress on being able to initialize a rev_info struct with a + macro. + + * Add trace2 counters to the region to clear skip worktree bits in a + sparse checkout. + + * Modernize test script to avoid "test -f" and friends. + Fixes since v2.38 ----------------- @@ -219,6 +244,12 @@ Fixes since v2.38 configuration are taught to avoid making requests which require resolving localhost to reduce CI-flakiness. + * The adjust_shared_perm() helper function learned to refrain from + setting the "g+s" bit on directories when it is not necessary. + + * "git archive" mistakenly complained twice about a missing + executable, which has been corrected. + * Other code cleanup, docfix, build fix, etc. (merge 413bc6d20a ds/cmd-main-reorder later to maint). (merge 8d2863e4ed nw/t1002-cleanup later to maint). diff --git a/Documentation/config/core.txt b/Documentation/config/core.txt index 37afbaf5a4..dfbdaf00b8 100644 --- a/Documentation/config/core.txt +++ b/Documentation/config/core.txt @@ -618,7 +618,7 @@ but risks losing recent work in the event of an unclean system shutdown. * `loose-object` hardens objects added to the repo in loose-object form. * `pack` hardens objects added to the repo in packfile form. * `pack-metadata` hardens packfile bitmaps and indexes. -* `commit-graph` hardens the commit graph file. +* `commit-graph` hardens the commit-graph file. * `index` hardens the index when it is modified. * `objects` is an aggregate option that is equivalent to `loose-object,pack`. diff --git a/Documentation/config/feature.txt b/Documentation/config/feature.txt index cdecd04e5b..95975e5091 100644 --- a/Documentation/config/feature.txt +++ b/Documentation/config/feature.txt @@ -14,6 +14,9 @@ feature.experimental:: + * `fetch.negotiationAlgorithm=skipping` may improve fetch negotiation times by skipping more commits at a time, reducing the number of round trips. ++ +* `gc.cruftPacks=true` reduces disk space used by unreachable objects during +garbage collection, preventing loose object explosions. feature.manyFiles:: Enable config options that optimize for repos with many files in the diff --git a/Documentation/git-ls-files.txt b/Documentation/git-ls-files.txt index d7986419c2..440043cdb8 100644 --- a/Documentation/git-ls-files.txt +++ b/Documentation/git-ls-files.txt @@ -10,8 +10,8 @@ SYNOPSIS -------- [verse] 'git ls-files' [-z] [-t] [-v] [-f] - [-c|--cached] [-d|--deleted] [-o|--others] [-i|--|ignored] - [-s|--stage] [-u|--unmerged] [-k|--|killed] [-m|--modified] + [-c|--cached] [-d|--deleted] [-o|--others] [-i|--ignored] + [-s|--stage] [-u|--unmerged] [-k|--killed] [-m|--modified] [--directory [--no-empty-directory]] [--eol] [--deduplicate] [-x <pattern>|--exclude=<pattern>] diff --git a/Documentation/git-pack-redundant.txt b/Documentation/git-pack-redundant.txt index dda80a740c..99ef13839d 100644 --- a/Documentation/git-pack-redundant.txt +++ b/Documentation/git-pack-redundant.txt @@ -34,7 +34,7 @@ OPTIONS --alt-odb:: Don't require objects present in packs from alternate object - directories to be present in local packs. + database (odb) directories to be present in local packs. --verbose:: Outputs some statistics to stderr. Has a small performance penalty. diff --git a/Documentation/git-repack.txt b/Documentation/git-repack.txt index 0bf13893d8..4017157949 100644 --- a/Documentation/git-repack.txt +++ b/Documentation/git-repack.txt @@ -74,6 +74,12 @@ to the new separate pack will be written. immediately instead of waiting for the next `git gc` invocation. Only useful with `--cruft -d`. +--expire-to=<dir>:: + Write a cruft pack containing pruned objects (if any) to the + directory `<dir>`. This option is useful for keeping a copy of + any pruned objects in a separate directory as a backup. Only + useful with `--cruft -d`. + -l:: Pass the `--local` option to 'git pack-objects'. See linkgit:git-pack-objects[1]. diff --git a/Documentation/gitcredentials.txt b/Documentation/gitcredentials.txt index 80517b4eb2..6df50e8a14 100644 --- a/Documentation/gitcredentials.txt +++ b/Documentation/gitcredentials.txt @@ -17,9 +17,10 @@ DESCRIPTION Git will sometimes need credentials from the user in order to perform operations; for example, it may need to ask for a username and password -in order to access a remote repository over HTTP. This manual describes -the mechanisms Git uses to request these credentials, as well as some -features to avoid inputting these credentials repeatedly. +in order to access a remote repository over HTTP. Some remotes accept +a personal access token or OAuth access token as a password. This +manual describes the mechanisms Git uses to request these credentials, +as well as some features to avoid inputting these credentials repeatedly. REQUESTING CREDENTIALS ---------------------- diff --git a/Documentation/gitformat-commit-graph.txt b/Documentation/gitformat-commit-graph.txt index 7324665716..31cad585e2 100644 --- a/Documentation/gitformat-commit-graph.txt +++ b/Documentation/gitformat-commit-graph.txt @@ -3,7 +3,7 @@ gitformat-commit-graph(5) NAME ---- -gitformat-commit-graph - Git commit graph format +gitformat-commit-graph - Git commit-graph format SYNOPSIS -------- @@ -14,7 +14,7 @@ $GIT_DIR/objects/info/commit-graphs/* DESCRIPTION ----------- -The Git commit graph stores a list of commit OIDs and some associated +The Git commit-graph stores a list of commit OIDs and some associated metadata, including: - The generation number of the commit. @@ -34,7 +34,7 @@ corresponding to the array position within the list of commit OIDs. Due to some special constants we use to track parents, we can store at most (1 << 30) + (1 << 29) + (1 << 28) - 1 (around 1.8 billion) commits. -== Commit graph files have the following format: +== Commit-graph files have the following format: In order to allow extensions that add extra data to the graph, we organize the body into "chunks" and provide a binary lookup table at the beginning diff --git a/Documentation/glossary-content.txt b/Documentation/glossary-content.txt index aa2f41f5e7..5a537268e2 100644 --- a/Documentation/glossary-content.txt +++ b/Documentation/glossary-content.txt @@ -20,7 +20,7 @@ [[def_branch]]branch:: A "branch" is a line of development. The most recent <<def_commit,commit>> on a branch is referred to as the tip of - that branch. The tip of the branch is referenced by a branch + that branch. The tip of the branch is <<def_ref,referenced>> by a branch <<def_head,head>>, which moves forward as additional development is done on the branch. A single Git <<def_repository,repository>> can track an arbitrary number of @@ -75,6 +75,21 @@ state in the Git history, by creating a new commit representing the current state of the <<def_index,index>> and advancing <<def_HEAD,HEAD>> to point at the new commit. +[[def_commit_graph_general]]commit graph concept, representations and usage:: + A synonym for the <<def_DAG,DAG>> structure formed by the commits + in the object database, <<def_ref,referenced>> by branch tips, + using their <<def_chain,chain>> of linked commits. + This structure is the definitive commit graph. The + graph can be represented in other ways, e.g. the + <<def_commit_graph_file,"commit-graph" file>>. + +[[def_commit_graph_file]]commit-graph file:: + The "commit-graph" (normally hyphenated) file is a supplemental + representation of the <<def_commit_graph_general,commit graph>> + which accelerates commit graph walks. The "commit-graph" file is + stored either in the .git/objects/info directory or in the info + directory of an alternate object database. + [[def_commit_object]]commit object:: An <<def_object,object>> which contains the information about a particular <<def_revision,revision>>, such as <<def_parent,parents>>, committer, @@ -262,7 +277,7 @@ This commit is referred to as a "merge commit", or sometimes just a identified by its <<def_object_name,object name>>. The objects usually live in `$GIT_DIR/objects/`. -[[def_object_identifier]]object identifier:: +[[def_object_identifier]]object identifier (oid):: Synonym for <<def_object_name,object name>>. [[def_object_name]]object name:: @@ -493,6 +508,14 @@ exclude;; <<def_tree_object,trees>> to the trees or <<def_blob_object,blobs>> that they contain. +[[def_reachability_bitmap]]reachability bitmaps:: + Reachability bitmaps store information about the + <<def_reachable,reachability>> of a selected set of commits in + a packfile, or a multi-pack index (MIDX), to speed up object search. + The bitmaps are stored in a ".bitmap" file. A repository may have at + most one bitmap file in use. The bitmap file may belong to either one + pack, or the repository's multi-pack index (if it exists). + [[def_rebase]]rebase:: To reapply a series of changes from a <<def_branch,branch>> to a different base, and reset the <<def_head,head>> of that branch diff --git a/Documentation/howto/maintain-git.txt b/Documentation/howto/maintain-git.txt index a67130debb..215e2edb0f 100644 --- a/Documentation/howto/maintain-git.txt +++ b/Documentation/howto/maintain-git.txt @@ -256,7 +256,7 @@ by doing the following: merged to 'next', add it at the end of the list. Then: $ git checkout -B jch master - $ Meta/redo-jch.sh -c1 + $ sh Meta/redo-jch.sh -c1 to rebuild the 'jch' branch from scratch. "-c1" tells the script to stop merging at the first line that begins with '###' diff --git a/Documentation/technical/commit-graph.txt b/Documentation/technical/commit-graph.txt index 90c9760c23..86fed0de0f 100644 --- a/Documentation/technical/commit-graph.txt +++ b/Documentation/technical/commit-graph.txt @@ -1,4 +1,4 @@ -Git Commit Graph Design Notes +Git Commit-Graph Design Notes ============================= Git walks the commit graph for many reasons, including: @@ -17,7 +17,7 @@ There are two main costs here: The commit-graph file is a supplemental data structure that accelerates commit graph walks. If a user downgrades or disables the 'core.commitGraph' -config setting, then the existing ODB is sufficient. The file is stored +config setting, then the existing object database is sufficient. The file is stored as "commit-graph" either in the .git/objects/info directory or in the info directory of an alternate. @@ -95,7 +95,7 @@ with default order), but is not used when the topological order is required (such as merge base calculations, "git log --graph"). In practice, we expect some commits to be created recently and not stored -in the commit graph. We can treat these commits as having "infinite" +in the commit-graph. We can treat these commits as having "infinite" generation number and walk until reaching commits with known generation number. @@ -149,7 +149,7 @@ Design Details helpful for these clones, anyway. The commit-graph will not be read or written when shallow commits are present. -Commit Graphs Chains +Commit-Graphs Chains -------------------- Typically, repos grow with near-constant velocity (commits per day). Over time, diff --git a/Documentation/technical/parallel-checkout.txt b/Documentation/technical/parallel-checkout.txt index e790258a1a..47c9b6183c 100644 --- a/Documentation/technical/parallel-checkout.txt +++ b/Documentation/technical/parallel-checkout.txt @@ -56,7 +56,7 @@ Rejected Multi-Threaded Solution The most "straightforward" implementation would be to spread the set of to-be-updated cache entries across multiple threads. But due to the -thread-unsafe functions in the ODB code, we would have to use locks to +thread-unsafe functions in the object database code, we would have to use locks to coordinate the parallel operation. An early prototype of this solution showed that the multi-threaded checkout would bring performance improvements over the sequential code, but there was still too much lock diff --git a/Documentation/technical/repository-version.txt b/Documentation/technical/repository-version.txt index 7844ef30ff..8ef664b0b9 100644 --- a/Documentation/technical/repository-version.txt +++ b/Documentation/technical/repository-version.txt @@ -82,9 +82,9 @@ 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` +==== `partialClone` -When the config key `extensions.partialclone` is set, it indicates +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" @@ -133,10 +133,6 @@ Issues of note: you are using libcurl older than 7.34.0. Otherwise you can use NO_OPENSSL without losing git-imap-send. - By default, git uses OpenSSL for SHA1 but it will use its own - library (inspired by Mozilla's) with either NO_OPENSSL or - BLK_SHA1. - - "libcurl" library is used for fetching and pushing repositories over http:// or https://, as well as by git-imap-send if the curl version is >= 7.34.0. If you do @@ -4,8 +4,20 @@ all:: # Import tree-wide shared Makefile behavior and libraries include shared.mak +# == Makefile defines == +# +# These defines change the behavior of the Makefile itself, but have +# no impact on what it builds: +# # Define V=1 to have a more verbose compile. # +# == Portability and optional library defines == +# +# These defines indicate what Git can expect from the OS, what +# libraries are available etc. Much of this is auto-detected in +# config.mak.uname, or in configure.ac when using the optional "make +# configure && ./configure" (see INSTALL). +# # Define SHELL_PATH to a POSIX shell if your /bin/sh is broken. # # Define SANE_TOOL_PATH to a colon-separated list of paths to prepend @@ -30,68 +42,8 @@ include shared.mak # # Define NO_OPENSSL environment variable if you do not have OpenSSL. # -# Define USE_LIBPCRE if you have and want to use libpcre. Various -# commands such as log and grep offer runtime options to use -# Perl-compatible regular expressions instead of standard or extended -# POSIX regular expressions. -# -# Only libpcre version 2 is supported. USE_LIBPCRE2 is a synonym for -# USE_LIBPCRE, support for the old USE_LIBPCRE1 has been removed. -# -# Define LIBPCREDIR=/foo/bar if your PCRE header and library files are -# in /foo/bar/include and /foo/bar/lib directories. -# # Define HAVE_ALLOCA_H if you have working alloca(3) defined in that header. # -# Define NO_CURL if you do not have libcurl installed. git-http-fetch and -# git-http-push are not built, and you cannot use http:// and https:// -# transports (neither smart nor dumb). -# -# Define CURLDIR=/foo/bar if your curl header and library files are in -# /foo/bar/include and /foo/bar/lib directories. -# -# Define CURL_CONFIG to curl's configuration program that prints information -# about the library (e.g., its version number). The default is 'curl-config'. -# -# Define CURL_LDFLAGS to specify flags that you need to link when using libcurl, -# if you do not want to rely on the libraries provided by CURL_CONFIG. The -# default value is a result of `curl-config --libs`. An example value for -# CURL_LDFLAGS is as follows: -# -# CURL_LDFLAGS=-lcurl -# -# Define NO_EXPAT if you do not have expat installed. git-http-push is -# not built, and you cannot push using http:// and https:// transports (dumb). -# -# Define EXPATDIR=/foo/bar if your expat header and library files are in -# /foo/bar/include and /foo/bar/lib directories. -# -# Define EXPAT_NEEDS_XMLPARSE_H if you have an old version of expat (e.g., -# 1.1 or 1.2) that provides xmlparse.h instead of expat.h. -# -# Define NO_GETTEXT if you don't want Git output to be translated. -# A translated Git requires GNU libintl or another gettext implementation, -# plus libintl-perl at runtime. -# -# Define USE_GETTEXT_SCHEME and set it to 'fallthrough', if you don't trust -# the installed gettext translation of the shell scripts output. -# -# Define HAVE_LIBCHARSET_H if you haven't set NO_GETTEXT and you can't -# trust the langinfo.h's nl_langinfo(CODESET) function to return the -# current character set. GNU and Solaris have a nl_langinfo(CODESET), -# FreeBSD can use either, but MinGW and some others need to use -# libcharset.h's locale_charset() instead. -# -# Define CHARSET_LIB to the library you need to link with in order to -# use locale_charset() function. On some platforms this needs to set to -# -lcharset, on others to -liconv . -# -# Define LIBC_CONTAINS_LIBINTL if your gettext implementation doesn't -# need -lintl when linking. -# -# Define NO_MSGFMT_EXTENDED_OPTIONS if your implementation of msgfmt -# doesn't support GNU extensions like --check and --statistics -# # Define HAVE_PATHS_H if you have paths.h and want to use the default PATH # it specifies. # @@ -152,39 +104,6 @@ include shared.mak # and do not want to use Apple's CommonCrypto library. This allows you # to provide your own OpenSSL library, for example from MacPorts. # -# Define BLK_SHA1 environment variable to make use of the bundled -# optimized C SHA1 routine. -# -# Define DC_SHA1 to unconditionally enable the collision-detecting sha1 -# algorithm. This is slower, but may detect attempted collision attacks. -# Takes priority over other *_SHA1 knobs. -# -# Define DC_SHA1_EXTERNAL in addition to DC_SHA1 if you want to build / link -# git with the external SHA1 collision-detect library. -# Without this option, i.e. the default behavior is to build git with its -# own built-in code (or submodule). -# -# Define DC_SHA1_SUBMODULE in addition to DC_SHA1 to use the -# sha1collisiondetection shipped as a submodule instead of the -# non-submodule copy in sha1dc/. This is an experimental option used -# by the git project to migrate to using sha1collisiondetection as a -# submodule. -# -# Define OPENSSL_SHA1 environment variable when running make to link -# with the SHA1 routine from openssl library. -# -# Define SHA1_MAX_BLOCK_SIZE to limit the amount of data that will be hashed -# in one call to the platform's SHA1_Update(). e.g. APPLE_COMMON_CRYPTO -# wants 'SHA1_MAX_BLOCK_SIZE=1024L*1024L*1024L' defined. -# -# Define BLK_SHA256 to use the built-in SHA-256 routines. -# -# Define NETTLE_SHA256 to use the SHA-256 routines in libnettle. -# -# Define GCRYPT_SHA256 to use the SHA-256 routines in libgcrypt. -# -# Define OPENSSL_SHA256 to use the SHA-256 routines in OpenSSL. -# # Define NEEDS_CRYPTO_WITH_SSL if you need -lcrypto when using -lssl (Darwin). # # Define NEEDS_SSL_WITH_CRYPTO if you need -lssl when using -lcrypto (Darwin). @@ -490,6 +409,151 @@ include shared.mak # to the "<name>" of the corresponding `compat/fsmonitor/fsm-settings-<name>.c` # that implements the `fsm_os_settings__*()` routines. # +# === Optional library: libintl === +# +# Define NO_GETTEXT if you don't want Git output to be translated. +# A translated Git requires GNU libintl or another gettext implementation, +# plus libintl-perl at runtime. +# +# Define USE_GETTEXT_SCHEME and set it to 'fallthrough', if you don't trust +# the installed gettext translation of the shell scripts output. +# +# Define HAVE_LIBCHARSET_H if you haven't set NO_GETTEXT and you can't +# trust the langinfo.h's nl_langinfo(CODESET) function to return the +# current character set. GNU and Solaris have a nl_langinfo(CODESET), +# FreeBSD can use either, but MinGW and some others need to use +# libcharset.h's locale_charset() instead. +# +# Define CHARSET_LIB to the library you need to link with in order to +# use locale_charset() function. On some platforms this needs to set to +# -lcharset, on others to -liconv . +# +# Define LIBC_CONTAINS_LIBINTL if your gettext implementation doesn't +# need -lintl when linking. +# +# Define NO_MSGFMT_EXTENDED_OPTIONS if your implementation of msgfmt +# doesn't support GNU extensions like --check and --statistics +# +# === Optional library: libexpat === +# +# Define NO_EXPAT if you do not have expat installed. git-http-push is +# not built, and you cannot push using http:// and https:// transports (dumb). +# +# Define EXPATDIR=/foo/bar if your expat header and library files are in +# /foo/bar/include and /foo/bar/lib directories. +# +# Define EXPAT_NEEDS_XMLPARSE_H if you have an old version of expat (e.g., +# 1.1 or 1.2) that provides xmlparse.h instead of expat.h. + +# === Optional library: libcurl === +# +# Define NO_CURL if you do not have libcurl installed. git-http-fetch and +# git-http-push are not built, and you cannot use http:// and https:// +# transports (neither smart nor dumb). +# +# Define CURLDIR=/foo/bar if your curl header and library files are in +# /foo/bar/include and /foo/bar/lib directories. +# +# Define CURL_CONFIG to curl's configuration program that prints information +# about the library (e.g., its version number). The default is 'curl-config'. +# +# Define CURL_LDFLAGS to specify flags that you need to link when using libcurl, +# if you do not want to rely on the libraries provided by CURL_CONFIG. The +# default value is a result of `curl-config --libs`. An example value for +# CURL_LDFLAGS is as follows: +# +# CURL_LDFLAGS=-lcurl +# +# === Optional library: libpcre2 === +# +# Define USE_LIBPCRE if you have and want to use libpcre. Various +# commands such as log and grep offer runtime options to use +# Perl-compatible regular expressions instead of standard or extended +# POSIX regular expressions. +# +# Only libpcre version 2 is supported. USE_LIBPCRE2 is a synonym for +# USE_LIBPCRE, support for the old USE_LIBPCRE1 has been removed. +# +# Define LIBPCREDIR=/foo/bar if your PCRE header and library files are +# in /foo/bar/include and /foo/bar/lib directories. +# +# == SHA-1 and SHA-256 defines == +# +# === SHA-1 backend === +# +# ==== Security ==== +# +# Due to the SHAttered (https://shattered.io) attack vector on SHA-1 +# it's strongly recommended to use the sha1collisiondetection +# counter-cryptanalysis library for SHA-1 hashing. +# +# If you know that you can trust the repository contents, or where +# potential SHA-1 attacks are otherwise mitigated the other backends +# listed in "SHA-1 implementations" are faster than +# sha1collisiondetection. +# +# ==== Default SHA-1 backend ==== +# +# If no *_SHA1 backend is picked, the first supported one listed in +# "SHA-1 implementations" will be picked. +# +# ==== Options common to all SHA-1 implementations ==== +# +# Define SHA1_MAX_BLOCK_SIZE to limit the amount of data that will be hashed +# in one call to the platform's SHA1_Update(). e.g. APPLE_COMMON_CRYPTO +# wants 'SHA1_MAX_BLOCK_SIZE=1024L*1024L*1024L' defined. +# +# ==== SHA-1 implementations ==== +# +# Define OPENSSL_SHA1 to link to the SHA-1 routines from the OpenSSL +# library. +# +# Define BLK_SHA1 to make use of optimized C SHA-1 routines bundled +# with git (in the block-sha1/ directory). +# +# Define NO_APPLE_COMMON_CRYPTO on OSX to opt-out of using the +# "APPLE_COMMON_CRYPTO" backend for SHA-1, which is currently the +# default on that OS. On macOS 01.4 (Tiger) or older, +# NO_APPLE_COMMON_CRYPTO is defined by default. +# +# If don't enable any of the *_SHA1 settings in this section, Git will +# default to its built-in sha1collisiondetection library, which is a +# collision-detecting sha1 This is slower, but may detect attempted +# collision attacks. +# +# ==== Options for the sha1collisiondetection library ==== +# +# Define DC_SHA1_EXTERNAL if you want to build / link +# git with the external SHA1 collision-detect library. +# Without this option, i.e. the default behavior is to build git with its +# own built-in code (or submodule). +# +# Define DC_SHA1_SUBMODULE to use the +# sha1collisiondetection shipped as a submodule instead of the +# non-submodule copy in sha1dc/. This is an experimental option used +# by the git project to migrate to using sha1collisiondetection as a +# submodule. +# +# === SHA-256 backend === +# +# ==== Security ==== +# +# Unlike SHA-1 the SHA-256 algorithm does not suffer from any known +# vulnerabilities, so any implementation will do. +# +# ==== SHA-256 implementations ==== +# +# Define OPENSSL_SHA256 to use the SHA-256 routines in OpenSSL. +# +# Define NETTLE_SHA256 to use the SHA-256 routines in libnettle. +# +# Define GCRYPT_SHA256 to use the SHA-256 routines in libgcrypt. +# +# If don't enable any of the *_SHA256 settings in this section, Git +# will default to its built-in sha256 implementation. +# +# == DEVELOPER defines == +# # Define DEVELOPER to enable more compiler warnings. Compiler version # and family are auto detected, but could be overridden by defining # COMPILER_FEATURES (see config.mak.dev). You can still set @@ -1826,7 +1890,6 @@ ifdef APPLE_COMMON_CRYPTO COMPAT_CFLAGS += -DCOMMON_DIGEST_FOR_OPENSSL BASIC_CFLAGS += -DSHA1_APPLE else - DC_SHA1 := YesPlease BASIC_CFLAGS += -DSHA1_DC LIB_OBJS += sha1dc_git.o ifdef DC_SHA1_EXTERNAL @@ -2989,7 +3052,6 @@ GIT-BUILD-OPTIONS: FORCE @echo NO_REGEX=\''$(subst ','\'',$(subst ','\'',$(NO_REGEX)))'\' >>$@+ @echo NO_UNIX_SOCKETS=\''$(subst ','\'',$(subst ','\'',$(NO_UNIX_SOCKETS)))'\' >>$@+ @echo PAGER_ENV=\''$(subst ','\'',$(subst ','\'',$(PAGER_ENV)))'\' >>$@+ - @echo DC_SHA1=\''$(subst ','\'',$(subst ','\'',$(DC_SHA1)))'\' >>$@+ @echo SANITIZE_LEAK=\''$(subst ','\'',$(subst ','\'',$(SANITIZE_LEAK)))'\' >>$@+ @echo SANITIZE_ADDRESS=\''$(subst ','\'',$(subst ','\'',$(SANITIZE_ADDRESS)))'\' >>$@+ @echo X=\'$(X)\' >>$@+ diff --git a/add-interactive.c b/add-interactive.c index f071b2a1b4..ecc5ae1b24 100644 --- a/add-interactive.c +++ b/add-interactive.c @@ -997,18 +997,17 @@ static int run_diff(struct add_i_state *s, const struct pathspec *ps, count = list_and_choose(s, files, opts); opts->flags = 0; if (count > 0) { - struct strvec args = STRVEC_INIT; + struct child_process cmd = CHILD_PROCESS_INIT; - strvec_pushl(&args, "git", "diff", "-p", "--cached", + strvec_pushl(&cmd.args, "git", "diff", "-p", "--cached", oid_to_hex(!is_initial ? &oid : s->r->hash_algo->empty_tree), "--", NULL); for (i = 0; i < files->items.nr; i++) if (files->selected[i]) - strvec_push(&args, + strvec_push(&cmd.args, files->items.items[i].string); - res = run_command_v_opt(args.v, 0); - strvec_clear(&args); + res = run_command(&cmd); } putchar('\n'); diff --git a/archive-tar.c b/archive-tar.c index 3e4822b684..f8fad2946e 100644 --- a/archive-tar.c +++ b/archive-tar.c @@ -498,6 +498,7 @@ static int write_tar_filter_archive(const struct archiver *ar, strvec_push(&filter.args, cmd.buf); filter.use_shell = 1; filter.in = -1; + filter.silent_exec_failure = 1; if (start_command(&filter) < 0) die_errno(_("unable to start '%s' filter"), cmd.buf); @@ -22,8 +22,6 @@ static struct oid_array skipped_revs; static struct object_id *current_bad_oid; -static const char *argv_checkout[] = {"checkout", "-q", NULL, "--", NULL}; - static const char *term_bad; static const char *term_good; @@ -729,20 +727,22 @@ static int is_expected_rev(const struct object_id *oid) enum bisect_error bisect_checkout(const struct object_id *bisect_rev, int no_checkout) { - char bisect_rev_hex[GIT_MAX_HEXSZ + 1]; struct commit *commit; struct pretty_print_context pp = {0}; struct strbuf commit_msg = STRBUF_INIT; - oid_to_hex_r(bisect_rev_hex, bisect_rev); update_ref(NULL, "BISECT_EXPECTED_REV", bisect_rev, NULL, 0, UPDATE_REFS_DIE_ON_ERR); - argv_checkout[2] = bisect_rev_hex; if (no_checkout) { update_ref(NULL, "BISECT_HEAD", bisect_rev, NULL, 0, UPDATE_REFS_DIE_ON_ERR); } else { - if (run_command_v_opt(argv_checkout, RUN_GIT_CMD)) + struct child_process cmd = CHILD_PROCESS_INIT; + + cmd.git_cmd = 1; + strvec_pushl(&cmd.args, "checkout", "-q", + oid_to_hex(bisect_rev), "--", NULL); + if (run_command(&cmd)) /* * Errors in `run_command()` itself, signaled by res < 0, * and errors in the child process, signaled by res > 0 diff --git a/builtin/add.c b/builtin/add.c index f84372964c..626c71ec6a 100644 --- a/builtin/add.c +++ b/builtin/add.c @@ -240,8 +240,8 @@ static int refresh(int verbose, const struct pathspec *pathspec) int run_add_interactive(const char *revision, const char *patch_mode, const struct pathspec *pathspec) { - int status, i; - struct strvec argv = STRVEC_INIT; + int i; + struct child_process cmd = CHILD_PROCESS_INIT; int use_builtin_add_i = git_env_bool("GIT_TEST_ADD_I_USE_BUILTIN", -1); @@ -272,19 +272,18 @@ int run_add_interactive(const char *revision, const char *patch_mode, return !!run_add_p(the_repository, mode, revision, pathspec); } - strvec_push(&argv, "add--interactive"); + strvec_push(&cmd.args, "add--interactive"); if (patch_mode) - strvec_push(&argv, patch_mode); + strvec_push(&cmd.args, patch_mode); if (revision) - strvec_push(&argv, revision); - strvec_push(&argv, "--"); + strvec_push(&cmd.args, revision); + strvec_push(&cmd.args, "--"); for (i = 0; i < pathspec->nr; i++) /* pass original pathspec, to be re-parsed */ - strvec_push(&argv, pathspec->items[i].original); + strvec_push(&cmd.args, pathspec->items[i].original); - status = run_command_v_opt(argv.v, RUN_GIT_CMD); - strvec_clear(&argv); - return status; + cmd.git_cmd = 1; + return run_command(&cmd); } int interactive_add(const char **argv, const char *prefix, int patch) diff --git a/builtin/am.c b/builtin/am.c index 39fea24833..20aea0d248 100644 --- a/builtin/am.c +++ b/builtin/am.c @@ -2187,14 +2187,12 @@ static int show_patch(struct am_state *state, enum show_patch_type sub_mode) int len; if (!is_null_oid(&state->orig_commit)) { - const char *av[4] = { "show", NULL, "--", NULL }; - char *new_oid_str; - int ret; + struct child_process cmd = CHILD_PROCESS_INIT; - av[1] = new_oid_str = xstrdup(oid_to_hex(&state->orig_commit)); - ret = run_command_v_opt(av, RUN_GIT_CMD); - free(new_oid_str); - return ret; + strvec_pushl(&cmd.args, "show", oid_to_hex(&state->orig_commit), + "--", NULL); + cmd.git_cmd = 1; + return run_command(&cmd); } switch (sub_mode) { diff --git a/builtin/bisect--helper.c b/builtin/bisect--helper.c index 28ef7ec2a4..1d2ce8a0e1 100644 --- a/builtin/bisect--helper.c +++ b/builtin/bisect--helper.c @@ -220,18 +220,17 @@ static int bisect_reset(const char *commit) } if (!ref_exists("BISECT_HEAD")) { - struct strvec argv = STRVEC_INIT; + struct child_process cmd = CHILD_PROCESS_INIT; - strvec_pushl(&argv, "checkout", branch.buf, "--", NULL); - if (run_command_v_opt(argv.v, RUN_GIT_CMD)) { + cmd.git_cmd = 1; + strvec_pushl(&cmd.args, "checkout", branch.buf, "--", NULL); + if (run_command(&cmd)) { error(_("could not check out original" " HEAD '%s'. Try 'git bisect" " reset <commit>'."), branch.buf); strbuf_release(&branch); - strvec_clear(&argv); return -1; } - strvec_clear(&argv); } strbuf_release(&branch); @@ -765,10 +764,12 @@ static enum bisect_error bisect_start(struct bisect_terms *terms, const char **a strbuf_read_file(&start_head, git_path_bisect_start(), 0); strbuf_trim(&start_head); if (!no_checkout) { - const char *argv[] = { "checkout", start_head.buf, - "--", NULL }; + struct child_process cmd = CHILD_PROCESS_INIT; - if (run_command_v_opt(argv, RUN_GIT_CMD)) { + cmd.git_cmd = 1; + strvec_pushl(&cmd.args, "checkout", start_head.buf, + "--", NULL); + if (run_command(&cmd)) { res = error(_("checking out '%s' failed." " Try 'git bisect start " "<valid-branch>'."), @@ -1098,40 +1099,38 @@ static enum bisect_error bisect_skip(struct bisect_terms *terms, const char **ar static int bisect_visualize(struct bisect_terms *terms, const char **argv, int argc) { - struct strvec args = STRVEC_INIT; - int flags = RUN_COMMAND_NO_STDIN, res = 0; + struct child_process cmd = CHILD_PROCESS_INIT; struct strbuf sb = STRBUF_INIT; if (bisect_next_check(terms, NULL) != 0) return BISECT_FAILED; + cmd.no_stdin = 1; if (!argc) { if ((getenv("DISPLAY") || getenv("SESSIONNAME") || getenv("MSYSTEM") || getenv("SECURITYSESSIONID")) && exists_in_PATH("gitk")) { - strvec_push(&args, "gitk"); + strvec_push(&cmd.args, "gitk"); } else { - strvec_push(&args, "log"); - flags |= RUN_GIT_CMD; + strvec_push(&cmd.args, "log"); + cmd.git_cmd = 1; } } else { if (argv[0][0] == '-') { - strvec_push(&args, "log"); - flags |= RUN_GIT_CMD; + strvec_push(&cmd.args, "log"); + cmd.git_cmd = 1; } else if (strcmp(argv[0], "tig") && !starts_with(argv[0], "git")) - flags |= RUN_GIT_CMD; + cmd.git_cmd = 1; - strvec_pushv(&args, argv); + strvec_pushv(&cmd.args, argv); } - strvec_pushl(&args, "--bisect", "--", NULL); + strvec_pushl(&cmd.args, "--bisect", "--", NULL); strbuf_read_file(&sb, git_path_bisect_names(), 0); - sq_dequote_to_strvec(sb.buf, &args); + sq_dequote_to_strvec(sb.buf, &cmd.args); strbuf_release(&sb); - res = run_command_v_opt(args.v, flags); - strvec_clear(&args); - return res; + return run_command(&cmd); } static int get_first_good(const char *refname UNUSED, @@ -1142,8 +1141,17 @@ static int get_first_good(const char *refname UNUSED, return 1; } -static int verify_good(const struct bisect_terms *terms, - const char **quoted_argv) +static int do_bisect_run(const char *command) +{ + struct child_process cmd = CHILD_PROCESS_INIT; + + printf(_("running %s\n"), command); + cmd.use_shell = 1; + strvec_push(&cmd.args, command); + return run_command(&cmd); +} + +static int verify_good(const struct bisect_terms *terms, const char *command) { int rc; enum bisect_error res; @@ -1163,8 +1171,7 @@ static int verify_good(const struct bisect_terms *terms, if (res != BISECT_OK) return -1; - printf(_("running %s\n"), quoted_argv[0]); - rc = run_command_v_opt(quoted_argv, RUN_USING_SHELL); + rc = do_bisect_run(command); res = bisect_checkout(¤t_rev, no_checkout); if (res != BISECT_OK) @@ -1177,7 +1184,6 @@ static int bisect_run(struct bisect_terms *terms, const char **argv, int argc) { int res = BISECT_OK; struct strbuf command = STRBUF_INIT; - struct strvec run_args = STRVEC_INIT; const char *new_state; int temporary_stdout_fd, saved_stdout; int is_first_run = 1; @@ -1192,11 +1198,8 @@ static int bisect_run(struct bisect_terms *terms, const char **argv, int argc) return BISECT_FAILED; } - strvec_push(&run_args, command.buf); - while (1) { - printf(_("running %s\n"), command.buf); - res = run_command_v_opt(run_args.v, RUN_USING_SHELL); + res = do_bisect_run(command.buf); /* * Exit code 126 and 127 can either come from the shell @@ -1206,7 +1209,7 @@ static int bisect_run(struct bisect_terms *terms, const char **argv, int argc) * missing or non-executable script. */ if (is_first_run && (res == 126 || res == 127)) { - int rc = verify_good(terms, run_args.v); + int rc = verify_good(terms, command.buf); is_first_run = 0; if (rc < 0) { error(_("unable to verify '%s' on good" @@ -1273,7 +1276,6 @@ static int bisect_run(struct bisect_terms *terms, const char **argv, int argc) } strbuf_release(&command); - strvec_clear(&run_args); return res; } diff --git a/builtin/clone.c b/builtin/clone.c index 547d6464b3..0e4348686b 100644 --- a/builtin/clone.c +++ b/builtin/clone.c @@ -653,9 +653,9 @@ static void update_head(const struct ref *our, const struct ref *remote, static int git_sparse_checkout_init(const char *repo) { - struct strvec argv = STRVEC_INIT; + struct child_process cmd = CHILD_PROCESS_INIT; int result = 0; - strvec_pushl(&argv, "-C", repo, "sparse-checkout", "set", NULL); + strvec_pushl(&cmd.args, "-C", repo, "sparse-checkout", "set", NULL); /* * We must apply the setting in the current process @@ -663,12 +663,12 @@ static int git_sparse_checkout_init(const char *repo) */ core_apply_sparse_checkout = 1; - if (run_command_v_opt(argv.v, RUN_GIT_CMD)) { + cmd.git_cmd = 1; + if (run_command(&cmd)) { error(_("failed to initialize sparse-checkout")); result = 1; } - strvec_clear(&argv); return result; } @@ -733,37 +733,38 @@ static int checkout(int submodule_progress, int filter_submodules) oid_to_hex(&oid), "1", NULL); if (!err && (option_recurse_submodules.nr > 0)) { - struct strvec args = STRVEC_INIT; - strvec_pushl(&args, "submodule", "update", "--require-init", "--recursive", NULL); + struct child_process cmd = CHILD_PROCESS_INIT; + strvec_pushl(&cmd.args, "submodule", "update", "--require-init", + "--recursive", NULL); if (option_shallow_submodules == 1) - strvec_push(&args, "--depth=1"); + strvec_push(&cmd.args, "--depth=1"); if (max_jobs != -1) - strvec_pushf(&args, "--jobs=%d", max_jobs); + strvec_pushf(&cmd.args, "--jobs=%d", max_jobs); if (submodule_progress) - strvec_push(&args, "--progress"); + strvec_push(&cmd.args, "--progress"); if (option_verbosity < 0) - strvec_push(&args, "--quiet"); + strvec_push(&cmd.args, "--quiet"); if (option_remote_submodules) { - strvec_push(&args, "--remote"); - strvec_push(&args, "--no-fetch"); + strvec_push(&cmd.args, "--remote"); + strvec_push(&cmd.args, "--no-fetch"); } if (filter_submodules && filter_options.choice) - strvec_pushf(&args, "--filter=%s", + strvec_pushf(&cmd.args, "--filter=%s", expand_list_objects_filter_spec(&filter_options)); if (option_single_branch >= 0) - strvec_push(&args, option_single_branch ? + strvec_push(&cmd.args, option_single_branch ? "--single-branch" : "--no-single-branch"); - err = run_command_v_opt(args.v, RUN_GIT_CMD); - strvec_clear(&args); + cmd.git_cmd = 1; + err = run_command(&cmd); } return err; @@ -864,11 +865,15 @@ static void write_refspec_config(const char *src_ref_prefix, static void dissociate_from_references(void) { - static const char* argv[] = { "repack", "-a", "-d", NULL }; char *alternates = git_pathdup("objects/info/alternates"); if (!access(alternates, F_OK)) { - if (run_command_v_opt(argv, RUN_GIT_CMD|RUN_COMMAND_NO_STDIN)) + struct child_process cmd = CHILD_PROCESS_INIT; + + cmd.git_cmd = 1; + cmd.no_stdin = 1; + strvec_pushl(&cmd.args, "repack", "-a", "-d", NULL); + if (run_command(&cmd)) die(_("cannot repack to clean up")); if (unlink(alternates) && errno != ENOENT) die_errno(_("cannot unlink temporary alternates file")); diff --git a/builtin/difftool.c b/builtin/difftool.c index 4b10ad1a36..d7f08c8a7f 100644 --- a/builtin/difftool.c +++ b/builtin/difftool.c @@ -44,8 +44,11 @@ static int difftool_config(const char *var, const char *value, void *cb) static int print_tool_help(void) { - const char *argv[] = { "mergetool", "--tool-help=diff", NULL }; - return run_command_v_opt(argv, RUN_GIT_CMD); + struct child_process cmd = CHILD_PROCESS_INIT; + + cmd.git_cmd = 1; + strvec_pushl(&cmd.args, "mergetool", "--tool-help=diff", NULL); + return run_command(&cmd); } static int parse_index_info(char *p, int *mode1, int *mode2, @@ -360,8 +363,8 @@ static int run_dir_diff(const char *extcmd, int symlinks, const char *prefix, struct pair_entry *entry; struct index_state wtindex; struct checkout lstate, rstate; - int flags = RUN_GIT_CMD, err = 0; - const char *helper_argv[] = { "difftool--helper", NULL, NULL, NULL }; + int err = 0; + struct child_process cmd = CHILD_PROCESS_INIT; struct hashmap wt_modified, tmp_modified; int indices_loaded = 0; @@ -563,16 +566,17 @@ static int run_dir_diff(const char *extcmd, int symlinks, const char *prefix, } strbuf_setlen(&ldir, ldir_len); - helper_argv[1] = ldir.buf; strbuf_setlen(&rdir, rdir_len); - helper_argv[2] = rdir.buf; if (extcmd) { - helper_argv[0] = extcmd; - flags = 0; - } else + strvec_push(&cmd.args, extcmd); + } else { + strvec_push(&cmd.args, "difftool--helper"); + cmd.git_cmd = 1; setenv("GIT_DIFFTOOL_DIRDIFF", "true", 1); - ret = run_command_v_opt(helper_argv, flags); + } + strvec_pushl(&cmd.args, ldir.buf, rdir.buf, NULL); + ret = run_command(&cmd); /* TODO: audit for interaction with sparse-index. */ ensure_full_index(&wtindex); diff --git a/builtin/fetch.c b/builtin/fetch.c index b06e454cbd..7378cafeec 100644 --- a/builtin/fetch.c +++ b/builtin/fetch.c @@ -1972,14 +1972,17 @@ static int fetch_multiple(struct string_list *list, int max_children) } else for (i = 0; i < list->nr; i++) { const char *name = list->items[i].string; - strvec_push(&argv, name); + struct child_process cmd = CHILD_PROCESS_INIT; + + strvec_pushv(&cmd.args, argv.v); + strvec_push(&cmd.args, name); if (verbosity >= 0) printf(_("Fetching %s\n"), name); - if (run_command_v_opt(argv.v, RUN_GIT_CMD)) { + cmd.git_cmd = 1; + if (run_command(&cmd)) { error(_("could not fetch %s"), name); result = 1; } - strvec_pop(&argv); } strvec_clear(&argv); diff --git a/builtin/gc.c b/builtin/gc.c index 24ea85c7af..6b08dcf3c5 100644 --- a/builtin/gc.c +++ b/builtin/gc.c @@ -42,7 +42,7 @@ static const char * const builtin_gc_usage[] = { static int pack_refs = 1; static int prune_reflogs = 1; -static int cruft_packs = 0; +static int cruft_packs = -1; static int aggressive_depth = 50; static int aggressive_window = 250; static int gc_auto_threshold = 6700; @@ -167,9 +167,11 @@ static void gc_config(void) struct maintenance_run_opts; static int maintenance_task_pack_refs(MAYBE_UNUSED struct maintenance_run_opts *opts) { - const char *argv[] = { "pack-refs", "--all", "--prune", NULL }; + struct child_process cmd = CHILD_PROCESS_INIT; - return run_command_v_opt(argv, RUN_GIT_CMD); + cmd.git_cmd = 1; + strvec_pushl(&cmd.args, "pack-refs", "--all", "--prune", NULL); + return run_command(&cmd); } static int too_many_loose_objects(void) @@ -535,8 +537,14 @@ static void gc_before_repack(void) if (pack_refs && maintenance_task_pack_refs(NULL)) die(FAILED_RUN, "pack-refs"); - if (prune_reflogs && run_command_v_opt(reflog.v, RUN_GIT_CMD)) - die(FAILED_RUN, reflog.v[0]); + if (prune_reflogs) { + struct child_process cmd = CHILD_PROCESS_INIT; + + cmd.git_cmd = 1; + strvec_pushv(&cmd.args, reflog.v); + if (run_command(&cmd)) + die(FAILED_RUN, reflog.v[0]); + } } int cmd_gc(int argc, const char **argv, const char *prefix) @@ -550,6 +558,7 @@ int cmd_gc(int argc, const char **argv, const char *prefix) int daemonized = 0; int keep_largest_pack = -1; timestamp_t dummy; + struct child_process rerere_cmd = CHILD_PROCESS_INIT; struct option builtin_gc_options[] = { OPT__QUIET(&quiet, N_("suppress progress reporting")), @@ -593,6 +602,10 @@ int cmd_gc(int argc, const char **argv, const char *prefix) if (prune_expire && parse_expiry_date(prune_expire, &dummy)) die(_("failed to parse prune expiry value %s"), prune_expire); + prepare_repo_settings(the_repository); + if (cruft_packs < 0) + cruft_packs = the_repository->settings.gc_cruft_packs; + if (aggressive) { strvec_push(&repack, "-f"); if (aggressive_depth > 0) @@ -671,11 +684,17 @@ int cmd_gc(int argc, const char **argv, const char *prefix) gc_before_repack(); if (!repository_format_precious_objects) { - if (run_command_v_opt(repack.v, - RUN_GIT_CMD | RUN_CLOSE_OBJECT_STORE)) + struct child_process repack_cmd = CHILD_PROCESS_INIT; + + repack_cmd.git_cmd = 1; + repack_cmd.close_object_store = 1; + strvec_pushv(&repack_cmd.args, repack.v); + if (run_command(&repack_cmd)) die(FAILED_RUN, repack.v[0]); if (prune_expire) { + struct child_process prune_cmd = CHILD_PROCESS_INIT; + /* run `git prune` even if using cruft packs */ strvec_push(&prune, prune_expire); if (quiet) @@ -683,18 +702,26 @@ int cmd_gc(int argc, const char **argv, const char *prefix) if (has_promisor_remote()) strvec_push(&prune, "--exclude-promisor-objects"); - if (run_command_v_opt(prune.v, RUN_GIT_CMD)) + prune_cmd.git_cmd = 1; + strvec_pushv(&prune_cmd.args, prune.v); + if (run_command(&prune_cmd)) die(FAILED_RUN, prune.v[0]); } } if (prune_worktrees_expire) { + struct child_process prune_worktrees_cmd = CHILD_PROCESS_INIT; + strvec_push(&prune_worktrees, prune_worktrees_expire); - if (run_command_v_opt(prune_worktrees.v, RUN_GIT_CMD)) + prune_worktrees_cmd.git_cmd = 1; + strvec_pushv(&prune_worktrees_cmd.args, prune_worktrees.v); + if (run_command(&prune_worktrees_cmd)) die(FAILED_RUN, prune_worktrees.v[0]); } - if (run_command_v_opt(rerere.v, RUN_GIT_CMD)) + rerere_cmd.git_cmd = 1; + strvec_pushv(&rerere_cmd.args, rerere.v); + if (run_command(&rerere_cmd)) die(FAILED_RUN, rerere.v[0]); report_garbage = report_pack_garbage; @@ -704,7 +731,6 @@ int cmd_gc(int argc, const char **argv, const char *prefix) clean_pack_garbage(); } - prepare_repo_settings(the_repository); if (the_repository->settings.gc_write_commit_graph == 1) write_commit_graph_reachable(the_repository->objects->odb, !quiet && !daemonized ? COMMIT_GRAPH_WRITE_PROGRESS : 0, @@ -1910,20 +1936,16 @@ static char *schtasks_task_name(const char *frequency) static int schtasks_remove_task(enum schedule_priority schedule) { const char *cmd = "schtasks"; - int result; - struct strvec args = STRVEC_INIT; + struct child_process child = CHILD_PROCESS_INIT; const char *frequency = get_frequency(schedule); char *name = schtasks_task_name(frequency); get_schedule_cmd(&cmd, NULL); - strvec_split(&args, cmd); - strvec_pushl(&args, "/delete", "/tn", name, "/f", NULL); - - result = run_command_v_opt(args.v, 0); - - strvec_clear(&args); + strvec_split(&child.args, cmd); + strvec_pushl(&child.args, "/delete", "/tn", name, "/f", NULL); free(name); - return result; + + return run_command(&child); } static int schtasks_remove_tasks(void) diff --git a/builtin/merge-index.c b/builtin/merge-index.c index c0383fe9df..012f52bd00 100644 --- a/builtin/merge-index.c +++ b/builtin/merge-index.c @@ -12,6 +12,7 @@ static int merge_entry(int pos, const char *path) const char *arguments[] = { pgm, "", "", "", path, "", "", "", NULL }; char hexbuf[4][GIT_MAX_HEXSZ + 1]; char ownbuf[4][60]; + struct child_process cmd = CHILD_PROCESS_INIT; if (pos >= active_nr) die("git merge-index: %s not in the cache", path); @@ -31,7 +32,8 @@ static int merge_entry(int pos, const char *path) if (!found) die("git merge-index: %s not in the cache", path); - if (run_command_v_opt(arguments, 0)) { + strvec_pushv(&cmd.args, arguments); + if (run_command(&cmd)) { if (one_shot) err++; else { diff --git a/builtin/merge.c b/builtin/merge.c index 5900b81729..b3f75f55c8 100644 --- a/builtin/merge.c +++ b/builtin/merge.c @@ -345,60 +345,49 @@ out: return rc; } -static void read_empty(const struct object_id *oid, int verbose) +static void read_empty(const struct object_id *oid) { - int i = 0; - const char *args[7]; - - args[i++] = "read-tree"; - if (verbose) - args[i++] = "-v"; - args[i++] = "-m"; - args[i++] = "-u"; - args[i++] = empty_tree_oid_hex(); - args[i++] = oid_to_hex(oid); - args[i] = NULL; + struct child_process cmd = CHILD_PROCESS_INIT; + + strvec_pushl(&cmd.args, "read-tree", "-m", "-u", empty_tree_oid_hex(), + oid_to_hex(oid), NULL); + cmd.git_cmd = 1; - if (run_command_v_opt(args, RUN_GIT_CMD)) + if (run_command(&cmd)) die(_("read-tree failed")); } -static void reset_hard(const struct object_id *oid, int verbose) +static void reset_hard(const struct object_id *oid) { - int i = 0; - const char *args[6]; - - args[i++] = "read-tree"; - if (verbose) - args[i++] = "-v"; - args[i++] = "--reset"; - args[i++] = "-u"; - args[i++] = oid_to_hex(oid); - args[i] = NULL; + struct child_process cmd = CHILD_PROCESS_INIT; + + strvec_pushl(&cmd.args, "read-tree", "-v", "--reset", "-u", + oid_to_hex(oid), NULL); + cmd.git_cmd = 1; - if (run_command_v_opt(args, RUN_GIT_CMD)) + if (run_command(&cmd)) die(_("read-tree failed")); } static void restore_state(const struct object_id *head, const struct object_id *stash) { - struct strvec args = STRVEC_INIT; + struct child_process cmd = CHILD_PROCESS_INIT; - reset_hard(head, 1); + reset_hard(head); if (is_null_oid(stash)) goto refresh_cache; - strvec_pushl(&args, "stash", "apply", "--index", "--quiet", NULL); - strvec_push(&args, oid_to_hex(stash)); + strvec_pushl(&cmd.args, "stash", "apply", "--index", "--quiet", NULL); + strvec_push(&cmd.args, oid_to_hex(stash)); /* * It is OK to ignore error here, for example when there was * nothing to restore. */ - run_command_v_opt(args.v, RUN_GIT_CMD); - strvec_clear(&args); + cmd.git_cmd = 1; + run_command(&cmd); refresh_cache: if (discard_cache() < 0 || read_cache() < 0) @@ -1470,7 +1459,7 @@ int cmd_merge(int argc, const char **argv, const char *prefix) check_trust_level); remote_head_oid = &remoteheads->item->object.oid; - read_empty(remote_head_oid, 0); + read_empty(remote_head_oid); update_ref("initial pull", "HEAD", remote_head_oid, NULL, 0, UPDATE_REFS_DIE_ON_ERR); goto done; diff --git a/builtin/pull.c b/builtin/pull.c index 403a24d7ca..b21edd767a 100644 --- a/builtin/pull.c +++ b/builtin/pull.c @@ -515,76 +515,75 @@ static void parse_repo_refspecs(int argc, const char **argv, const char **repo, */ static int run_fetch(const char *repo, const char **refspecs) { - struct strvec args = STRVEC_INIT; - int ret; + struct child_process cmd = CHILD_PROCESS_INIT; - strvec_pushl(&args, "fetch", "--update-head-ok", NULL); + strvec_pushl(&cmd.args, "fetch", "--update-head-ok", NULL); /* Shared options */ - argv_push_verbosity(&args); + argv_push_verbosity(&cmd.args); if (opt_progress) - strvec_push(&args, opt_progress); + strvec_push(&cmd.args, opt_progress); /* Options passed to git-fetch */ if (opt_all) - strvec_push(&args, opt_all); + strvec_push(&cmd.args, opt_all); if (opt_append) - strvec_push(&args, opt_append); + strvec_push(&cmd.args, opt_append); if (opt_upload_pack) - strvec_push(&args, opt_upload_pack); - argv_push_force(&args); + strvec_push(&cmd.args, opt_upload_pack); + argv_push_force(&cmd.args); if (opt_tags) - strvec_push(&args, opt_tags); + strvec_push(&cmd.args, opt_tags); if (opt_prune) - strvec_push(&args, opt_prune); + strvec_push(&cmd.args, opt_prune); if (recurse_submodules_cli != RECURSE_SUBMODULES_DEFAULT) switch (recurse_submodules_cli) { case RECURSE_SUBMODULES_ON: - strvec_push(&args, "--recurse-submodules=on"); + strvec_push(&cmd.args, "--recurse-submodules=on"); break; case RECURSE_SUBMODULES_OFF: - strvec_push(&args, "--recurse-submodules=no"); + strvec_push(&cmd.args, "--recurse-submodules=no"); break; case RECURSE_SUBMODULES_ON_DEMAND: - strvec_push(&args, "--recurse-submodules=on-demand"); + strvec_push(&cmd.args, "--recurse-submodules=on-demand"); break; default: BUG("submodule recursion option not understood"); } if (max_children) - strvec_push(&args, max_children); + strvec_push(&cmd.args, max_children); if (opt_dry_run) - strvec_push(&args, "--dry-run"); + strvec_push(&cmd.args, "--dry-run"); if (opt_keep) - strvec_push(&args, opt_keep); + strvec_push(&cmd.args, opt_keep); if (opt_depth) - strvec_push(&args, opt_depth); + strvec_push(&cmd.args, opt_depth); if (opt_unshallow) - strvec_push(&args, opt_unshallow); + strvec_push(&cmd.args, opt_unshallow); if (opt_update_shallow) - strvec_push(&args, opt_update_shallow); + strvec_push(&cmd.args, opt_update_shallow); if (opt_refmap) - strvec_push(&args, opt_refmap); + strvec_push(&cmd.args, opt_refmap); if (opt_ipv4) - strvec_push(&args, opt_ipv4); + strvec_push(&cmd.args, opt_ipv4); if (opt_ipv6) - strvec_push(&args, opt_ipv6); + strvec_push(&cmd.args, opt_ipv6); if (opt_show_forced_updates > 0) - strvec_push(&args, "--show-forced-updates"); + strvec_push(&cmd.args, "--show-forced-updates"); else if (opt_show_forced_updates == 0) - strvec_push(&args, "--no-show-forced-updates"); + strvec_push(&cmd.args, "--no-show-forced-updates"); if (set_upstream) - strvec_push(&args, set_upstream); - strvec_pushv(&args, opt_fetch.v); + strvec_push(&cmd.args, set_upstream); + strvec_pushv(&cmd.args, opt_fetch.v); if (repo) { - strvec_push(&args, repo); - strvec_pushv(&args, refspecs); + strvec_push(&cmd.args, repo); + strvec_pushv(&cmd.args, refspecs); } else if (*refspecs) BUG("refspecs without repo?"); - ret = run_command_v_opt(args.v, RUN_GIT_CMD | RUN_CLOSE_OBJECT_STORE); - strvec_clear(&args); - return ret; + cmd.git_cmd = 1; + cmd.close_object_store = 1; + return run_command(&cmd); } /** @@ -653,52 +652,50 @@ static int update_submodules(void) */ static int run_merge(void) { - int ret; - struct strvec args = STRVEC_INIT; + struct child_process cmd = CHILD_PROCESS_INIT; - strvec_pushl(&args, "merge", NULL); + strvec_pushl(&cmd.args, "merge", NULL); /* Shared options */ - argv_push_verbosity(&args); + argv_push_verbosity(&cmd.args); if (opt_progress) - strvec_push(&args, opt_progress); + strvec_push(&cmd.args, opt_progress); /* Options passed to git-merge */ if (opt_diffstat) - strvec_push(&args, opt_diffstat); + strvec_push(&cmd.args, opt_diffstat); if (opt_log) - strvec_push(&args, opt_log); + strvec_push(&cmd.args, opt_log); if (opt_signoff) - strvec_push(&args, opt_signoff); + strvec_push(&cmd.args, opt_signoff); if (opt_squash) - strvec_push(&args, opt_squash); + strvec_push(&cmd.args, opt_squash); if (opt_commit) - strvec_push(&args, opt_commit); + strvec_push(&cmd.args, opt_commit); if (opt_edit) - strvec_push(&args, opt_edit); + strvec_push(&cmd.args, opt_edit); if (cleanup_arg) - strvec_pushf(&args, "--cleanup=%s", cleanup_arg); + strvec_pushf(&cmd.args, "--cleanup=%s", cleanup_arg); if (opt_ff) - strvec_push(&args, opt_ff); + strvec_push(&cmd.args, opt_ff); if (opt_verify) - strvec_push(&args, opt_verify); + strvec_push(&cmd.args, opt_verify); if (opt_verify_signatures) - strvec_push(&args, opt_verify_signatures); - strvec_pushv(&args, opt_strategies.v); - strvec_pushv(&args, opt_strategy_opts.v); + strvec_push(&cmd.args, opt_verify_signatures); + strvec_pushv(&cmd.args, opt_strategies.v); + strvec_pushv(&cmd.args, opt_strategy_opts.v); if (opt_gpg_sign) - strvec_push(&args, opt_gpg_sign); + strvec_push(&cmd.args, opt_gpg_sign); if (opt_autostash == 0) - strvec_push(&args, "--no-autostash"); + strvec_push(&cmd.args, "--no-autostash"); else if (opt_autostash == 1) - strvec_push(&args, "--autostash"); + strvec_push(&cmd.args, "--autostash"); if (opt_allow_unrelated_histories > 0) - strvec_push(&args, "--allow-unrelated-histories"); + strvec_push(&cmd.args, "--allow-unrelated-histories"); - strvec_push(&args, "FETCH_HEAD"); - ret = run_command_v_opt(args.v, RUN_GIT_CMD); - strvec_clear(&args); - return ret; + strvec_push(&cmd.args, "FETCH_HEAD"); + cmd.git_cmd = 1; + return run_command(&cmd); } /** @@ -879,43 +876,41 @@ static int get_rebase_newbase_and_upstream(struct object_id *newbase, static int run_rebase(const struct object_id *newbase, const struct object_id *upstream) { - int ret; - struct strvec args = STRVEC_INIT; + struct child_process cmd = CHILD_PROCESS_INIT; - strvec_push(&args, "rebase"); + strvec_push(&cmd.args, "rebase"); /* Shared options */ - argv_push_verbosity(&args); + argv_push_verbosity(&cmd.args); /* Options passed to git-rebase */ if (opt_rebase == REBASE_MERGES) - strvec_push(&args, "--rebase-merges"); + strvec_push(&cmd.args, "--rebase-merges"); else if (opt_rebase == REBASE_INTERACTIVE) - strvec_push(&args, "--interactive"); + strvec_push(&cmd.args, "--interactive"); if (opt_diffstat) - strvec_push(&args, opt_diffstat); - strvec_pushv(&args, opt_strategies.v); - strvec_pushv(&args, opt_strategy_opts.v); + strvec_push(&cmd.args, opt_diffstat); + strvec_pushv(&cmd.args, opt_strategies.v); + strvec_pushv(&cmd.args, opt_strategy_opts.v); if (opt_gpg_sign) - strvec_push(&args, opt_gpg_sign); + strvec_push(&cmd.args, opt_gpg_sign); if (opt_signoff) - strvec_push(&args, opt_signoff); + strvec_push(&cmd.args, opt_signoff); if (opt_autostash == 0) - strvec_push(&args, "--no-autostash"); + strvec_push(&cmd.args, "--no-autostash"); else if (opt_autostash == 1) - strvec_push(&args, "--autostash"); + strvec_push(&cmd.args, "--autostash"); if (opt_verify_signatures && !strcmp(opt_verify_signatures, "--verify-signatures")) warning(_("ignoring --verify-signatures for rebase")); - strvec_push(&args, "--onto"); - strvec_push(&args, oid_to_hex(newbase)); + strvec_push(&cmd.args, "--onto"); + strvec_push(&cmd.args, oid_to_hex(newbase)); - strvec_push(&args, oid_to_hex(upstream)); + strvec_push(&cmd.args, oid_to_hex(upstream)); - ret = run_command_v_opt(args.v, RUN_GIT_CMD); - strvec_clear(&args); - return ret; + cmd.git_cmd = 1; + return run_command(&cmd); } static int get_can_ff(struct object_id *orig_head, diff --git a/builtin/remote.c b/builtin/remote.c index 93285fc06e..729f6f3643 100644 --- a/builtin/remote.c +++ b/builtin/remote.c @@ -92,13 +92,15 @@ static int verbose; static int fetch_remote(const char *name) { - const char *argv[] = { "fetch", name, NULL, NULL }; - if (verbose) { - argv[1] = "-v"; - argv[2] = name; - } + struct child_process cmd = CHILD_PROCESS_INIT; + + strvec_push(&cmd.args, "fetch"); + if (verbose) + strvec_push(&cmd.args, "-v"); + strvec_push(&cmd.args, name); + cmd.git_cmd = 1; printf_ln(_("Updating %s"), name); - if (run_command_v_opt(argv, RUN_GIT_CMD)) + if (run_command(&cmd)) return error(_("Could not fetch %s"), name); return 0; } @@ -1508,37 +1510,35 @@ static int update(int argc, const char **argv, const char *prefix) N_("prune remotes after fetching")), OPT_END() }; - struct strvec fetch_argv = STRVEC_INIT; + struct child_process cmd = CHILD_PROCESS_INIT; int default_defined = 0; - int retval; argc = parse_options(argc, argv, prefix, options, builtin_remote_update_usage, PARSE_OPT_KEEP_ARGV0); - strvec_push(&fetch_argv, "fetch"); + strvec_push(&cmd.args, "fetch"); if (prune != -1) - strvec_push(&fetch_argv, prune ? "--prune" : "--no-prune"); + strvec_push(&cmd.args, prune ? "--prune" : "--no-prune"); if (verbose) - strvec_push(&fetch_argv, "-v"); - strvec_push(&fetch_argv, "--multiple"); + strvec_push(&cmd.args, "-v"); + strvec_push(&cmd.args, "--multiple"); if (argc < 2) - strvec_push(&fetch_argv, "default"); + strvec_push(&cmd.args, "default"); for (i = 1; i < argc; i++) - strvec_push(&fetch_argv, argv[i]); + strvec_push(&cmd.args, argv[i]); - if (strcmp(fetch_argv.v[fetch_argv.nr-1], "default") == 0) { + if (strcmp(cmd.args.v[cmd.args.nr-1], "default") == 0) { git_config(get_remote_default, &default_defined); if (!default_defined) { - strvec_pop(&fetch_argv); - strvec_push(&fetch_argv, "--all"); + strvec_pop(&cmd.args); + strvec_push(&cmd.args, "--all"); } } - retval = run_command_v_opt(fetch_argv.v, RUN_GIT_CMD); - strvec_clear(&fetch_argv); - return retval; + cmd.git_cmd = 1; + return run_command(&cmd); } static int remove_all_fetch_refspecs(const char *key) diff --git a/builtin/repack.c b/builtin/repack.c index 10e23f9ee1..65eb1b8bd2 100644 --- a/builtin/repack.c +++ b/builtin/repack.c @@ -32,7 +32,6 @@ static int write_bitmaps = -1; static int use_delta_islands; static int run_update_server_info = 1; static char *packdir, *packtmp_name, *packtmp; -static char *cruft_expiration; static const char *const git_repack_usage[] = { N_("git repack [<options>]"), @@ -150,7 +149,8 @@ static void remove_redundant_pack(const char *dir_name, const char *base_name) } static void prepare_pack_objects(struct child_process *cmd, - const struct pack_objects_args *args) + const struct pack_objects_args *args, + const char *out) { strvec_push(&cmd->args, "pack-objects"); if (args->window) @@ -173,7 +173,7 @@ static void prepare_pack_objects(struct child_process *cmd, strvec_push(&cmd->args, "--quiet"); if (delta_base_offset) strvec_push(&cmd->args, "--delta-base-offset"); - strvec_push(&cmd->args, packtmp); + strvec_push(&cmd->args, out); cmd->git_cmd = 1; cmd->out = -1; } @@ -241,7 +241,7 @@ static void repack_promisor_objects(const struct pack_objects_args *args, FILE *out; struct strbuf line = STRBUF_INIT; - prepare_pack_objects(&cmd, args); + prepare_pack_objects(&cmd, args, packtmp); cmd.in = -1; /* @@ -657,7 +657,9 @@ static void remove_redundant_bitmaps(struct string_list *include, } static int write_cruft_pack(const struct pack_objects_args *args, + const char *destination, const char *pack_prefix, + const char *cruft_expiration, struct string_list *names, struct string_list *existing_packs, struct string_list *existing_kept_packs) @@ -667,8 +669,10 @@ static int write_cruft_pack(const struct pack_objects_args *args, struct string_list_item *item; FILE *in, *out; int ret; + const char *scratch; + int local = skip_prefix(destination, packdir, &scratch); - prepare_pack_objects(&cmd, args); + prepare_pack_objects(&cmd, args, destination); strvec_push(&cmd.args, "--cruft"); if (cruft_expiration) @@ -693,6 +697,10 @@ static int write_cruft_pack(const struct pack_objects_args *args, * By the time it is read here, it contains only the pack(s) * that were just written, which is exactly the set of packs we * want to consider kept. + * + * If `--expire-to` is given, the double-use served by `names` + * ensures that the pack written to `--expire-to` excludes any + * objects contained in the cruft pack. */ in = xfdopen(cmd.in, "w"); for_each_string_list_item(item, names) @@ -710,9 +718,14 @@ static int write_cruft_pack(const struct pack_objects_args *args, if (line.len != the_hash_algo->hexsz) die(_("repack: Expecting full hex object ID lines only " "from pack-objects.")); - - item = string_list_append(names, line.buf); - item->util = populate_pack_exts(line.buf); + /* + * avoid putting packs written outside of the repository in the + * list of names + */ + if (local) { + item = string_list_append(names, line.buf); + item->util = populate_pack_exts(line.buf); + } } fclose(out); @@ -744,6 +757,8 @@ int cmd_repack(int argc, const char **argv, const char *prefix) struct pack_objects_args cruft_po_args = {NULL}; int geometric_factor = 0; int write_midx = 0; + const char *cruft_expiration = NULL; + const char *expire_to = NULL; struct option builtin_repack_options[] = { OPT_BIT('a', NULL, &pack_everything, @@ -793,6 +808,8 @@ int cmd_repack(int argc, const char **argv, const char *prefix) N_("find a geometric progression with factor <N>")), OPT_BOOL('m', "write-midx", &write_midx, N_("write a multi-pack index of the resulting packs")), + OPT_STRING(0, "expire-to", &expire_to, N_("dir"), + N_("pack prefix to store a pack containing pruned objects")), OPT_END() }; @@ -858,7 +875,7 @@ int cmd_repack(int argc, const char **argv, const char *prefix) split_pack_geometry(geometry, geometric_factor); } - prepare_pack_objects(&cmd, &po_args); + prepare_pack_objects(&cmd, &po_args, packtmp); show_progress = !po_args.quiet && isatty(2); @@ -984,11 +1001,45 @@ int cmd_repack(int argc, const char **argv, const char *prefix) cruft_po_args.local = po_args.local; cruft_po_args.quiet = po_args.quiet; - ret = write_cruft_pack(&cruft_po_args, pack_prefix, &names, + ret = write_cruft_pack(&cruft_po_args, packtmp, pack_prefix, + cruft_expiration, &names, &existing_nonkept_packs, &existing_kept_packs); if (ret) return ret; + + if (delete_redundant && expire_to) { + /* + * If `--expire-to` is given with `-d`, it's possible + * that we're about to prune some objects. With cruft + * packs, pruning is implicit: any objects from existing + * packs that weren't picked up by new packs are removed + * when their packs are deleted. + * + * Generate an additional cruft pack, with one twist: + * `names` now includes the name of the cruft pack + * written in the previous step. So the contents of + * _this_ cruft pack exclude everything contained in the + * existing cruft pack (that is, all of the unreachable + * objects which are no older than + * `--cruft-expiration`). + * + * To make this work, cruft_expiration must become NULL + * so that this cruft pack doesn't actually prune any + * objects. If it were non-NULL, this call would always + * generate an empty pack (since every object not in the + * cruft pack generated above will have an mtime older + * than the expiration). + */ + ret = write_cruft_pack(&cruft_po_args, expire_to, + pack_prefix, + NULL, + &names, + &existing_nonkept_packs, + &existing_kept_packs); + if (ret) + return ret; + } } string_list_sort(&names); @@ -260,7 +260,7 @@ macos-latest) else MAKEFLAGS="$MAKEFLAGS PYTHON_PATH=$(which python2)" MAKEFLAGS="$MAKEFLAGS NO_APPLE_COMMON_CRYPTO=NoThanks" - MAKEFLAGS="$MAKEFLAGS DC_SHA1=YesPlease NO_OPENSSL=NoThanks" + MAKEFLAGS="$MAKEFLAGS NO_OPENSSL=NoThanks" fi ;; esac diff --git a/compat/mingw.c b/compat/mingw.c index 901375d584..d614f156df 100644 --- a/compat/mingw.c +++ b/compat/mingw.c @@ -196,16 +196,19 @@ static int read_yes_no_answer(void) static int ask_yes_no_if_possible(const char *format, ...) { char question[4096]; - const char *retry_hook[] = { NULL, NULL, NULL }; + const char *retry_hook; va_list args; va_start(args, format); vsnprintf(question, sizeof(question), format, args); va_end(args); - if ((retry_hook[0] = mingw_getenv("GIT_ASK_YESNO"))) { - retry_hook[1] = question; - return !run_command_v_opt(retry_hook, 0); + retry_hook = mingw_getenv("GIT_ASK_YESNO"); + if (retry_hook) { + struct child_process cmd = CHILD_PROCESS_INIT; + + strvec_pushl(&cmd.args, retry_hook, question, NULL); + return !run_command(&cmd); } if (!isatty(_fileno(stdin)) || !isatty(_fileno(stderr))) diff --git a/contrib/buildsystems/CMakeLists.txt b/contrib/buildsystems/CMakeLists.txt index 3957e4cf8c..2f6e0197ff 100644 --- a/contrib/buildsystems/CMakeLists.txt +++ b/contrib/buildsystems/CMakeLists.txt @@ -1025,7 +1025,6 @@ set(NO_PERL ) set(NO_PTHREADS ) set(NO_PYTHON ) set(PAGER_ENV "LESS=FRX LV=-c") -set(DC_SHA1 YesPlease) set(RUNTIME_PREFIX true) set(NO_GETTEXT ) @@ -1061,7 +1060,6 @@ file(APPEND ${CMAKE_BINARY_DIR}/GIT-BUILD-OPTIONS "NO_PERL='${NO_PERL}'\n") file(APPEND ${CMAKE_BINARY_DIR}/GIT-BUILD-OPTIONS "NO_PTHREADS='${NO_PTHREADS}'\n") file(APPEND ${CMAKE_BINARY_DIR}/GIT-BUILD-OPTIONS "NO_UNIX_SOCKETS='${NO_UNIX_SOCKETS}'\n") file(APPEND ${CMAKE_BINARY_DIR}/GIT-BUILD-OPTIONS "PAGER_ENV='${PAGER_ENV}'\n") -file(APPEND ${CMAKE_BINARY_DIR}/GIT-BUILD-OPTIONS "DC_SHA1='${DC_SHA1}'\n") file(APPEND ${CMAKE_BINARY_DIR}/GIT-BUILD-OPTIONS "X='${EXE_EXTENSION}'\n") file(APPEND ${CMAKE_BINARY_DIR}/GIT-BUILD-OPTIONS "NO_GETTEXT='${NO_GETTEXT}'\n") file(APPEND ${CMAKE_BINARY_DIR}/GIT-BUILD-OPTIONS "RUNTIME_PREFIX='${RUNTIME_PREFIX}'\n") @@ -4301,35 +4301,34 @@ static void run_external_diff(const char *pgm, const char *xfrm_msg, struct diff_options *o) { - struct strvec argv = STRVEC_INIT; - struct strvec env = STRVEC_INIT; + struct child_process cmd = CHILD_PROCESS_INIT; struct diff_queue_struct *q = &diff_queued_diff; - strvec_push(&argv, pgm); - strvec_push(&argv, name); + strvec_push(&cmd.args, pgm); + strvec_push(&cmd.args, name); if (one && two) { - add_external_diff_name(o->repo, &argv, name, one); + add_external_diff_name(o->repo, &cmd.args, name, one); if (!other) - add_external_diff_name(o->repo, &argv, name, two); + add_external_diff_name(o->repo, &cmd.args, name, two); else { - add_external_diff_name(o->repo, &argv, other, two); - strvec_push(&argv, other); - strvec_push(&argv, xfrm_msg); + add_external_diff_name(o->repo, &cmd.args, other, two); + strvec_push(&cmd.args, other); + strvec_push(&cmd.args, xfrm_msg); } } - strvec_pushf(&env, "GIT_DIFF_PATH_COUNTER=%d", ++o->diff_path_counter); - strvec_pushf(&env, "GIT_DIFF_PATH_TOTAL=%d", q->nr); + strvec_pushf(&cmd.env, "GIT_DIFF_PATH_COUNTER=%d", + ++o->diff_path_counter); + strvec_pushf(&cmd.env, "GIT_DIFF_PATH_TOTAL=%d", q->nr); diff_free_filespec_data(one); diff_free_filespec_data(two); - if (run_command_v_opt_cd_env(argv.v, RUN_USING_SHELL, NULL, env.v)) + cmd.use_shell = 1; + if (run_command(&cmd)) die(_("external diff died, stopping at %s"), name); remove_tempfile(); - strvec_clear(&argv); - strvec_clear(&env); } static int similarity_index(struct diff_filepair *p) diff --git a/fsmonitor-ipc.c b/fsmonitor-ipc.c index c0f42301c8..19d772f0f3 100644 --- a/fsmonitor-ipc.c +++ b/fsmonitor-ipc.c @@ -54,10 +54,14 @@ enum ipc_active_state fsmonitor_ipc__get_state(void) static int spawn_daemon(void) { - const char *args[] = { "fsmonitor--daemon", "start", NULL }; + struct child_process cmd = CHILD_PROCESS_INIT; - return run_command_v_opt_tr2(args, RUN_COMMAND_NO_STDIN | RUN_GIT_CMD, - "fsmonitor"); + cmd.git_cmd = 1; + cmd.no_stdin = 1; + cmd.trace2_child_class = "fsmonitor"; + strvec_pushl(&cmd.args, "fsmonitor--daemon", "start", NULL); + + return run_command(&cmd); } int fsmonitor_ipc__send_query(const char *since_token, @@ -787,7 +787,7 @@ static int run_argv(int *argcp, const char ***argv) if (!done_alias) handle_builtin(*argcp, *argv); else if (get_builtin(**argv)) { - struct strvec args = STRVEC_INIT; + struct child_process cmd = CHILD_PROCESS_INIT; int i; /* @@ -804,18 +804,21 @@ static int run_argv(int *argcp, const char ***argv) commit_pager_choice(); - strvec_push(&args, "git"); + strvec_push(&cmd.args, "git"); for (i = 0; i < *argcp; i++) - strvec_push(&args, (*argv)[i]); + strvec_push(&cmd.args, (*argv)[i]); - trace_argv_printf(args.v, "trace: exec:"); + trace_argv_printf(cmd.args.v, "trace: exec:"); /* * if we fail because the command is not found, it is * OK to return. Otherwise, we just pass along the status code. */ - i = run_command_v_opt_tr2(args.v, RUN_SILENT_EXEC_FAILURE | - RUN_CLEAN_ON_EXIT | RUN_WAIT_AFTER_CLEAN, "git_alias"); + cmd.silent_exec_failure = 1; + cmd.clean_on_exit = 1; + cmd.wait_after_clean = 1; + cmd.trace2_child_class = "git_alias"; + i = run_command(&cmd); if (i >= 0 || errno != ENOENT) exit(i); die("could not execute builtin %s", **argv); diff --git a/ll-merge.c b/ll-merge.c index a8e2db9336..22a603e8af 100644 --- a/ll-merge.c +++ b/ll-merge.c @@ -193,7 +193,7 @@ static enum ll_merge_result ll_ext_merge(const struct ll_merge_driver *fn, struct strbuf cmd = STRBUF_INIT; struct strbuf_expand_dict_entry dict[6]; struct strbuf path_sq = STRBUF_INIT; - const char *args[] = { NULL, NULL }; + struct child_process child = CHILD_PROCESS_INIT; int status, fd, i; struct stat st; enum ll_merge_result ret; @@ -219,8 +219,9 @@ static enum ll_merge_result ll_ext_merge(const struct ll_merge_driver *fn, strbuf_expand(&cmd, fn->cmdline, strbuf_expand_dict_cb, &dict); - args[0] = cmd.buf; - status = run_command_v_opt(args, RUN_USING_SHELL); + child.use_shell = 1; + strvec_push(&child.args, cmd.buf); + status = run_command(&child); fd = open(temp[1], O_RDONLY); if (fd < 0) goto bad; @@ -19,22 +19,22 @@ int try_merge_command(struct repository *r, const char **xopts, struct commit_list *common, const char *head_arg, struct commit_list *remotes) { - struct strvec args = STRVEC_INIT; + struct child_process cmd = CHILD_PROCESS_INIT; int i, ret; struct commit_list *j; - strvec_pushf(&args, "merge-%s", strategy); + strvec_pushf(&cmd.args, "merge-%s", strategy); for (i = 0; i < xopts_nr; i++) - strvec_pushf(&args, "--%s", xopts[i]); + strvec_pushf(&cmd.args, "--%s", xopts[i]); for (j = common; j; j = j->next) - strvec_push(&args, merge_argument(j->item)); - strvec_push(&args, "--"); - strvec_push(&args, head_arg); + strvec_push(&cmd.args, merge_argument(j->item)); + strvec_push(&cmd.args, "--"); + strvec_push(&cmd.args, head_arg); for (j = remotes; j; j = j->next) - strvec_push(&args, merge_argument(j->item)); + strvec_push(&cmd.args, merge_argument(j->item)); - ret = run_command_v_opt(args.v, RUN_GIT_CMD); - strvec_clear(&args); + cmd.git_cmd = 1; + ret = run_command(&cmd); discard_index(r->index); if (repo_read_index(r) < 0) @@ -901,7 +901,13 @@ int adjust_shared_perm(const char *path) if (S_ISDIR(old_mode)) { /* Copy read bits to execute bits */ new_mode |= (new_mode & 0444) >> 2; - new_mode |= FORCE_DIR_SET_GID; + + /* + * g+s matters only if any extra access is granted + * based on group membership. + */ + if (FORCE_DIR_SET_GID && (new_mode & 060)) + new_mode |= FORCE_DIR_SET_GID; } if (((old_mode ^ new_mode) & ~S_IFMT) && diff --git a/ref-filter.c b/ref-filter.c index 914908fac5..9dc2cd1451 100644 --- a/ref-filter.c +++ b/ref-filter.c @@ -1375,12 +1375,12 @@ static void find_subpos(const char *buf, /* subject is first non-empty line */ *sub = buf; /* subject goes to first empty line before signature begins */ - if ((eol = strstr(*sub, "\n\n"))) { + if ((eol = strstr(*sub, "\n\n")) || + (eol = strstr(*sub, "\r\n\r\n"))) { eol = eol < sigstart ? eol : sigstart; - /* check if message uses CRLF */ - } else if (! (eol = strstr(*sub, "\r\n\r\n"))) { + } else { /* treat whole message as subject */ - eol = strrchr(*sub, '\0'); + eol = sigstart; } buf = eol; *sublen = buf - *sub; diff --git a/repo-settings.c b/repo-settings.c index e8b58151bc..3021921c53 100644 --- a/repo-settings.c +++ b/repo-settings.c @@ -43,6 +43,7 @@ void prepare_repo_settings(struct repository *r) /* Defaults modified by feature.* */ if (experimental) { r->settings.fetch_negotiation_algorithm = FETCH_NEGOTIATION_SKIPPING; + r->settings.gc_cruft_packs = 1; } if (manyfiles) { r->settings.index_version = 4; diff --git a/repository.h b/repository.h index 24316ac944..6c461c5b9d 100644 --- a/repository.h +++ b/repository.h @@ -34,6 +34,7 @@ struct repo_settings { int commit_graph_generation_version; int commit_graph_read_changed_paths; int gc_write_commit_graph; + int gc_cruft_packs; int fetch_write_commit_graph; int command_requires_full_index; int sparse_index; diff --git a/revision.c b/revision.c index 0760e78936..c6b3996583 100644 --- a/revision.c +++ b/revision.c @@ -1865,30 +1865,15 @@ void repo_init_revisions(struct repository *r, struct rev_info *revs, const char *prefix) { - memset(revs, 0, sizeof(*revs)); + struct rev_info blank = REV_INFO_INIT; + memcpy(revs, &blank, sizeof(*revs)); revs->repo = r; - revs->abbrev = DEFAULT_ABBREV; - revs->simplify_history = 1; revs->pruning.repo = r; - revs->pruning.flags.recursive = 1; - revs->pruning.flags.quick = 1; revs->pruning.add_remove = file_add_remove; revs->pruning.change = file_change; revs->pruning.change_fn_data = revs; - revs->sort_order = REV_SORT_IN_GRAPH_ORDER; - revs->dense = 1; revs->prefix = prefix; - revs->max_age = -1; - revs->max_age_as_filter = -1; - revs->min_age = -1; - revs->skip_count = -1; - revs->max_count = -1; - revs->max_parents = -1; - revs->expand_tabs_in_log = -1; - - revs->commit_format = CMIT_FMT_DEFAULT; - revs->expand_tabs_in_log_default = 8; grep_init(&revs->grep_filter, revs->repo); revs->grep_filter.status_only = 1; diff --git a/revision.h b/revision.h index afe1b77985..8493a3f3b9 100644 --- a/revision.h +++ b/revision.h @@ -357,7 +357,23 @@ struct rev_info { * called before release_revisions() the "struct rev_info" can be left * uninitialized. */ -#define REV_INFO_INIT { 0 } +#define REV_INFO_INIT { \ + .abbrev = DEFAULT_ABBREV, \ + .simplify_history = 1, \ + .pruning.flags.recursive = 1, \ + .pruning.flags.quick = 1, \ + .sort_order = REV_SORT_IN_GRAPH_ORDER, \ + .dense = 1, \ + .max_age = -1, \ + .max_age_as_filter = -1, \ + .min_age = -1, \ + .skip_count = -1, \ + .max_count = -1, \ + .max_parents = -1, \ + .expand_tabs_in_log = -1, \ + .commit_format = CMIT_FMT_DEFAULT, \ + .expand_tabs_in_log_default = 8, \ +} /** * Initialize a rev_info structure with default values. The third parameter may diff --git a/run-command.c b/run-command.c index c772acd743..48b9ba6d6f 100644 --- a/run-command.c +++ b/run-command.c @@ -1004,41 +1004,6 @@ int run_command(struct child_process *cmd) return finish_command(cmd); } -int run_command_v_opt(const char **argv, int opt) -{ - return run_command_v_opt_cd_env(argv, opt, NULL, NULL); -} - -int run_command_v_opt_tr2(const char **argv, int opt, const char *tr2_class) -{ - return run_command_v_opt_cd_env_tr2(argv, opt, NULL, NULL, tr2_class); -} - -int run_command_v_opt_cd_env(const char **argv, int opt, const char *dir, const char *const *env) -{ - return run_command_v_opt_cd_env_tr2(argv, opt, dir, env, NULL); -} - -int run_command_v_opt_cd_env_tr2(const char **argv, int opt, const char *dir, - const char *const *env, const char *tr2_class) -{ - struct child_process cmd = CHILD_PROCESS_INIT; - strvec_pushv(&cmd.args, argv); - cmd.no_stdin = opt & RUN_COMMAND_NO_STDIN ? 1 : 0; - cmd.git_cmd = opt & RUN_GIT_CMD ? 1 : 0; - cmd.stdout_to_stderr = opt & RUN_COMMAND_STDOUT_TO_STDERR ? 1 : 0; - cmd.silent_exec_failure = opt & RUN_SILENT_EXEC_FAILURE ? 1 : 0; - cmd.use_shell = opt & RUN_USING_SHELL ? 1 : 0; - cmd.clean_on_exit = opt & RUN_CLEAN_ON_EXIT ? 1 : 0; - cmd.wait_after_clean = opt & RUN_WAIT_AFTER_CLEAN ? 1 : 0; - cmd.close_object_store = opt & RUN_CLOSE_OBJECT_STORE ? 1 : 0; - cmd.dir = dir; - if (env) - strvec_pushv(&cmd.env, (const char **)env); - cmd.trace2_child_class = tr2_class; - return run_command(&cmd); -} - #ifndef NO_PTHREADS static pthread_t main_thread; static int main_thread_set; diff --git a/run-command.h b/run-command.h index e3e1ea01ad..072db56a4d 100644 --- a/run-command.h +++ b/run-command.h @@ -150,9 +150,7 @@ struct child_process { } /** - * The functions: child_process_init, start_command, finish_command, - * run_command, run_command_v_opt, run_command_v_opt_cd_env, child_process_clear - * do the following: + * The functions: start_command, finish_command, run_command do the following: * * - If a system call failed, errno is set and -1 is returned. A diagnostic * is printed. @@ -224,36 +222,6 @@ int run_command(struct child_process *); */ int run_auto_maintenance(int quiet); -#define RUN_COMMAND_NO_STDIN (1<<0) -#define RUN_GIT_CMD (1<<1) -#define RUN_COMMAND_STDOUT_TO_STDERR (1<<2) -#define RUN_SILENT_EXEC_FAILURE (1<<3) -#define RUN_USING_SHELL (1<<4) -#define RUN_CLEAN_ON_EXIT (1<<5) -#define RUN_WAIT_AFTER_CLEAN (1<<6) -#define RUN_CLOSE_OBJECT_STORE (1<<7) - -/** - * Convenience functions that encapsulate a sequence of - * start_command() followed by finish_command(). The argument argv - * specifies the program and its arguments. The argument opt is zero - * or more of the flags `RUN_COMMAND_NO_STDIN`, `RUN_GIT_CMD`, - * `RUN_COMMAND_STDOUT_TO_STDERR`, or `RUN_SILENT_EXEC_FAILURE` - * that correspond to the members .no_stdin, .git_cmd, - * .stdout_to_stderr, .silent_exec_failure of `struct child_process`. - * The argument dir corresponds the member .dir. The argument env - * corresponds to the member .env. - */ -int run_command_v_opt(const char **argv, int opt); -int run_command_v_opt_tr2(const char **argv, int opt, const char *tr2_class); -/* - * env (the environment) is to be formatted like environ: "VAR=VALUE". - * To unset an environment variable use just "VAR". - */ -int run_command_v_opt_cd_env(const char **argv, int opt, const char *dir, const char *const *env); -int run_command_v_opt_cd_env_tr2(const char **argv, int opt, const char *dir, - const char *const *env, const char *tr2_class); - /** * Execute the given command, sending "in" to its stdin, and capturing its * stdout and stderr in the "out" and "err" strbufs. Any of the three may @@ -69,21 +69,18 @@ static void setup_enlistment_directory(int argc, const char **argv, static int run_git(const char *arg, ...) { - struct strvec argv = STRVEC_INIT; + struct child_process cmd = CHILD_PROCESS_INIT; va_list args; const char *p; - int res; va_start(args, arg); - strvec_push(&argv, arg); + strvec_push(&cmd.args, arg); while ((p = va_arg(args, const char *))) - strvec_push(&argv, p); + strvec_push(&cmd.args, p); va_end(args); - res = run_command_v_opt(argv.v, RUN_GIT_CMD); - - strvec_clear(&argv); - return res; + cmd.git_cmd = 1; + return run_command(&cmd); } struct scalar_config { diff --git a/sequencer.c b/sequencer.c index 798a970296..edb01643e1 100644 --- a/sequencer.c +++ b/sequencer.c @@ -3183,18 +3183,15 @@ static int rollback_is_safe(void) static int reset_merge(const struct object_id *oid) { - int ret; - struct strvec argv = STRVEC_INIT; + struct child_process cmd = CHILD_PROCESS_INIT; - strvec_pushl(&argv, "reset", "--merge", NULL); + cmd.git_cmd = 1; + strvec_pushl(&cmd.args, "reset", "--merge", NULL); if (!is_null_oid(oid)) - strvec_push(&argv, oid_to_hex(oid)); - - ret = run_command_v_opt(argv.v, RUN_GIT_CMD); - strvec_clear(&argv); + strvec_push(&cmd.args, oid_to_hex(oid)); - return ret; + return run_command(&cmd); } static int rollback_single_pick(struct repository *r) @@ -3558,12 +3555,13 @@ static int error_failed_squash(struct repository *r, static int do_exec(struct repository *r, const char *command_line) { - const char *child_argv[] = { NULL, NULL }; + struct child_process cmd = CHILD_PROCESS_INIT; int dirty, status; fprintf(stderr, _("Executing: %s\n"), command_line); - child_argv[0] = command_line; - status = run_command_v_opt(child_argv, RUN_USING_SHELL); + cmd.use_shell = 1; + strvec_push(&cmd.args, command_line); + status = run_command(&cmd); /* force re-reading of the cache */ if (discard_index(r->index) < 0 || repo_read_index(r) < 0) @@ -4870,14 +4868,14 @@ cleanup_head_ref: static int continue_single_pick(struct repository *r, struct replay_opts *opts) { - struct strvec argv = STRVEC_INIT; - int ret; + struct child_process cmd = CHILD_PROCESS_INIT; if (!refs_ref_exists(get_main_ref_store(r), "CHERRY_PICK_HEAD") && !refs_ref_exists(get_main_ref_store(r), "REVERT_HEAD")) return error(_("no cherry-pick or revert in progress")); - strvec_push(&argv, "commit"); + cmd.git_cmd = 1; + strvec_push(&cmd.args, "commit"); /* * continue_single_pick() handles the case of recovering from a @@ -4890,11 +4888,9 @@ static int continue_single_pick(struct repository *r, struct replay_opts *opts) * Include --cleanup=strip as well because we don't want the * "# Conflicts:" messages. */ - strvec_pushl(&argv, "--no-edit", "--cleanup=strip", NULL); + strvec_pushl(&cmd.args, "--no-edit", "--cleanup=strip", NULL); - ret = run_command_v_opt(argv.v, RUN_GIT_CMD); - strvec_clear(&argv); - return ret; + return run_command(&cmd); } static int commit_staged_changes(struct repository *r, diff --git a/sha1dc_git.h b/sha1dc_git.h index 41e1c3fd3f..60e3ce8439 100644 --- a/sha1dc_git.h +++ b/sha1dc_git.h @@ -17,6 +17,7 @@ void git_SHA1DCInit(SHA1_CTX *); void git_SHA1DCFinal(unsigned char [20], SHA1_CTX *); void git_SHA1DCUpdate(SHA1_CTX *ctx, const void *data, unsigned long len); +#define platform_SHA_IS_SHA1DC /* used by "test-tool sha1-is-sha1dc" */ #define platform_SHA_CTX SHA1_CTX #define platform_SHA1_Init git_SHA1DCInit #define platform_SHA1_Update git_SHA1DCUpdate @@ -52,21 +52,24 @@ static void cd_to_homedir(void) static void run_shell(void) { int done = 0; - static const char *help_argv[] = { HELP_COMMAND, NULL }; + struct child_process help_cmd = CHILD_PROCESS_INIT; if (!access(NOLOGIN_COMMAND, F_OK)) { /* Interactive login disabled. */ - const char *argv[] = { NOLOGIN_COMMAND, NULL }; + struct child_process nologin_cmd = CHILD_PROCESS_INIT; int status; - status = run_command_v_opt(argv, 0); + strvec_push(&nologin_cmd.args, NOLOGIN_COMMAND); + status = run_command(&nologin_cmd); if (status < 0) exit(127); exit(status); } /* Print help if enabled */ - run_command_v_opt(help_argv, RUN_SILENT_EXEC_FAILURE); + help_cmd.silent_exec_failure = 1; + strvec_push(&help_cmd.args, HELP_COMMAND); + run_command(&help_cmd); do { const char *prog; @@ -125,9 +128,13 @@ static void run_shell(void) !strcmp(prog, "exit") || !strcmp(prog, "bye")) { done = 1; } else if (is_valid_cmd_name(prog)) { + struct child_process cmd = CHILD_PROCESS_INIT; + full_cmd = make_cmd(prog); argv[0] = full_cmd; - code = run_command_v_opt(argv, RUN_SILENT_EXEC_FAILURE); + cmd.silent_exec_failure = 1; + strvec_pushv(&cmd.args, argv); + code = run_command(&cmd); if (code == -1 && errno == ENOENT) { fprintf(stderr, "unrecognized command '%s'\n", prog); } diff --git a/sparse-index.c b/sparse-index.c index e4a54ce194..8c269dab80 100644 --- a/sparse-index.c +++ b/sparse-index.c @@ -493,24 +493,42 @@ void clear_skip_worktree_from_present_files(struct index_state *istate) int dir_found = 1; int i; + int path_count[2] = {0, 0}; + int restarted = 0; if (!core_apply_sparse_checkout || sparse_expect_files_outside_of_patterns) return; + trace2_region_enter("index", "clear_skip_worktree_from_present_files", + istate->repo); restart: for (i = 0; i < istate->cache_nr; i++) { struct cache_entry *ce = istate->cache[i]; - if (ce_skip_worktree(ce) && - path_found(ce->name, &last_dirname, &dir_len, &dir_found)) { - if (S_ISSPARSEDIR(ce->ce_mode)) { - ensure_full_index(istate); - goto restart; + if (ce_skip_worktree(ce)) { + path_count[restarted]++; + if (path_found(ce->name, &last_dirname, &dir_len, &dir_found)) { + if (S_ISSPARSEDIR(ce->ce_mode)) { + if (restarted) + BUG("ensure-full-index did not fully flatten?"); + ensure_full_index(istate); + restarted = 1; + goto restart; + } + ce->ce_flags &= ~CE_SKIP_WORKTREE; } - ce->ce_flags &= ~CE_SKIP_WORKTREE; } } + + if (path_count[0]) + trace2_data_intmax("index", istate->repo, + "sparse_path_count", path_count[0]); + if (restarted) + trace2_data_intmax("index", istate->repo, + "sparse_path_count_full", path_count[1]); + trace2_region_leave("index", "clear_skip_worktree_from_present_files", + istate->repo); } /* diff --git a/t/helper/test-fake-ssh.c b/t/helper/test-fake-ssh.c index 12beee99ad..2e576bcc11 100644 --- a/t/helper/test-fake-ssh.c +++ b/t/helper/test-fake-ssh.c @@ -8,7 +8,7 @@ int cmd_main(int argc, const char **argv) struct strbuf buf = STRBUF_INIT; FILE *f; int i; - const char *child_argv[] = { NULL, NULL }; + struct child_process cmd = CHILD_PROCESS_INIT; /* First, print all parameters into $TRASH_DIRECTORY/ssh-output */ if (!trash_directory) @@ -25,6 +25,7 @@ int cmd_main(int argc, const char **argv) /* Now, evaluate the *last* parameter */ if (argc < 2) return 0; - child_argv[0] = argv[argc - 1]; - return run_command_v_opt(child_argv, RUN_USING_SHELL); + cmd.use_shell = 1; + strvec_push(&cmd.args, argv[argc - 1]); + return run_command(&cmd); } diff --git a/t/helper/test-sha1.c b/t/helper/test-sha1.c index d860c387c3..71fe5c6145 100644 --- a/t/helper/test-sha1.c +++ b/t/helper/test-sha1.c @@ -5,3 +5,11 @@ int cmd__sha1(int ac, const char **av) { return cmd_hash_impl(ac, av, GIT_HASH_SHA1); } + +int cmd__sha1_is_sha1dc(int argc UNUSED, const char **argv UNUSED) +{ +#ifdef platform_SHA_IS_SHA1DC + return 0; +#endif + return 1; +} diff --git a/t/helper/test-tool.c b/t/helper/test-tool.c index 01cda9358d..775854c9f9 100644 --- a/t/helper/test-tool.c +++ b/t/helper/test-tool.c @@ -73,6 +73,7 @@ static struct test_cmd cmds[] = { { "scrap-cache-tree", cmd__scrap_cache_tree }, { "serve-v2", cmd__serve_v2 }, { "sha1", cmd__sha1 }, + { "sha1-is-sha1dc", cmd__sha1_is_sha1dc }, { "sha256", cmd__sha256 }, { "sigchain", cmd__sigchain }, { "simple-ipc", cmd__simple_ipc }, diff --git a/t/helper/test-tool.h b/t/helper/test-tool.h index ca2948066f..7cf84eca12 100644 --- a/t/helper/test-tool.h +++ b/t/helper/test-tool.h @@ -66,6 +66,7 @@ int cmd__run_command(int argc, const char **argv); int cmd__scrap_cache_tree(int argc, const char **argv); int cmd__serve_v2(int argc, const char **argv); int cmd__sha1(int argc, const char **argv); +int cmd__sha1_is_sha1dc(int argc, const char **argv); int cmd__oid_array(int argc, const char **argv); int cmd__sha256(int argc, const char **argv); int cmd__sigchain(int argc, const char **argv); diff --git a/t/helper/test-trace2.c b/t/helper/test-trace2.c index 1b092c6071..f374c21ec3 100644 --- a/t/helper/test-trace2.c +++ b/t/helper/test-trace2.c @@ -132,6 +132,7 @@ static int ut_003error(int argc, const char **argv) */ static int ut_004child(int argc, const char **argv) { + struct child_process cmd = CHILD_PROCESS_INIT; int result; /* @@ -141,7 +142,8 @@ static int ut_004child(int argc, const char **argv) if (!argc) return 0; - result = run_command_v_opt(argv, 0); + strvec_pushv(&cmd.args, argv); + result = run_command(&cmd); exit(result); } diff --git a/t/t0013-sha1dc.sh b/t/t0013-sha1dc.sh index 9ad76080aa..5324047689 100755 --- a/t/t0013-sha1dc.sh +++ b/t/t0013-sha1dc.sh @@ -6,9 +6,11 @@ TEST_PASSES_SANITIZE_LEAK=true . ./test-lib.sh TEST_DATA="$TEST_DIRECTORY/t0013" -if test -z "$DC_SHA1" +test_lazy_prereq SHA1_IS_SHA1DC 'test-tool sha1-is-sha1dc' + +if ! test_have_prereq SHA1_IS_SHA1DC then - skip_all='skipping sha1 collision tests, DC_SHA1 not set' + skip_all='skipping sha1 collision tests, not using sha1collisiondetection' test_done fi diff --git a/t/t0061-run-command.sh b/t/t0061-run-command.sh index 7b5423eebd..e2411f6a9b 100755 --- a/t/t0061-run-command.sh +++ b/t/t0061-run-command.sh @@ -130,7 +130,8 @@ World EOF test_expect_success 'run_command runs in parallel with more jobs available than tasks' ' - test-tool run-command run-command-parallel 5 sh -c "printf \"%s\n%s\n\" Hello World" 2>actual && + test-tool run-command run-command-parallel 5 sh -c "printf \"%s\n%s\n\" Hello World" >out 2>actual && + test_must_be_empty out && test_cmp expect actual ' @@ -141,7 +142,8 @@ test_expect_success 'run_command runs ungrouped in parallel with more jobs avail ' test_expect_success 'run_command runs in parallel with as many jobs as tasks' ' - test-tool run-command run-command-parallel 4 sh -c "printf \"%s\n%s\n\" Hello World" 2>actual && + test-tool run-command run-command-parallel 4 sh -c "printf \"%s\n%s\n\" Hello World" >out 2>actual && + test_must_be_empty out && test_cmp expect actual ' @@ -152,7 +154,8 @@ test_expect_success 'run_command runs ungrouped in parallel with as many jobs as ' test_expect_success 'run_command runs in parallel with more tasks than jobs available' ' - test-tool run-command run-command-parallel 3 sh -c "printf \"%s\n%s\n\" Hello World" 2>actual && + test-tool run-command run-command-parallel 3 sh -c "printf \"%s\n%s\n\" Hello World" >out 2>actual && + test_must_be_empty out && test_cmp expect actual ' @@ -172,7 +175,8 @@ asking for a quick stop EOF test_expect_success 'run_command is asked to abort gracefully' ' - test-tool run-command run-command-abort 3 false 2>actual && + test-tool run-command run-command-abort 3 false >out 2>actual && + test_must_be_empty out && test_cmp expect actual ' @@ -187,7 +191,8 @@ no further jobs available EOF test_expect_success 'run_command outputs ' ' - test-tool run-command run-command-no-jobs 3 sh -c "printf \"%s\n%s\n\" Hello World" 2>actual && + test-tool run-command run-command-no-jobs 3 sh -c "printf \"%s\n%s\n\" Hello World" >out 2>actual && + test_must_be_empty out && test_cmp expect actual ' diff --git a/t/t1800-hook.sh b/t/t1800-hook.sh index 43fcb7c0bf..2ef3579fa7 100755 --- a/t/t1800-hook.sh +++ b/t/t1800-hook.sh @@ -95,7 +95,7 @@ test_expect_success 'git hook run -- out-of-repo runs excluded' ' test_expect_success 'git -c core.hooksPath=<PATH> hook run' ' mkdir my-hooks && write_script my-hooks/test-hook <<-\EOF && - echo Hook ran $1 >>actual + echo Hook ran $1 EOF cat >expect <<-\EOF && diff --git a/t/t5000-tar-tree.sh b/t/t5000-tar-tree.sh index eaa0b22ece..d473048138 100755 --- a/t/t5000-tar-tree.sh +++ b/t/t5000-tar-tree.sh @@ -342,6 +342,13 @@ test_expect_success 'only enabled filters are available remotely' ' test_cmp_bin remote.bar config.bar ' +test_expect_success 'invalid filter is reported only once' ' + test_must_fail git -c tar.invalid.command= archive --format=invalid \ + HEAD >out 2>err && + test_must_be_empty out && + test_line_count = 1 err +' + test_expect_success 'git archive --format=tgz' ' git archive --format=tgz HEAD >j.tgz ' diff --git a/t/t5526-fetch-submodules.sh b/t/t5526-fetch-submodules.sh index 75da8acf8f..b9546ef8e5 100755 --- a/t/t5526-fetch-submodules.sh +++ b/t/t5526-fetch-submodules.sh @@ -178,6 +178,7 @@ test_expect_success "submodule.recurse option triggers recursive fetch" ' ' test_expect_success "fetch --recurse-submodules -j2 has the same output behaviour" ' + test_when_finished "rm -f trace.out" && add_submodule_commits && ( cd downstream && @@ -705,15 +706,22 @@ test_expect_success "'fetch.recurseSubmodules=on-demand' works also without .git test_expect_success 'fetching submodules respects parallel settings' ' git config fetch.recurseSubmodules true && + test_when_finished "rm -f downstream/trace.out" && ( cd downstream && GIT_TRACE=$(pwd)/trace.out git fetch && grep "1 tasks" trace.out && + >trace.out && + GIT_TRACE=$(pwd)/trace.out git fetch --jobs 7 && grep "7 tasks" trace.out && + >trace.out && + git config submodule.fetchJobs 8 && GIT_TRACE=$(pwd)/trace.out git fetch && grep "8 tasks" trace.out && + >trace.out && + GIT_TRACE=$(pwd)/trace.out git fetch --jobs 9 && grep "9 tasks" trace.out && >trace.out && diff --git a/t/t6300-for-each-ref.sh b/t/t6300-for-each-ref.sh index dcaab7265f..fa38b87441 100755 --- a/t/t6300-for-each-ref.sh +++ b/t/t6300-for-each-ref.sh @@ -1406,4 +1406,44 @@ test_expect_success 'for-each-ref reports broken tags' ' refs/tags/broken-tag-* ' +test_expect_success 'set up tag with signature and no blank lines' ' + git tag -F - fake-sig-no-blanks <<-\EOF + this is the subject + -----BEGIN PGP SIGNATURE----- + not a real signature, but we just care about the + subject/body parsing. It is important here that + there are no blank lines in the signature. + -----END PGP SIGNATURE----- + EOF +' + +test_atom refs/tags/fake-sig-no-blanks contents:subject 'this is the subject' +test_atom refs/tags/fake-sig-no-blanks contents:body '' +test_atom refs/tags/fake-sig-no-blanks contents:signature "$sig" + +test_expect_success 'set up tag with CRLF signature' ' + append_cr <<-\EOF | + this is the subject + -----BEGIN PGP SIGNATURE----- + + not a real signature, but we just care about + the subject/body parsing. It is important here + that there is a blank line separating this + from the signature header. + -----END PGP SIGNATURE----- + EOF + git tag -F - --cleanup=verbatim fake-sig-crlf +' + +test_atom refs/tags/fake-sig-crlf contents:subject 'this is the subject' +test_atom refs/tags/fake-sig-crlf contents:body '' + +# CRLF is retained in the signature, so we have to pass our expected value +# through append_cr. But test_atom requires a shell string, which means command +# substitution, and the shell will strip trailing newlines from the output of +# the substitution. Hack around it by adding and then removing a dummy line. +sig_crlf="$(printf "%s" "$sig" | append_cr; echo dummy)" +sig_crlf=${sig_crlf%dummy} +test_atom refs/tags/fake-sig-crlf contents:signature "$sig_crlf" + test_done diff --git a/t/t6500-gc.sh b/t/t6500-gc.sh index cd6c53360d..d9acb63951 100755 --- a/t/t6500-gc.sh +++ b/t/t6500-gc.sh @@ -202,6 +202,102 @@ test_expect_success 'one of gc.reflogExpire{Unreachable,}=never does not skip "e grep -E "^trace: (built-in|exec|run_command): git reflog expire --" trace.out ' +prepare_cruft_history () { + test_commit base && + + test_commit --no-tag foo && + test_commit --no-tag bar && + git reset HEAD^^ +} + +assert_cruft_packs () { + find .git/objects/pack -name "*.mtimes" >mtimes && + sed -e 's/\.mtimes$/\.pack/g' mtimes >packs && + + test_file_not_empty packs && + while read pack + do + test_path_is_file "$pack" || return 1 + done <packs +} + +assert_no_cruft_packs () { + find .git/objects/pack -name "*.mtimes" >mtimes && + test_must_be_empty mtimes +} + +test_expect_success 'gc --cruft generates a cruft pack' ' + test_when_finished "rm -fr crufts" && + git init crufts && + ( + cd crufts && + + prepare_cruft_history && + git gc --cruft && + assert_cruft_packs + ) +' + +test_expect_success 'gc.cruftPacks=true generates a cruft pack' ' + test_when_finished "rm -fr crufts" && + git init crufts && + ( + cd crufts && + + prepare_cruft_history && + git -c gc.cruftPacks=true gc && + assert_cruft_packs + ) +' + +test_expect_success 'feature.experimental=true generates a cruft pack' ' + git init crufts && + test_when_finished "rm -fr crufts" && + ( + cd crufts && + + prepare_cruft_history && + git -c feature.experimental=true gc && + assert_cruft_packs + ) +' + +test_expect_success 'feature.experimental=false allows explicit cruft packs' ' + git init crufts && + test_when_finished "rm -fr crufts" && + ( + cd crufts && + + prepare_cruft_history && + git -c gc.cruftPacks=true -c feature.experimental=false gc && + assert_cruft_packs + ) +' + +test_expect_success 'feature.experimental=true can be overridden' ' + git init crufts && + test_when_finished "rm -fr crufts" && + ( + cd crufts && + + prepare_cruft_history && + git -c feature.expiremental=true -c gc.cruftPacks=false gc && + assert_no_cruft_packs + ) +' + +test_expect_success 'feature.experimental=false avoids cruft packs by default' ' + git init crufts && + test_when_finished "rm -fr crufts" && + ( + cd crufts && + + prepare_cruft_history && + git -c feature.experimental=false gc && + assert_no_cruft_packs + ) +' + run_and_wait_for_auto_gc () { # We read stdout from gc for the side effect of waiting until the # background gc process exits, closing its fd 9. Furthermore, the diff --git a/t/t7001-mv.sh b/t/t7001-mv.sh index 8c37bceb33..d72cef8826 100755 --- a/t/t7001-mv.sh +++ b/t/t7001-mv.sh @@ -60,8 +60,8 @@ test_expect_success 'checking the commit' ' test_expect_success 'mv --dry-run does not move file' ' git mv -n path0/COPYING MOVED && - test -f path0/COPYING && - test ! -f MOVED + test_path_is_file path0/COPYING && + test_path_is_missing MOVED ' test_expect_success 'checking -k on non-existing file' ' @@ -71,25 +71,25 @@ test_expect_success 'checking -k on non-existing file' ' test_expect_success 'checking -k on untracked file' ' >untracked1 && git mv -k untracked1 path0 && - test -f untracked1 && - test ! -f path0/untracked1 + test_path_is_file untracked1 && + test_path_is_missing path0/untracked1 ' test_expect_success 'checking -k on multiple untracked files' ' >untracked2 && git mv -k untracked1 untracked2 path0 && - test -f untracked1 && - test -f untracked2 && - test ! -f path0/untracked1 && - test ! -f path0/untracked2 + test_path_is_file untracked1 && + test_path_is_file untracked2 && + test_path_is_missing path0/untracked1 && + test_path_is_missing path0/untracked2 ' test_expect_success 'checking -f on untracked file with existing target' ' >path0/untracked1 && test_must_fail git mv -f untracked1 path0 && - test ! -f .git/index.lock && - test -f untracked1 && - test -f path0/untracked1 + test_path_is_missing .git/index.lock && + test_path_is_file untracked1 && + test_path_is_file path0/untracked1 ' # clean up the mess in case bad things happen @@ -215,8 +215,8 @@ test_expect_success 'absolute pathname' ' git add sub/file && git mv sub "$(pwd)/in" && - ! test -d sub && - test -d in && + test_path_is_missing sub && + test_path_is_dir in && git ls-files --error-unmatch in/file ) ' @@ -234,8 +234,8 @@ test_expect_success 'absolute pathname outside should fail' ' git add sub/file && test_must_fail git mv sub "$out/out" && - test -d sub && - ! test -d ../in && + test_path_is_dir sub && + test_path_is_missing ../in && git ls-files --error-unmatch sub/file ) ' @@ -295,8 +295,8 @@ test_expect_success 'git mv should overwrite symlink to a file' ' git add moved && test_must_fail git mv moved symlink && git mv -f moved symlink && - ! test -e moved && - test -f symlink && + test_path_is_missing moved && + test_path_is_file symlink && test "$(cat symlink)" = 1 && git update-index --refresh && git diff-files --quiet @@ -312,13 +312,13 @@ test_expect_success 'git mv should overwrite file with a symlink' ' git add moved && test_must_fail git mv symlink moved && git mv -f symlink moved && - ! test -e symlink && + test_path_is_missing symlink && git update-index --refresh && git diff-files --quiet ' test_expect_success SYMLINKS 'check moved symlink' ' - test -h moved + test_path_is_symlink moved ' rm -f moved symlink @@ -352,7 +352,7 @@ test_expect_success 'git mv moves a submodule with a .git directory and no .gitm ) && mkdir mod && git mv sub mod/sub && - ! test -e sub && + test_path_is_missing sub && test "$entry" = "$(git ls-files --stage mod/sub | cut -f 1)" && git -C mod/sub status && git update-index --refresh && @@ -372,7 +372,7 @@ test_expect_success 'git mv moves a submodule with a .git directory and .gitmodu ) && mkdir mod && git mv sub mod/sub && - ! test -e sub && + test_path_is_missing sub && test "$entry" = "$(git ls-files --stage mod/sub | cut -f 1)" && git -C mod/sub status && echo mod/sub >expected && @@ -389,7 +389,7 @@ test_expect_success 'git mv moves a submodule with gitfile' ' entry="$(git ls-files --stage sub | cut -f 1)" && mkdir mod && git -C mod mv ../sub/ . && - ! test -e sub && + test_path_is_missing sub && test "$entry" = "$(git ls-files --stage mod/sub | cut -f 1)" && git -C mod/sub status && echo mod/sub >expected && @@ -408,7 +408,7 @@ test_expect_success 'mv does not complain when no .gitmodules file is found' ' mkdir mod && git mv sub mod/sub 2>actual.err && test_must_be_empty actual.err && - ! test -e sub && + test_path_is_missing sub && test "$entry" = "$(git ls-files --stage mod/sub | cut -f 1)" && git -C mod/sub status && git update-index --refresh && @@ -423,13 +423,13 @@ test_expect_success 'mv will error out on a modified .gitmodules file unless sta entry="$(git ls-files --stage sub | cut -f 1)" && mkdir mod && test_must_fail git mv sub mod/sub 2>actual.err && - test -s actual.err && - test -e sub && + test_file_not_empty actual.err && + test_path_exists sub && git diff-files --quiet -- sub && git add .gitmodules && git mv sub mod/sub 2>actual.err && test_must_be_empty actual.err && - ! test -e sub && + test_path_is_missing sub && test "$entry" = "$(git ls-files --stage mod/sub | cut -f 1)" && git -C mod/sub status && git update-index --refresh && @@ -447,7 +447,7 @@ test_expect_success 'mv issues a warning when section is not found in .gitmodule mkdir mod && git mv sub mod/sub 2>actual.err && test_cmp expect.err actual.err && - ! test -e sub && + test_path_is_missing sub && test "$entry" = "$(git ls-files --stage mod/sub | cut -f 1)" && git -C mod/sub status && git update-index --refresh && @@ -460,7 +460,7 @@ test_expect_success 'mv --dry-run does not touch the submodule or .gitmodules' ' git submodule update && mkdir mod && git mv -n sub mod/sub 2>actual.err && - test -f sub/.git && + test_path_is_file sub/.git && git diff-index --exit-code HEAD && git update-index --refresh && git diff-files --quiet -- sub .gitmodules @@ -474,10 +474,10 @@ test_expect_success 'checking out a commit before submodule moved needs manual u git status -s sub2 >actual && echo "?? sub2/" >expected && test_cmp expected actual && - ! test -f sub/.git && - test -f sub2/.git && + test_path_is_missing sub/.git && + test_path_is_file sub2/.git && git submodule update && - test -f sub/.git && + test_path_is_file sub/.git && rm -rf sub2 && git diff-index --exit-code HEAD && git update-index --refresh && diff --git a/t/t7700-repack.sh b/t/t7700-repack.sh index 5be483bf88..c630e0d52d 100755 --- a/t/t7700-repack.sh +++ b/t/t7700-repack.sh @@ -543,4 +543,125 @@ test_expect_success '-n overrides repack.updateServerInfo=true' ' test_server_info_missing ' +test_expect_success '--expire-to stores pruned objects (now)' ' + git init expire-to-now && + ( + cd expire-to-now && + + git branch -M main && + + test_commit base && + + git checkout -b cruft && + test_commit --no-tag cruft && + + git rev-list --objects --no-object-names main..cruft >moved.raw && + sort moved.raw >moved.want && + + git rev-list --all --objects --no-object-names >expect.raw && + sort expect.raw >expect && + + git checkout main && + git branch -D cruft && + git reflog expire --all --expire=all && + + git init --bare expired.git && + git repack -d \ + --cruft --cruft-expiration="now" \ + --expire-to="expired.git/objects/pack/pack" && + + expired="$(ls expired.git/objects/pack/pack-*.idx)" && + test_path_is_file "${expired%.idx}.mtimes" && + + # Since the `--cruft-expiration` is "now", the effective + # behavior is to move _all_ unreachable objects out to + # the location in `--expire-to`. + git show-index <$expired >expired.raw && + cut -d" " -f2 expired.raw | sort >expired.objects && + git rev-list --all --objects --no-object-names \ + >remaining.objects && + + # ...in other words, the combined contents of this + # repository and expired.git should be the same as the + # set of objects we started with. + cat expired.objects remaining.objects | sort >actual && + test_cmp expect actual && + + # The "moved" objects (i.e., those in expired.git) + # should be the same as the cruft objects which were + # expired in the previous step. + test_cmp moved.want expired.objects + ) +' + +test_expect_success '--expire-to stores pruned objects (5.minutes.ago)' ' + git init expire-to-5.minutes.ago && + ( + cd expire-to-5.minutes.ago && + + git branch -M main && + + test_commit base && + + # Create two classes of unreachable objects, one which + # is older than 5 minutes (stale), and another which is + # newer (recent). + for kind in stale recent + do + git checkout -b $kind main && + test_commit --no-tag $kind || return 1 + done && + + git rev-list --objects --no-object-names main..stale >in && + stale="$(git pack-objects $objdir/pack/pack <in)" && + mtime="$(test-tool chmtime --get =-600 $objdir/pack/pack-$stale.pack)" && + + # expect holds the set of objects we expect to find in + # this repository after repacking + git rev-list --objects --no-object-names recent >expect.raw && + sort expect.raw >expect && + + # moved.want holds the set of objects we expect to find + # in expired.git + git rev-list --objects --no-object-names main..stale >out && + sort out >moved.want && + + git checkout main && + git branch -D stale recent && + git reflog expire --all --expire=all && + git prune-packed && + + git init --bare expired.git && + git repack -d \ + --cruft --cruft-expiration=5.minutes.ago \ + --expire-to="expired.git/objects/pack/pack" && + + # Some of the remaining objects in this repository are + # unreachable, so use `cat-file --batch-all-objects` + # instead of `rev-list` to get their names + git cat-file --batch-all-objects --batch-check="%(objectname)" \ + >remaining.objects && + sort remaining.objects >actual && + test_cmp expect actual && + + ( + cd expired.git && + + expired="$(ls objects/pack/pack-*.mtimes)" && + test-tool pack-mtimes $(basename $expired) >out && + cut -d" " -f1 out | sort >../moved.got && + + # Ensure that there are as many objects with the + # expected mtime as were moved to expired.git. + # + # In other words, ensure that the recorded + # mtimes of any moved objects was written + # correctly. + grep " $mtime$" out >matching && + test_line_count = $(wc -l <../moved.want) matching + ) && + test_cmp moved.want moved.got + ) +' + test_done diff --git a/t/test-lib-functions.sh b/t/test-lib-functions.sh index 29d914a12b..796093a7b3 100644 --- a/t/test-lib-functions.sh +++ b/t/test-lib-functions.sh @@ -921,10 +921,6 @@ test_path_is_missing () { then echo "Path exists:" ls -ld "$1" - if test $# -ge 1 - then - echo "$*" - fi false fi } diff --git a/tmp-objdir.h b/tmp-objdir.h index 76efc7edee..237d96b660 100644 --- a/tmp-objdir.h +++ b/tmp-objdir.h @@ -10,9 +10,11 @@ * * Example: * + * struct child_process child = CHILD_PROCESS_INIT; * struct tmp_objdir *t = tmp_objdir_create("incoming"); - * if (!run_command_v_opt_cd_env(cmd, 0, NULL, tmp_objdir_env(t)) && - * !tmp_objdir_migrate(t)) + * strvec_push(&child.args, cmd); + * strvec_pushv(&child.env, tmp_objdir_env(t)); + * if (!run_command(&child)) && !tmp_objdir_migrate(t)) * printf("success!\n"); * else * die("failed...tmp_objdir will clean up for us"); |
