Git 2.35.3
Signed-off-by: Junio C Hamano <gitster@pobox.com>
diff --git a/.github/workflows/check-whitespace.yml b/.github/workflows/check-whitespace.yml
index 8c4358d..ad3466a 100644
--- a/.github/workflows/check-whitespace.yml
+++ b/.github/workflows/check-whitespace.yml
@@ -1,8 +1,9 @@
name: check-whitespace
-# Get the repo with the commits(+1) in the series.
+# Get the repository with all commits to ensure that we can analyze
+# all of the commits contributed via the Pull Request.
# Process `git log --check` output to extract just the check errors.
-# Add a comment to the pull request with the check errors.
+# Exit with failure upon white-space issues.
on:
pull_request:
diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml
index 6ed6a9e..c35200d 100644
--- a/.github/workflows/main.yml
+++ b/.github/workflows/main.yml
@@ -1,4 +1,4 @@
-name: CI/PR
+name: CI
on: [push, pull_request]
@@ -7,6 +7,7 @@
jobs:
ci-config:
+ name: config
runs-on: ubuntu-latest
outputs:
enabled: ${{ steps.check-ref.outputs.enabled }}${{ steps.skip-if-redundant.outputs.enabled }}
@@ -77,6 +78,7 @@
}
windows-build:
+ name: win build
needs: ci-config
if: needs.ci-config.outputs.enabled == 'yes'
runs-on: windows-latest
@@ -97,6 +99,7 @@
name: windows-artifacts
path: artifacts
windows-test:
+ name: win test
runs-on: windows-latest
needs: [windows-build]
strategy:
@@ -127,6 +130,7 @@
name: failed-tests-windows
path: ${{env.FAILED_TEST_ARTIFACTS}}
vs-build:
+ name: win+VS build
needs: ci-config
if: needs.ci-config.outputs.enabled == 'yes'
env:
@@ -178,6 +182,7 @@
name: vs-artifacts
path: artifacts
vs-test:
+ name: win+VS test
runs-on: windows-latest
needs: vs-build
strategy:
@@ -210,6 +215,7 @@
name: failed-tests-windows
path: ${{env.FAILED_TEST_ARTIFACTS}}
regular:
+ name: ${{matrix.vector.jobname}} (${{matrix.vector.pool}})
needs: ci-config
if: needs.ci-config.outputs.enabled == 'yes'
strategy:
@@ -219,14 +225,25 @@
- jobname: linux-clang
cc: clang
pool: ubuntu-latest
+ - jobname: linux-sha256
+ cc: clang
+ os: ubuntu
+ pool: ubuntu-latest
- jobname: linux-gcc
cc: gcc
+ cc_package: gcc-8
+ pool: ubuntu-latest
+ - jobname: linux-TEST-vars
+ cc: gcc
+ os: ubuntu
+ cc_package: gcc-8
pool: ubuntu-latest
- jobname: osx-clang
cc: clang
pool: macos-latest
- jobname: osx-gcc
cc: gcc
+ cc_package: gcc-9
pool: macos-latest
- jobname: linux-gcc-default
cc: gcc
@@ -236,7 +253,9 @@
pool: ubuntu-latest
env:
CC: ${{matrix.vector.cc}}
+ CC_PACKAGE: ${{matrix.vector.cc_package}}
jobname: ${{matrix.vector.jobname}}
+ runs_on_pool: ${{matrix.vector.pool}}
runs-on: ${{matrix.vector.pool}}
steps:
- uses: actions/checkout@v2
@@ -251,6 +270,7 @@
name: failed-tests-${{matrix.vector.jobname}}
path: ${{env.FAILED_TEST_ARTIFACTS}}
dockerized:
+ name: ${{matrix.vector.jobname}} (${{matrix.vector.image}})
needs: ci-config
if: needs.ci-config.outputs.enabled == 'yes'
strategy:
@@ -259,7 +279,8 @@
vector:
- jobname: linux-musl
image: alpine
- - jobname: Linux32
+ - jobname: linux32
+ os: ubuntu32
image: daald/ubuntu32:xenial
- jobname: pedantic
image: fedora
@@ -289,6 +310,7 @@
- uses: actions/checkout@v2
- run: ci/install-dependencies.sh
- run: ci/run-static-analysis.sh
+ - run: ci/check-directional-formatting.bash
sparse:
needs: ci-config
if: needs.ci-config.outputs.enabled == 'yes'
@@ -310,6 +332,7 @@
run: ci/install-dependencies.sh
- run: make sparse
documentation:
+ name: documentation
needs: ci-config
if: needs.ci-config.outputs.enabled == 'yes'
env:
diff --git a/.travis.yml b/.travis.yml
deleted file mode 100644
index 908330a..0000000
--- a/.travis.yml
+++ /dev/null
@@ -1,60 +0,0 @@
-language: c
-
-cache:
- directories:
- - $HOME/travis-cache
-
-os:
- - linux
- - osx
-
-osx_image: xcode10.1
-
-compiler:
- - clang
- - gcc
-
-matrix:
- include:
- - env: jobname=linux-gcc-default
- os: linux
- compiler:
- addons:
- before_install:
- - env: jobname=linux-gcc-4.8
- os: linux
- dist: trusty
- compiler:
- - env: jobname=Linux32
- os: linux
- compiler:
- addons:
- services:
- - docker
- before_install:
- script: ci/run-docker.sh
- - env: jobname=linux-musl
- os: linux
- compiler:
- addons:
- services:
- - docker
- before_install:
- script: ci/run-docker.sh
- - env: jobname=StaticAnalysis
- os: linux
- compiler:
- script: ci/run-static-analysis.sh
- after_failure:
- - env: jobname=Documentation
- os: linux
- compiler:
- script: ci/test-documentation.sh
- after_failure:
-
-before_install: ci/install-dependencies.sh
-script: ci/run-build-and-tests.sh
-after_failure: ci/print-test-failures.sh
-
-notifications:
- email: false
diff --git a/Documentation/CodingGuidelines b/Documentation/CodingGuidelines
index 711cb91..0e27b53 100644
--- a/Documentation/CodingGuidelines
+++ b/Documentation/CodingGuidelines
@@ -499,6 +499,33 @@
- Where required libraries do not restrict us to Python 2, we try to
also be compatible with Python 3.1 and later.
+
+Program Output
+
+ We make a distinction between a Git command's primary output and
+ output which is merely chatty feedback (for instance, status
+ messages, running transcript, or progress display), as well as error
+ messages. Roughly speaking, a Git command's primary output is that
+ which one might want to capture to a file or send down a pipe; its
+ chatty output should not interfere with these use-cases.
+
+ As such, primary output should be sent to the standard output stream
+ (stdout), and chatty output should be sent to the standard error
+ stream (stderr). Examples of commands which produce primary output
+ include `git log`, `git show`, and `git branch --list` which generate
+ output on the stdout stream.
+
+ Not all Git commands have primary output; this is often true of
+ commands whose main function is to perform an action. Some action
+ commands are silent, whereas others are chatty. An example of a
+ chatty action commands is `git clone` with its "Cloning into
+ '<path>'..." and "Checking connectivity..." status messages which it
+ sends to the stderr stream.
+
+ Error messages from Git commands should always be sent to the stderr
+ stream.
+
+
Error Messages
- Do not end error messages with a full stop.
diff --git a/Documentation/MyFirstContribution.txt b/Documentation/MyFirstContribution.txt
index b20bc8e..63a2ef5 100644
--- a/Documentation/MyFirstContribution.txt
+++ b/Documentation/MyFirstContribution.txt
@@ -905,19 +905,34 @@
themselves, you'll need to prepare the patches. Luckily, this is pretty simple:
----
-$ git format-patch --cover-letter -o psuh/ master..psuh
+$ git format-patch --cover-letter -o psuh/ --base=auto psuh@{u}..psuh
----
-The `--cover-letter` parameter tells `format-patch` to create a cover letter
-template for you. You will need to fill in the template before you're ready
-to send - but for now, the template will be next to your other patches.
+ . The `--cover-letter` option tells `format-patch` to create a
+ cover letter template for you. You will need to fill in the
+ template before you're ready to send - but for now, the template
+ will be next to your other patches.
-The `-o psuh/` parameter tells `format-patch` to place the patch files into a
-directory. This is useful because `git send-email` can take a directory and
-send out all the patches from there.
+ . The `-o psuh/` option tells `format-patch` to place the patch
+ files into a directory. This is useful because `git send-email`
+ can take a directory and send out all the patches from there.
-`master..psuh` tells `format-patch` to generate patches for the difference
-between `master` and `psuh`. It will make one patch file per commit. After you
+ . The `--base=auto` option tells the command to record the "base
+ commit", on which the recipient is expected to apply the patch
+ series. The `auto` value will cause `format-patch` to compute
+ the base commit automatically, which is the merge base of tip
+ commit of the remote-tracking branch and the specified revision
+ range.
+
+ . The `psuh@{u}..psuh` option tells `format-patch` to generate
+ patches for the commits you created on the `psuh` branch since it
+ forked from its upstream (which is `origin/master` if you
+ followed the example in the "Set up your workspace" section). If
+ you are already on the `psuh` branch, you can just say `@{u}`,
+ which means "commits on the current branch since it forked from
+ its upstream", which is the same thing.
+
+The command will make one patch file per commit. After you
run, you can go have a look at each of the patches with your favorite text
editor and make sure everything looks alright; however, it's not recommended to
make code fixups via the patch file. It's a better idea to make the change the
diff --git a/Documentation/MyFirstObjectWalk.txt b/Documentation/MyFirstObjectWalk.txt
index 45eb84d..ca26794 100644
--- a/Documentation/MyFirstObjectWalk.txt
+++ b/Documentation/MyFirstObjectWalk.txt
@@ -58,14 +58,19 @@
Add usage text and `-h` handling, like all subcommands should consistently do
(our test suite will notice and complain if you fail to do so).
+We'll need to include the `parse-options.h` header.
----
+#include "parse-options.h"
+
+...
+
int cmd_walken(int argc, const char **argv, const char *prefix)
{
const char * const walken_usage[] = {
N_("git walken"),
NULL,
- }
+ };
struct option options[] = {
OPT_END()
};
@@ -195,9 +200,14 @@
ourselves; however, we should call `git_default_config()` if we aren't calling
any other existing config callbacks.
-Add a new function to `builtin/walken.c`:
+Add a new function to `builtin/walken.c`.
+We'll also need to include the `config.h` header:
----
+#include "config.h"
+
+...
+
static int git_walken_config(const char *var, const char *value, void *cb)
{
/*
@@ -229,8 +239,14 @@
to target, as well as the `prefix` argument of `cmd_walken` and your `rev_info`
struct.
-Add the `struct rev_info` and the `repo_init_revisions()` call:
+Add the `struct rev_info` and the `repo_init_revisions()` call.
+We'll also need to include the `revision.h` header:
+
----
+#include "revision.h"
+
+...
+
int cmd_walken(int argc, const char **argv, const char *prefix)
{
/* This can go wherever you like in your declarations.*/
@@ -624,9 +640,14 @@
----
Let's start by calling just the unfiltered walk and reporting our counts.
-Complete your implementation of `walken_object_walk()`:
+Complete your implementation of `walken_object_walk()`.
+We'll also need to include the `list-objects.h` header.
----
+#include "list-objects.h"
+
+...
+
traverse_commit_list(rev, walken_show_commit, walken_show_object, NULL);
printf("commits %d\nblobs %d\ntags %d\ntrees %d\n", commit_count,
@@ -697,7 +718,7 @@
----
static void walken_object_walk(struct rev_info *rev)
{
- struct list_objects_filter_options filter_options = {};
+ struct list_objects_filter_options filter_options = { 0 };
...
----
diff --git a/Documentation/RelNotes/2.35.0.txt b/Documentation/RelNotes/2.35.0.txt
new file mode 100644
index 0000000..d69b50d
--- /dev/null
+++ b/Documentation/RelNotes/2.35.0.txt
@@ -0,0 +1,412 @@
+Git 2.35 Release Notes
+======================
+
+Updates since Git 2.34
+----------------------
+
+Backward compatibility warts
+
+ * "_" is now treated as any other URL-valid characters in an URL when
+ matching the per-URL configuration variable names.
+
+ * The color palette used by "git grep" has been updated to match that
+ of GNU grep.
+
+
+Note to those who build from the source
+
+ * You may need to define NO_UNCOMPRESS2 Makefile macro if you build
+ with zlib older than 1.2.9.
+
+ * If your compiler cannot grok C99, the build will fail. See the
+ instruction at the beginning of git-compat-util.h if this happens
+ to you.
+
+
+UI, Workflows & Features
+
+ * "git status --porcelain=v2" now show the number of stash entries
+ with --show-stash like the normal output does.
+
+ * "git stash" learned the "--staged" option to stash away what has
+ been added to the index (and nothing else).
+
+ * "git var GIT_DEFAULT_BRANCH" is a way to see what name is used for
+ the newly created branch if "git init" is run.
+
+ * Various operating modes of "git reset" have been made to work
+ better with the sparse index.
+
+ * "git submodule deinit" for a submodule whose .git metadata
+ directory is embedded in its working tree refused to work, until
+ the submodule gets converted to use the "absorbed" form where the
+ metadata directory is stored in superproject, and a gitfile at the
+ top-level of the working tree of the submodule points at it. The
+ command is taught to convert such submodules to the absorbed form
+ as needed.
+
+ * The completion script (in contrib/) learns that the "--date"
+ option of commands from the "git log" family takes "human" and
+ "auto" as valid values.
+
+ * "Zealous diff3" style of merge conflict presentation has been added.
+
+ * The "git log --format=%(describe)" placeholder has been extended to
+ allow passing selected command-line options to the underlying "git
+ describe" command.
+
+ * "default" and "reset" have been added to our color palette.
+
+ * The cryptographic signing using ssh keys can specify literal keys
+ for keytypes whose name do not begin with the "ssh-" prefix by
+ using the "key::" prefix mechanism (e.g. "key::ecdsa-sha2-nistp256").
+
+ * "git fetch" without the "--update-head-ok" option ought to protect
+ a checked out branch from getting updated, to prevent the working
+ tree that checks it out to go out of sync. The code was written
+ before the use of "git worktree" got widespread, and only checked
+ the branch that was checked out in the current worktree, which has
+ been updated.
+
+ * "git name-rev" has been tweaked to give output that is shorter and
+ easier to understand.
+
+ * "git apply" has been taught to ignore a message without a patch
+ with the "--allow-empty" option. It also learned to honor the
+ "--quiet" option given from the command line.
+
+ * The "init" and "set" subcommands in "git sparse-checkout" have been
+ unified for a better user experience and performance.
+
+ * Many git commands that deal with working tree files try to remove a
+ directory that becomes empty (i.e. "git switch" from a branch that
+ has the directory to another branch that does not would attempt
+ remove all files in the directory and the directory itself). This
+ drops users into an unfamiliar situation if the command was run in
+ a subdirectory that becomes subject to removal due to the command.
+ The commands have been taught to keep an empty directory if it is
+ the directory they were started in to avoid surprising users.
+
+ * "git am" learns "--empty=(stop|drop|keep)" option to tweak what is
+ done to a piece of e-mail without a patch in it.
+
+ * The default merge message prepared by "git merge" records the name
+ of the current branch; the name can be overridden with a new option
+ to allow users to pretend a merge is made on a different branch.
+
+ * The way "git p4" shows file sizes in its output has been updated to
+ use human-readable units.
+
+ * "git -c branch.autosetupmerge=inherit branch new old" makes "new"
+ to have the same upstream as the "old" branch, instead of marking
+ "old" itself as its upstream.
+
+
+Performance, Internal Implementation, Development Support etc.
+
+ * The use of errno as a means to carry the nature of error in the ref
+ API implementation has been reworked and reduced.
+
+ * Teach and encourage first-time contributors to this project to
+ state the base commit when they submit their topic.
+
+ * The command line completion for "git send-email" options have been
+ tweaked to make it easier to keep it in sync with the command itself.
+
+ * Ensure that the sparseness of the in-core index matches the
+ index.sparse configuration specified by the repository immediately
+ after the on-disk index file is read.
+
+ * Code clean-up to eventually allow information on remotes defined
+ for an arbitrary repository to be read.
+
+ * Build optimization.
+
+ * Tighten code for testing pack-bitmap.
+
+ * Weather balloon to break people with compilers that do not support
+ C99.
+
+ * The "reftable" backend for the refs API, without integrating into
+ the refs subsystem, has been added.
+
+ * More tests are marked as leak-free.
+
+ * The test framework learns to list unsatisfied test prerequisites,
+ and optionally error out when prerequisites that are expected to be
+ satisfied are not.
+
+ * The default setting for trace2 event nesting was too low to cause
+ test failures, which is worked around by bumping it up in the test
+ framework.
+
+ * Drop support for TravisCI and update test workflows at GitHub.
+
+ * Many tests that used to need GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+ mechanism to force "git" to use 'master' as the default name for
+ the initial branch no longer need it; the use of the mechanism from
+ them have been removed.
+
+ * Allow running our tests while disabling fsync.
+
+ * Document the parameters given to the reflog entry iterator callback
+ functions.
+ (merge e6e94f34b2 jc/reflog-iterator-callback-doc later to maint).
+
+ * The test helper for refs subsystem learned to write bogus and/or
+ nonexistent object name to refs to simulate error situations we
+ want to test Git in.
+
+ * "diff --histogram" optimization.
+
+ * Weather balloon to find compilers that do not grok variable
+ declaration in the for() loop.
+
+ * diff and blame commands have been taught to work better with sparse
+ index.
+
+ * The chainlint test script linter in the test suite has been updated.
+
+ * The DEVELOPER=yes build uses -std=gnu99 now.
+
+ * "git format-patch" uses a single rev_info instance and then exits.
+ Mark the structure with UNLEAK() macro to squelch leak sanitizer.
+
+ * New interface into the tmp-objdir API to help in-core use of the
+ quarantine feature.
+
+ * Broken &&-chains in the test scripts have been corrected.
+
+ * The RCS keyword substitution in "git p4" used to be done assuming
+ that the contents are UTF-8 text, which can trigger decoding
+ errors. We now treat the contents as a bytestring for robustness
+ and correctness.
+
+ * The conditions to choose different definitions of the FLEX_ARRAY
+ macro for vendor compilers has been simplified to make it easier to
+ maintain.
+
+ * Correctness and performance update to "diff --color-moved" feature.
+
+ * "git upload-pack" (the other side of "git fetch") used a 8kB buffer
+ but most of its payload came on 64kB "packets". The buffer size
+ has been enlarged so that such a packet fits.
+
+ * "git fetch" and "git pull" are now declared sparse-index clean.
+ Also "git ls-files" learns the "--sparse" option to help debugging.
+
+ * Similar message templates have been consolidated so that
+ translators need to work on fewer number of messages.
+
+
+Fixes since v2.34
+-----------------
+
+ * "git grep" looking in a blob that has non-UTF8 payload was
+ completely broken when linked with certain versions of PCREv2
+ library in the latest release.
+
+ * Other code cleanup, docfix, build fix, etc.
+
+ * "git pull" with any strategy when the other side is behind us
+ should succeed as it is a no-op, but doesn't.
+
+ * An earlier change in 2.34.0 caused JGit application (that abused
+ GIT_EDITOR mechanism when invoking "git config") to get stuck with
+ a SIGTTOU signal; it has been reverted.
+
+ * An earlier change that broke .gitignore matching has been reverted.
+
+ * Things like "git -c branch.sort=bogus branch new HEAD", i.e. the
+ operation modes of the "git branch" command that do not need the
+ sort key information, no longer errors out by seeing a bogus sort
+ key.
+ (merge 98e7ab6d42 jc/fix-ref-sorting-parse later to maint).
+
+ * The compatibility implementation for unsetenv(3) were written to
+ mimic ancient, non-POSIX, variant seen in an old glibc; it has been
+ changed to return an integer to match the more modern era.
+ (merge a38989bd5b jc/unsetenv-returns-an-int later to maint).
+
+ * The clean/smudge conversion code path has been prepared to better
+ work on platforms where ulong is narrower than size_t.
+ (merge 596b5e77c9 mc/clean-smudge-with-llp64 later to maint).
+
+ * Redact the path part of packfile URI that appears in the trace output.
+ (merge 0ba558ffb1 if/redact-packfile-uri later to maint).
+
+ * CI has been taught to catch some Unicode directional formatting
+ sequence that can be used in certain mischief.
+ (merge 0e7696c64d js/ci-no-directional-formatting later to maint).
+
+ * The "--date=format:<strftime>" gained a workaround for the lack of
+ system support for a non-local timezone to handle "%s" placeholder.
+ (merge 9b591b9403 jk/strbuf-addftime-seconds-since-epoch later to maint).
+
+ * The "merge" subcommand of "git jump" (in contrib/) silently ignored
+ pathspec and other parameters.
+ (merge 67ba13e5a4 jk/jump-merge-with-pathspec later to maint).
+
+ * The code to decode the length of packed object size has been
+ corrected.
+ (merge 34de5b8eac jt/pack-header-lshift-overflow later to maint).
+
+ * The advice message given by "git pull" when the user hasn't made a
+ choice between merge and rebase still said that the merge is the
+ default, which no longer is the case. This has been corrected.
+ (merge 71076d0edd ah/advice-pull-has-no-preference-between-rebase-and-merge later to maint).
+
+ * "git fetch", when received a bad packfile, can fail with SIGPIPE.
+ This wasn't wrong per-se, but we now detect the situation and fail
+ in a more predictable way.
+ (merge 2a4aed42ec jk/fetch-pack-avoid-sigpipe-to-index-pack later to maint).
+
+ * The function to cull a child process and determine the exit status
+ had two separate code paths for normal callers and callers in a
+ signal handler, and the latter did not yield correct value when the
+ child has caught a signal. The handling of the exit status has
+ been unified for these two code paths. An existing test with
+ flakiness has also been corrected.
+ (merge 5263e22cba jk/t7006-sigpipe-tests-fix later to maint).
+
+ * When a non-existent program is given as the pager, we tried to
+ reuse an uninitialized child_process structure and crashed, which
+ has been fixed.
+ (merge f917f57f40 em/missing-pager later to maint).
+
+ * The single-key-input mode in "git add -p" had some code to handle
+ keys that generate a sequence of input via ReadKey(), which did not
+ handle end-of-file correctly, which has been fixed.
+ (merge fc8a8126df cb/add-p-single-key-fix later to maint).
+
+ * "git rebase -x" added an unnecessary 'exec' instructions before
+ 'noop', which has been corrected.
+ (merge cc9dcdee61 en/rebase-x-fix later to maint).
+
+ * When the "git push" command is killed while the receiving end is
+ trying to report what happened to the ref update proposals, the
+ latter used to die, due to SIGPIPE. The code now ignores SIGPIPE
+ to increase our chances to run the post-receive hook after it
+ happens.
+ (merge d34182b9e3 rj/receive-pack-avoid-sigpipe-during-status-reporting later to maint).
+
+ * "git worktree add" showed "Preparing worktree" message to the
+ standard output stream, but when it failed, the message from die()
+ went to the standard error stream. Depending on the order the
+ stdio streams are flushed at the program end, this resulted in
+ confusing output. It has been corrected by sending all the chatty
+ messages to the standard error stream.
+ (merge b50252484f es/worktree-chatty-to-stderr later to maint).
+
+ * Coding guideline document has been updated to clarify what goes to
+ standard error in our system.
+ (merge e258eb4800 es/doc-stdout-vs-stderr later to maint).
+
+ * The sparse-index/sparse-checkout feature had a bug in its use of
+ the matching code to determine which path is in or outside the
+ sparse checkout patterns.
+ (merge 8c5de0d265 ds/sparse-deep-pattern-checkout-fix later to maint).
+
+ * "git rebase -x" by mistake started exporting the GIT_DIR and
+ GIT_WORK_TREE environment variables when the command was rewritten
+ in C, which has been corrected.
+ (merge 434e0636db en/rebase-x-wo-git-dir-env later to maint).
+
+ * When "git log" implicitly enabled the "decoration" processing
+ without being explicitly asked with "--decorate" option, it failed
+ to read and honor the settings given by the "--decorate-refs"
+ option.
+
+ * "git fetch --set-upstream" did not check if there is a current
+ branch, leading to a segfault when it is run on a detached HEAD,
+ which has been corrected.
+ (merge 17baeaf82d ab/fetch-set-upstream-while-detached later to maint).
+
+ * Among some code paths that ask an yes/no question, only one place
+ gave a prompt that looked different from the others, which has been
+ updated to match what the others create.
+ (merge 0fc8ed154c km/help-prompt-fix later to maint).
+
+ * "git log --invert-grep --author=<name>" used to exclude commits
+ written by the given author, but now "--invert-grep" only affects
+ the matches made by the "--grep=<pattern>" option.
+ (merge 794c000267 rs/log-invert-grep-with-headers later to maint).
+
+ * "git grep --perl-regexp" failed to match UTF-8 characters with
+ wildcard when the pattern consists only of ASCII letters, which has
+ been corrected.
+ (merge 32e3e8bc55 rs/pcre2-utf later to maint).
+
+ * Certain sparse-checkout patterns that are valid in non-cone mode
+ led to segfault in cone mode, which has been corrected.
+
+ * Use of certain "git rev-list" options with "git fast-export"
+ created nonsense results (the worst two of which being "--reverse"
+ and "--invert-grep --grep=<foo>"). The use of "--first-parent" is
+ made to behave a bit more sensible than before.
+ (merge 726a228dfb ws/fast-export-with-revision-options later to maint).
+
+ * Perf tests were run with end-user's shell, but it has been
+ corrected to use the shell specified by $TEST_SHELL_PATH.
+ (merge 9ccab75608 ja/perf-use-specified-shell later to maint).
+
+ * Fix dependency rules to generate hook-list.h header file.
+ (merge d3fd1a6667 ab/makefile-hook-list-dependency-fix later to maint).
+
+ * "git stash" by default triggers its "push" action, but its
+ implementation also made "git stash -h" to show short help only for
+ "git stash push", which has been corrected.
+ (merge ca7990cea5 ab/do-not-limit-stash-help-to-push later to maint).
+
+ * "git apply --3way" bypasses the attempt to do a three-way
+ application in more cases to address the regression caused by the
+ recent change to use direct application as a fallback.
+ (merge 34d607032c jz/apply-3-corner-cases later to maint).
+
+ * Fix performance-releated bug in "git subtree" (in contrib/).
+ (merge 3ce8888fb4 jl/subtree-check-parents-argument-passing-fix later to maint).
+
+ * Extend the guidance to choose the base commit to build your work
+ on, and hint/nudge contributors to read others' changes.
+ (merge fdfae830f8 jc/doc-submitting-patches-choice-of-base later to maint).
+
+ * A corner case bug in the ort merge strategy has been corrected.
+ (merge d30126c20d en/merge-ort-renorm-with-rename-delete-conflict-fix later to maint).
+
+ * "git stash apply" forgot to attempt restoring untracked files when
+ it failed to restore changes to tracked ones.
+ (merge 71cade5a0b en/stash-df-fix later to maint).
+
+ * Calling dynamically loaded functions on Windows has been corrected.
+ (merge 4a9b204920 ma/windows-dynload-fix later to maint).
+
+ * Some lockfile code called free() in signal-death code path, which
+ has been corrected.
+ (merge 58d4d7f1c5 ps/lockfile-cleanup-fix later to maint).
+
+ * Other code cleanup, docfix, build fix, etc.
+ (merge 74db416c9c cw/protocol-v2-doc-fix later to maint).
+ (merge f9b2b6684d ja/doc-cleanup later to maint).
+ (merge 7d1b866778 jc/fix-first-object-walk later to maint).
+ (merge 538ac74604 js/trace2-avoid-recursive-errors later to maint).
+ (merge 152923b132 jk/t5319-midx-corruption-test-deflake later to maint).
+ (merge 9081a421a6 ab/checkout-branch-info-leakfix later to maint).
+ (merge 42c456ff81 rs/mergesort later to maint).
+ (merge ad506e6780 tl/midx-docfix later to maint).
+ (merge bf5b83fd8a hk/ci-checkwhitespace-commentfix later to maint).
+ (merge 49f1eb3b34 jk/refs-g11-workaround later to maint).
+ (merge 7d3fc7df70 jt/midx-doc-fix later to maint).
+ (merge 7b089120d9 hn/create-reflog-simplify later to maint).
+ (merge 9e12400da8 cb/mingw-gmtime-r later to maint).
+ (merge 0bf0de6cc7 tb/pack-revindex-on-disk-cleanup later to maint).
+ (merge 2c68f577fc ew/cbtree-remove-unused-and-broken-cb-unlink later to maint).
+ (merge eafd6e7e55 ab/die-with-bug later to maint).
+ (merge 91028f7659 jc/grep-patterntype-default-doc later to maint).
+ (merge 47ca93d071 ds/repack-fixlets later to maint).
+ (merge e6a9bc0c60 rs/t4202-invert-grep-test-fix later to maint).
+ (merge deb5407a42 gh/gpg-doc-markup-fix later to maint).
+ (merge 999bba3e0b rs/daemon-plug-leak later to maint).
+ (merge 786eb1ba39 js/l10n-mention-ngettext-early-in-readme later to maint).
+ (merge 2f12b31b74 ab/makefile-msgfmt-wo-stats later to maint).
+ (merge 0517f591ca fs/gpg-unknown-key-test-fix later to maint).
+ (merge 97d6fb5a1f ma/header-dup-cleanup later to maint).
diff --git a/Documentation/RelNotes/2.35.1.txt b/Documentation/RelNotes/2.35.1.txt
new file mode 100644
index 0000000..726ba25
--- /dev/null
+++ b/Documentation/RelNotes/2.35.1.txt
@@ -0,0 +1,6 @@
+Git v2.35.1 Release Notes
+=========================
+
+Git 2.35 shipped with a regression that broke use of "rebase" and
+"stash" in a secondary worktree. This maintenance release ought to
+fix it.
diff --git a/Documentation/RelNotes/2.35.2.txt b/Documentation/RelNotes/2.35.2.txt
new file mode 100644
index 0000000..290bfa9
--- /dev/null
+++ b/Documentation/RelNotes/2.35.2.txt
@@ -0,0 +1,7 @@
+Git v2.35.2 Release Notes
+=========================
+
+This release merges up the fixes that appear in v2.30.3,
+v2.31.2, v2.32.1, v2.33.2 and v2.34.2 to address the security
+issue CVE-2022-24765; see the release notes for these versions
+for details.
diff --git a/Documentation/RelNotes/2.35.3.txt b/Documentation/RelNotes/2.35.3.txt
new file mode 100644
index 0000000..5458ba3
--- /dev/null
+++ b/Documentation/RelNotes/2.35.3.txt
@@ -0,0 +1,4 @@
+Git Documentation/RelNotes/2.35.3.txt Release Notes
+=========================
+
+This release merges up the fixes that appear in v2.35.3.
diff --git a/Documentation/SubmittingPatches b/Documentation/SubmittingPatches
index 11e0305..92b80d9 100644
--- a/Documentation/SubmittingPatches
+++ b/Documentation/SubmittingPatches
@@ -19,8 +19,10 @@
base your work on the tip of the topic.
* A new feature should be based on `master` in general. If the new
- feature depends on a topic that is in `seen`, but not in `master`,
- base your work on the tip of that topic.
+ feature depends on other topics that are in `next`, but not in
+ `master`, fork a branch from the tip of `master`, merge these topics
+ to the branch, and work on that branch. You can remind yourself of
+ how you prepared the base with `git log --first-parent master..`.
* Corrections and enhancements to a topic not yet in `master` should
be based on the tip of that topic. If the topic has not been merged
@@ -28,10 +30,10 @@
into the series.
* In the exceptional case that a new feature depends on several topics
- not in `master`, start working on `next` or `seen` privately and send
- out patches for discussion. Before the final merge, you may have to
- wait until some of the dependent topics graduate to `master`, and
- rebase your work.
+ not in `master`, start working on `next` or `seen` privately and
+ send out patches only for discussion. Once your new feature starts
+ to stabilize, you would have to rebase it (see the "depends on other
+ topics" above).
* Some parts of the system have dedicated maintainers with their own
repositories (see the section "Subsystems" below). Changes to
@@ -71,8 +73,13 @@
[[tests]]
When adding a new feature, make sure that you have new tests to show
the feature triggers the new behavior when it should, and to show the
-feature does not trigger when it shouldn't. After any code change, make
-sure that the entire test suite passes.
+feature does not trigger when it shouldn't. After any code change,
+make sure that the entire test suite passes. When fixing a bug, make
+sure you have new tests that break if somebody else breaks what you
+fixed by accident to avoid regression. Also, try merging your work to
+'next' and 'seen' and make sure the tests still pass; topics by others
+that are still in flight may have unexpected interactions with what
+you are trying to do in your topic.
Pushing to a fork of https://github.com/git/git will use their CI
integration to test your changes on Linux, Mac and Windows. See the
@@ -144,8 +151,21 @@
archive, summarize the relevant points of the discussion.
[[commit-reference]]
-If you want to reference a previous commit in the history of a stable
-branch, use the format "abbreviated hash (subject, date)", like this:
+
+There are a few reasons why you may want to refer to another commit in
+the "more stable" part of the history (i.e. on branches like `maint`,
+`master`, and `next`):
+
+. A commit that introduced the root cause of a bug you are fixing.
+
+. A commit that introduced a feature that you are enhancing.
+
+. A commit that conflicts with your work when you made a trial merge
+ of your work into `next` and `seen` for testing.
+
+When you reference a commit on a more stable branch (like `master`,
+`maint` and `next`), use the format "abbreviated hash (subject,
+date)", like this:
....
Commit f86a374 (pack-bitmap.c: fix a memleak, 2015-03-30)
@@ -259,9 +279,11 @@
or include any extra files which do not relate to what your patch
is trying to achieve. Make sure to review
your patch after generating it, to ensure accuracy. Before
-sending out, please make sure it cleanly applies to the `master`
-branch head. If you are preparing a work based on "next" branch,
-that is fine, but please mark it as such.
+sending out, please make sure it cleanly applies to the base you
+have chosen in the "Decide what to base your work on" section,
+and unless it targets the `master` branch (which is the default),
+mark your patches as such.
+
[[send-patches]]
=== Sending your patches.
@@ -365,7 +387,10 @@
Send your patch with "To:" set to the mailing list, with "cc:" listing
people who are involved in the area you are touching (the `git
contacts` command in `contrib/contacts/` can help to
-identify them), to solicit comments and reviews.
+identify them), to solicit comments and reviews. Also, when you made
+trial merges of your topic to `next` and `seen`, you may have noticed
+work by others conflicting with your changes. There is a good possibility
+that these people may know the area you are touching well.
:current-maintainer: footnote:[The current maintainer: gitster@pobox.com]
:git-ml: footnote:[The mailing list: git@vger.kernel.org]
diff --git a/Documentation/config.txt b/Documentation/config.txt
index cdff79b..8d3f518 100644
--- a/Documentation/config.txt
+++ b/Documentation/config.txt
@@ -262,11 +262,19 @@
colors (at most two, one for foreground and one for background)
and attributes (as many as you want), separated by spaces.
+
-The basic colors accepted are `normal`, `black`, `red`, `green`, `yellow`,
-`blue`, `magenta`, `cyan` and `white`. The first color given is the
-foreground; the second is the background. All the basic colors except
-`normal` have a bright variant that can be specified by prefixing the
-color with `bright`, like `brightred`.
+The basic colors accepted are `normal`, `black`, `red`, `green`,
+`yellow`, `blue`, `magenta`, `cyan`, `white` and `default`. The first
+color given is the foreground; the second is the background. All the
+basic colors except `normal` and `default` have a bright variant that can
+be specified by prefixing the color with `bright`, like `brightred`.
++
+The color `normal` makes no change to the color. It is the same as an
+empty string, but can be used as the foreground color when specifying a
+background color alone (for example, "normal red").
++
+The color `default` explicitly resets the color to the terminal default,
+for example to specify a cleared background. Although it varies between
+terminals, this is usually not the same as setting to "white black".
+
Colors may also be given as numbers between 0 and 255; these use ANSI
256-color mode (but note that not all terminals may support this). If
@@ -280,6 +288,11 @@
be turned off by prefixing them with `no` or `no-` (e.g., `noreverse`,
`no-ul`, etc).
+
+The pseudo-attribute `reset` resets all colors and attributes before
+applying the specified coloring. For example, `reset green` will result
+in a green foreground and default background without any active
+attributes.
++
An empty color string produces no color effect at all. This can be used
to avoid coloring specific elements without disabling color entirely.
+
diff --git a/Documentation/config/branch.txt b/Documentation/config/branch.txt
index d323d73..1e0c7af 100644
--- a/Documentation/config/branch.txt
+++ b/Documentation/config/branch.txt
@@ -7,7 +7,8 @@
automatic setup is done; `true` -- automatic setup is done when the
starting point is a remote-tracking branch; `always` --
automatic setup is done when the starting point is either a
- local branch or remote-tracking
+ local branch or remote-tracking branch; `inherit` -- if the starting point
+ has a tracking configuration, it is copied to the new
branch. This option defaults to true.
branch.autoSetupRebase::
diff --git a/Documentation/config/gpg.txt b/Documentation/config/gpg.txt
index 4f30c7d..0cb189a 100644
--- a/Documentation/config/gpg.txt
+++ b/Documentation/config/gpg.txt
@@ -34,7 +34,7 @@
* `fully`
* `ultimate`
-gpg.ssh.defaultKeyCommand:
+gpg.ssh.defaultKeyCommand::
This command that will be run when user.signingkey is not set and a ssh
signature is requested. On successful exit a valid ssh public key is
expected in the first line of its output. To automatically use the first
@@ -44,7 +44,7 @@
A file containing ssh public keys which you are willing to trust.
The file consists of one or more lines of principals followed by an ssh
public key.
- e.g.: user1@example.com,user2@example.com ssh-rsa AAAAX1...
+ e.g.: `user1@example.com,user2@example.com ssh-rsa AAAAX1...`
See ssh-keygen(1) "ALLOWED SIGNERS" for details.
The principal is only used to identify the key and is available when
verifying a signature.
@@ -64,6 +64,11 @@
in the repository itself using a path relative to the top-level of the working tree.
This way only committers with an already valid key can add or change keys in the keyring.
+
+Since OpensSSH 8.8 this file allows specifying a key lifetime using valid-after &
+valid-before options. Git will mark signatures as valid if the signing key was
+valid at the time of the signatures creation. This allows users to change a
+signing key without invalidating all previously made signatures.
++
Using a SSH CA key with the cert-authority option
(see ssh-keygen(1) "CERTIFICATES") is also valid.
diff --git a/Documentation/config/grep.txt b/Documentation/config/grep.txt
index 44abe45..182edd8 100644
--- a/Documentation/config/grep.txt
+++ b/Documentation/config/grep.txt
@@ -8,7 +8,8 @@
Set the default matching behavior. Using a value of 'basic', 'extended',
'fixed', or 'perl' will enable the `--basic-regexp`, `--extended-regexp`,
`--fixed-strings`, or `--perl-regexp` option accordingly, while the
- value 'default' will return to the default matching behavior.
+ value 'default' will use the `grep.extendedRegexp` option to choose
+ between 'basic' and 'extended'.
grep.extendedRegexp::
If set to true, enable `--extended-regexp` option by default. This
diff --git a/Documentation/config/merge.txt b/Documentation/config/merge.txt
index e27cc63..99e83dd 100644
--- a/Documentation/config/merge.txt
+++ b/Documentation/config/merge.txt
@@ -4,7 +4,14 @@
shows a `<<<<<<<` conflict marker, changes made by one side,
a `=======` marker, changes made by the other side, and then
a `>>>>>>>` marker. An alternate style, "diff3", adds a `|||||||`
- marker and the original text before the `=======` marker.
+ marker and the original text before the `=======` marker. The
+ "merge" style tends to produce smaller conflict regions than diff3,
+ both because of the exclusion of the original text, and because
+ when a subset of lines match on the two sides they are just pulled
+ out of the conflict region. Another alternate style, "zdiff3", is
+ similar to diff3 but removes matching lines on the two sides from
+ the conflict region when those matching lines appear near either
+ the beginning or end of a conflict region.
merge.defaultToUpstream::
If merge is called without any commit argument, merge the upstream
diff --git a/Documentation/config/user.txt b/Documentation/config/user.txt
index ad78dce..ec9233b 100644
--- a/Documentation/config/user.txt
+++ b/Documentation/config/user.txt
@@ -36,10 +36,13 @@
commit, you can override the default selection with this variable.
This option is passed unchanged to gpg's --local-user parameter,
so you may specify a key using any method that gpg supports.
- If gpg.format is set to "ssh" this can contain the literal ssh public
- key (e.g.: "ssh-rsa XXXXXX identifier") or a file which contains it and
- corresponds to the private key used for signing. The private key
- needs to be available via ssh-agent. Alternatively it can be set to
- a file containing a private key directly. If not set git will call
- gpg.ssh.defaultKeyCommand (e.g.: "ssh-add -L") and try to use the first
- key available.
+ If gpg.format is set to `ssh` this can contain the path to either
+ your private ssh key or the public key when ssh-agent is used.
+ Alternatively it can contain a public key prefixed with `key::`
+ directly (e.g.: "key::ssh-rsa XXXXXX identifier"). The private key
+ needs to be available via ssh-agent. If not set git will call
+ gpg.ssh.defaultKeyCommand (e.g.: "ssh-add -L") and try to use the
+ first key available. For backward compatibility, a raw key which
+ begins with "ssh-", such as "ssh-rsa XXXXXX identifier", is treated
+ as "key::ssh-rsa XXXXXX identifier", but this form is deprecated;
+ use the `key::` form instead.
diff --git a/Documentation/date-formats.txt b/Documentation/date-formats.txt
index 99c455f..67645ca 100644
--- a/Documentation/date-formats.txt
+++ b/Documentation/date-formats.txt
@@ -5,9 +5,9 @@
support the following date formats:
Git internal format::
- It is `<unix timestamp> <time zone offset>`, where `<unix
- timestamp>` is the number of seconds since the UNIX epoch.
- `<time zone offset>` is a positive or negative offset from UTC.
+ It is `<unix-timestamp> <time-zone-offset>`, where
+ `<unix-timestamp>` is the number of seconds since the UNIX epoch.
+ `<time-zone-offset>` is a positive or negative offset from UTC.
For example CET (which is 1 hour ahead of UTC) is `+0100`.
RFC 2822::
diff --git a/Documentation/git-am.txt b/Documentation/git-am.txt
index 0a4a984..09107fb 100644
--- a/Documentation/git-am.txt
+++ b/Documentation/git-am.txt
@@ -16,8 +16,9 @@
[--exclude=<path>] [--include=<path>] [--reject] [-q | --quiet]
[--[no-]scissors] [-S[<keyid>]] [--patch-format=<format>]
[--quoted-cr=<action>]
+ [--empty=(stop|drop|keep)]
[(<mbox> | <Maildir>)...]
-'git am' (--continue | --skip | --abort | --quit | --show-current-patch[=(diff|raw)])
+'git am' (--continue | --skip | --abort | --quit | --show-current-patch[=(diff|raw)] | --allow-empty)
DESCRIPTION
-----------
@@ -63,6 +64,14 @@
--quoted-cr=<action>::
This flag will be passed down to 'git mailinfo' (see linkgit:git-mailinfo[1]).
+--empty=(stop|drop|keep)::
+ By default, or when the option is set to 'stop', the command
+ errors out on an input e-mail message lacking a patch
+ and stops into the middle of the current am session. When this
+ option is set to 'drop', skip such an e-mail message instead.
+ When this option is set to 'keep', create an empty commit,
+ recording the contents of the e-mail message as its log.
+
-m::
--message-id::
Pass the `-m` flag to 'git mailinfo' (see linkgit:git-mailinfo[1]),
@@ -191,6 +200,11 @@
the e-mail message; if `diff`, show the diff portion only.
Defaults to `raw`.
+--allow-empty::
+ After a patch failure on an input e-mail message lacking a patch,
+ create an empty commit with the contents of the e-mail message
+ as its log message.
+
DISCUSSION
----------
diff --git a/Documentation/git-apply.txt b/Documentation/git-apply.txt
index aa1ae56..b6d77f4 100644
--- a/Documentation/git-apply.txt
+++ b/Documentation/git-apply.txt
@@ -16,7 +16,7 @@
[--ignore-space-change | --ignore-whitespace]
[--whitespace=(nowarn|warn|fix|error|error-all)]
[--exclude=<path>] [--include=<path>] [--directory=<root>]
- [--verbose] [--unsafe-paths] [<patch>...]
+ [--verbose | --quiet] [--unsafe-paths] [--allow-empty] [<patch>...]
DESCRIPTION
-----------
@@ -228,6 +228,11 @@
current patch being applied will be printed. This option will cause
additional information to be reported.
+-q::
+--quiet::
+ Suppress stderr output. Messages about patch status and progress
+ will not be printed.
+
--recount::
Do not trust the line counts in the hunk headers, but infer them
by inspecting the patch (e.g. after editing the patch without
@@ -251,6 +256,10 @@
the `--unsafe-paths` option to override this safety check. This option
has no effect when `--index` or `--cached` is in use.
+--allow-empty::
+ Don't return error for patches containing no diff. This includes
+ empty patches and patches with commit text only.
+
CONFIGURATION
-------------
diff --git a/Documentation/git-archimport.txt b/Documentation/git-archimport.txt
index a595a0f..847777f 100644
--- a/Documentation/git-archimport.txt
+++ b/Documentation/git-archimport.txt
@@ -9,14 +9,14 @@
SYNOPSIS
--------
[verse]
-'git archimport' [-h] [-v] [-o] [-a] [-f] [-T] [-D depth] [-t tempdir]
- <archive/branch>[:<git-branch>] ...
+'git archimport' [-h] [-v] [-o] [-a] [-f] [-T] [-D <depth>] [-t <tempdir>]
+ <archive>/<branch>[:<git-branch>]...
DESCRIPTION
-----------
Imports a project from one or more GNU Arch repositories.
It will follow branches
-and repositories within the namespaces defined by the <archive/branch>
+and repositories within the namespaces defined by the <archive>/<branch>
parameters supplied. If it cannot find the remote branch a merge comes from
it will just import it as a regular commit. If it can find it, it will mark it
as a merge whenever possible (see discussion below).
@@ -27,7 +27,7 @@
It expects to be dealing with one project only. If it sees
branches that have different roots, it will refuse to run. In that case,
-edit your <archive/branch> parameters to define clearly the scope of the
+edit your <archive>/<branch> parameters to define clearly the scope of the
import.
'git archimport' uses `tla` extensively in the background to access the
@@ -42,7 +42,7 @@
While 'git archimport' will try to create sensible branch names for the
archives that it imports, it is also possible to specify Git branch names
-manually. To do so, write a Git branch name after each <archive/branch>
+manually. To do so, write a Git branch name after each <archive>/<branch>
parameter, separated by a colon. This way, you can shorten the Arch
branch names and convert Arch jargon to Git jargon, for example mapping a
"PROJECT{litdd}devo{litdd}VERSION" branch to "master".
@@ -104,8 +104,8 @@
Override the default tempdir.
-<archive/branch>::
- Archive/branch identifier in a format that `tla log` understands.
+<archive>/<branch>::
+ <archive>/<branch> identifier in a format that `tla log` understands.
GIT
diff --git a/Documentation/git-branch.txt b/Documentation/git-branch.txt
index 8af42ef..731e340 100644
--- a/Documentation/git-branch.txt
+++ b/Documentation/git-branch.txt
@@ -16,7 +16,7 @@
[--points-at <object>] [--format=<format>]
[(-r | --remotes) | (-a | --all)]
[--list] [<pattern>...]
-'git branch' [--track | --no-track] [-f] <branchname> [<start-point>]
+'git branch' [--track[=(direct|inherit)] | --no-track] [-f] <branchname> [<start-point>]
'git branch' (--set-upstream-to=<upstream> | -u <upstream>) [<branchname>]
'git branch' --unset-upstream [<branchname>]
'git branch' (-m | -M) [<oldbranch>] <newbranch>
@@ -206,24 +206,34 @@
Display the full sha1s in the output listing rather than abbreviating them.
-t::
---track::
+--track[=(direct|inherit)]::
When creating a new branch, set up `branch.<name>.remote` and
- `branch.<name>.merge` configuration entries to mark the
- start-point branch as "upstream" from the new branch. This
+ `branch.<name>.merge` configuration entries to set "upstream" tracking
+ configuration for the new branch. This
configuration will tell git to show the relationship between the
two branches in `git status` and `git branch -v`. Furthermore,
it directs `git pull` without arguments to pull from the
upstream when the new branch is checked out.
+
-This behavior is the default when the start point is a remote-tracking branch.
+The exact upstream branch is chosen depending on the optional argument:
+`-t`, `--track`, or `--track=direct` means to use the start-point branch
+itself as the upstream; `--track=inherit` means to copy the upstream
+configuration of the start-point branch.
++
+`--track=direct` is the default when the start point is a remote-tracking branch.
Set the branch.autoSetupMerge configuration variable to `false` if you
want `git switch`, `git checkout` and `git branch` to always behave as if `--no-track`
were given. Set it to `always` if you want this behavior when the
-start-point is either a local or remote-tracking branch.
+start-point is either a local or remote-tracking branch. Set it to
+`inherit` if you want to copy the tracking configuration from the
+branch point.
++
+See linkgit:git-pull[1] and linkgit:git-config[1] for additional discussion on
+how the `branch.<name>.remote` and `branch.<name>.merge` options are used.
--no-track::
Do not set up "upstream" configuration, even if the
- branch.autoSetupMerge configuration variable is true.
+ branch.autoSetupMerge configuration variable is set.
--set-upstream::
As this option had confusing syntax, it is no longer supported.
diff --git a/Documentation/git-checkout.txt b/Documentation/git-checkout.txt
index d473c9b..9f37e22 100644
--- a/Documentation/git-checkout.txt
+++ b/Documentation/git-checkout.txt
@@ -11,7 +11,7 @@
'git checkout' [-q] [-f] [-m] [<branch>]
'git checkout' [-q] [-f] [-m] --detach [<branch>]
'git checkout' [-q] [-f] [-m] [--detach] <commit>
-'git checkout' [-q] [-f] [-m] [[-b|-B|--orphan] <new_branch>] [<start_point>]
+'git checkout' [-q] [-f] [-m] [[-b|-B|--orphan] <new-branch>] [<start-point>]
'git checkout' [-f|--ours|--theirs|-m|--conflict=<style>] [<tree-ish>] [--] <pathspec>...
'git checkout' [-f|--ours|--theirs|-m|--conflict=<style>] [<tree-ish>] --pathspec-from-file=<file> [--pathspec-file-nul]
'git checkout' (-p|--patch) [<tree-ish>] [--] [<pathspec>...]
@@ -43,7 +43,7 @@
rather expensive side-effects to show only the tracking information,
if exists, for the current branch.
-'git checkout' -b|-B <new_branch> [<start point>]::
+'git checkout' -b|-B <new-branch> [<start-point>]::
Specifying `-b` causes a new branch to be created as if
linkgit:git-branch[1] were called and then checked out. In
@@ -52,11 +52,11 @@
`--track` without `-b` implies branch creation; see the
description of `--track` below.
+
-If `-B` is given, `<new_branch>` is created if it doesn't exist; otherwise, it
+If `-B` is given, `<new-branch>` is created if it doesn't exist; otherwise, it
is reset. This is the transactional equivalent of
+
------------
-$ git branch -f <branch> [<start point>]
+$ git branch -f <branch> [<start-point>]
$ git checkout <branch>
------------
+
@@ -145,18 +145,18 @@
on your side branch as `theirs` (i.e. "one contributor's work on top
of it").
--b <new_branch>::
- Create a new branch named `<new_branch>` and start it at
- `<start_point>`; see linkgit:git-branch[1] for details.
+-b <new-branch>::
+ Create a new branch named `<new-branch>` and start it at
+ `<start-point>`; see linkgit:git-branch[1] for details.
--B <new_branch>::
- Creates the branch `<new_branch>` and start it at `<start_point>`;
- if it already exists, then reset it to `<start_point>`. This is
+-B <new-branch>::
+ Creates the branch `<new-branch>` and start it at `<start-point>`;
+ if it already exists, then reset it to `<start-point>`. This is
equivalent to running "git branch" with "-f"; see
linkgit:git-branch[1] for details.
-t::
---track::
+--track[=(direct|inherit)]::
When creating a new branch, set up "upstream" configuration. See
"--track" in linkgit:git-branch[1] for details.
+
@@ -210,16 +210,16 @@
`<commit>` is not a branch name. See the "DETACHED HEAD" section
below for details.
---orphan <new_branch>::
- Create a new 'orphan' branch, named `<new_branch>`, started from
- `<start_point>` and switch to it. The first commit made on this
+--orphan <new-branch>::
+ Create a new 'orphan' branch, named `<new-branch>`, started from
+ `<start-point>` and switch to it. The first commit made on this
new branch will have no parents and it will be the root of a new
history totally disconnected from all the other branches and
commits.
+
The index and the working tree are adjusted as if you had previously run
-`git checkout <start_point>`. This allows you to start a new history
-that records a set of paths similar to `<start_point>` by easily running
+`git checkout <start-point>`. This allows you to start a new history
+that records a set of paths similar to `<start-point>` by easily running
`git commit -a` to make the root commit.
+
This can be useful when you want to publish the tree from a commit
@@ -229,7 +229,7 @@
code.
+
If you want to start a disconnected history that records a set of paths
-that is totally different from the one of `<start_point>`, then you should
+that is totally different from the one of `<start-point>`, then you should
clear the index and the working tree right after creating the orphan
branch by running `git rm -rf .` from the top level of the working tree.
Afterwards you will be ready to prepare your new files, repopulating the
@@ -266,8 +266,7 @@
The same as `--merge` option above, but changes the way the
conflicting hunks are presented, overriding the
`merge.conflictStyle` configuration variable. Possible values are
- "merge" (default) and "diff3" (in addition to what is shown by
- "merge" style, shows the original contents).
+ "merge" (default), "diff3", and "zdiff3".
-p::
--patch::
@@ -341,10 +340,10 @@
merge base of `A` and `B` if there is exactly one merge base. You can
leave out at most one of `A` and `B`, in which case it defaults to `HEAD`.
-<new_branch>::
+<new-branch>::
Name for the new branch.
-<start_point>::
+<start-point>::
The name of a commit at which to start the new branch; see
linkgit:git-branch[1] for details. Defaults to `HEAD`.
+
diff --git a/Documentation/git-cherry-pick.txt b/Documentation/git-cherry-pick.txt
index 5d75031..78dcc91 100644
--- a/Documentation/git-cherry-pick.txt
+++ b/Documentation/git-cherry-pick.txt
@@ -8,7 +8,7 @@
SYNOPSIS
--------
[verse]
-'git cherry-pick' [--edit] [-n] [-m parent-number] [-s] [-x] [--ff]
+'git cherry-pick' [--edit] [-n] [-m <parent-number>] [-s] [-x] [--ff]
[-S[<keyid>]] <commit>...
'git cherry-pick' (--continue | --skip | --abort | --quit)
@@ -81,8 +81,8 @@
described above, and `-r` was to disable it. Now the
default is not to do `-x` so this option is a no-op.
--m parent-number::
---mainline parent-number::
+-m <parent-number>::
+--mainline <parent-number>::
Usually you cannot cherry-pick a merge because you do not know which
side of the merge should be considered the mainline. This
option specifies the parent number (starting from 1) of
diff --git a/Documentation/git-clone.txt b/Documentation/git-clone.txt
index 3fe3810..984d194 100644
--- a/Documentation/git-clone.txt
+++ b/Documentation/git-clone.txt
@@ -9,10 +9,10 @@
SYNOPSIS
--------
[verse]
-'git clone' [--template=<template_directory>]
+'git clone' [--template=<template-directory>]
[-l] [-s] [--no-hardlinks] [-q] [-n] [--bare] [--mirror]
[-o <name>] [-b <name>] [-u <upload-pack>] [--reference <repository>]
- [--dissociate] [--separate-git-dir <git dir>]
+ [--dissociate] [--separate-git-dir <git-dir>]
[--depth <depth>] [--[no-]single-branch] [--no-tags]
[--recurse-submodules[=<pathspec>]] [--[no-]shallow-submodules]
[--[no-]remote-submodules] [--jobs <n>] [--sparse] [--[no-]reject-shallow]
@@ -167,10 +167,10 @@
configuration variables are created.
--sparse::
- Initialize the sparse-checkout file so the working
- directory starts with only the files in the root
- of the repository. The sparse-checkout file can be
- modified to grow the working directory as needed.
+ Employ a sparse-checkout, with only files in the toplevel
+ directory initially being present. The
+ linkgit:git-sparse-checkout[1] command can be used to grow the
+ working directory as needed.
--filter=<filter-spec>::
Use the partial clone feature and request that the server sends
@@ -211,7 +211,7 @@
via ssh, this specifies a non-default path for the command
run on the other end.
---template=<template_directory>::
+--template=<template-directory>::
Specify the directory from which templates will be used;
(See the "TEMPLATE DIRECTORY" section of linkgit:git-init[1].)
@@ -294,7 +294,7 @@
superproject's recorded SHA-1. Equivalent to passing `--remote` to
`git submodule update`.
---separate-git-dir=<git dir>::
+--separate-git-dir=<git-dir>::
Instead of placing the cloned repository where it is supposed
to be, place the cloned repository at the specified directory,
then make a filesystem-agnostic Git symbolic link to there.
diff --git a/Documentation/git-config.txt b/Documentation/git-config.txt
index 992225f..2285eff 100644
--- a/Documentation/git-config.txt
+++ b/Documentation/git-config.txt
@@ -9,20 +9,20 @@
SYNOPSIS
--------
[verse]
-'git config' [<file-option>] [--type=<type>] [--fixed-value] [--show-origin] [--show-scope] [-z|--null] name [value [value-pattern]]
-'git config' [<file-option>] [--type=<type>] --add name value
-'git config' [<file-option>] [--type=<type>] [--fixed-value] --replace-all name value [value-pattern]
-'git config' [<file-option>] [--type=<type>] [--show-origin] [--show-scope] [-z|--null] [--fixed-value] --get name [value-pattern]
-'git config' [<file-option>] [--type=<type>] [--show-origin] [--show-scope] [-z|--null] [--fixed-value] --get-all name [value-pattern]
-'git config' [<file-option>] [--type=<type>] [--show-origin] [--show-scope] [-z|--null] [--fixed-value] [--name-only] --get-regexp name_regex [value-pattern]
-'git config' [<file-option>] [--type=<type>] [-z|--null] --get-urlmatch name URL
-'git config' [<file-option>] [--fixed-value] --unset name [value-pattern]
-'git config' [<file-option>] [--fixed-value] --unset-all name [value-pattern]
-'git config' [<file-option>] --rename-section old_name new_name
-'git config' [<file-option>] --remove-section name
+'git config' [<file-option>] [--type=<type>] [--fixed-value] [--show-origin] [--show-scope] [-z|--null] <name> [<value> [<value-pattern>]]
+'git config' [<file-option>] [--type=<type>] --add <name> <value>
+'git config' [<file-option>] [--type=<type>] [--fixed-value] --replace-all <name> <value> [<value-pattern>]
+'git config' [<file-option>] [--type=<type>] [--show-origin] [--show-scope] [-z|--null] [--fixed-value] --get <name> [<value-pattern>]
+'git config' [<file-option>] [--type=<type>] [--show-origin] [--show-scope] [-z|--null] [--fixed-value] --get-all <name> [<value-pattern>]
+'git config' [<file-option>] [--type=<type>] [--show-origin] [--show-scope] [-z|--null] [--fixed-value] [--name-only] --get-regexp <name-regex> [<value-pattern>]
+'git config' [<file-option>] [--type=<type>] [-z|--null] --get-urlmatch <name> <URL>
+'git config' [<file-option>] [--fixed-value] --unset <name> [<value-pattern>]
+'git config' [<file-option>] [--fixed-value] --unset-all <name> [<value-pattern>]
+'git config' [<file-option>] --rename-section <old-name> <new-name>
+'git config' [<file-option>] --remove-section <name>
'git config' [<file-option>] [--show-origin] [--show-scope] [-z|--null] [--name-only] -l | --list
-'git config' [<file-option>] --get-color name [default]
-'git config' [<file-option>] --get-colorbool name [stdout-is-tty]
+'git config' [<file-option>] --get-color <name> [<default>]
+'git config' [<file-option>] --get-colorbool <name> [<stdout-is-tty>]
'git config' [<file-option>] -e | --edit
DESCRIPTION
@@ -102,9 +102,9 @@
in which section and variable names are lowercased, but subsection
names are not.
---get-urlmatch name URL::
+--get-urlmatch <name> <URL>::
When given a two-part name section.key, the value for
- section.<url>.key whose <url> part matches the best to the
+ section.<URL>.key whose <URL> part matches the best to the
given URL is returned (if no such key exists, the value for
section.key is used as a fallback). When given just the
section as name, do so for all the keys in the section and
@@ -145,8 +145,8 @@
read from or written to if `extensions.worktreeConfig` is
present. If not it's the same as `--local`.
--f config-file::
---file config-file::
+-f <config-file>::
+--file <config-file>::
For writing options: write to the specified file rather than the
repository `.git/config`.
+
@@ -155,7 +155,7 @@
+
See also <<FILES>>.
---blob blob::
+--blob <blob>::
Similar to `--file` but use the given blob instead of a file. E.g.
you can use 'master:.gitmodules' to read values from the file
'.gitmodules' in the master branch. See "SPECIFYING REVISIONS"
@@ -246,18 +246,18 @@
all queried config options with the scope of that value
(local, global, system, command).
---get-colorbool name [stdout-is-tty]::
+--get-colorbool <name> [<stdout-is-tty>]::
- Find the color setting for `name` (e.g. `color.diff`) and output
- "true" or "false". `stdout-is-tty` should be either "true" or
+ Find the color setting for `<name>` (e.g. `color.diff`) and output
+ "true" or "false". `<stdout-is-tty>` should be either "true" or
"false", and is taken into account when configuration says
- "auto". If `stdout-is-tty` is missing, then checks the standard
+ "auto". If `<stdout-is-tty>` is missing, then checks the standard
output of the command itself, and exits with status 0 if color
is to be used, or exits with status 1 otherwise.
When the color setting for `name` is undefined, the command uses
`color.ui` as fallback.
---get-color name [default]::
+--get-color <name> [<default>]::
Find the color configured for `name` (e.g. `color.diff.new`) and
output it as the ANSI color escape sequence to the standard
diff --git a/Documentation/git-credential.txt b/Documentation/git-credential.txt
index 206e3c5..f186730 100644
--- a/Documentation/git-credential.txt
+++ b/Documentation/git-credential.txt
@@ -8,7 +8,7 @@
SYNOPSIS
--------
------------------
-git credential <fill|approve|reject>
+'git credential' (fill|approve|reject)
------------------
DESCRIPTION
diff --git a/Documentation/git-cvsexportcommit.txt b/Documentation/git-cvsexportcommit.txt
index 00154b6..41c8a8a 100644
--- a/Documentation/git-cvsexportcommit.txt
+++ b/Documentation/git-cvsexportcommit.txt
@@ -9,8 +9,8 @@
SYNOPSIS
--------
[verse]
-'git cvsexportcommit' [-h] [-u] [-v] [-c] [-P] [-p] [-a] [-d cvsroot]
- [-w cvsworkdir] [-W] [-f] [-m msgprefix] [PARENTCOMMIT] COMMITID
+'git cvsexportcommit' [-h] [-u] [-v] [-c] [-P] [-p] [-a] [-d <cvsroot>]
+ [-w <cvs-workdir>] [-W] [-f] [-m <msgprefix>] [<parent-commit>] <commit-id>
DESCRIPTION
diff --git a/Documentation/git-cvsimport.txt b/Documentation/git-cvsimport.txt
index de1ebed..b3f2767 100644
--- a/Documentation/git-cvsimport.txt
+++ b/Documentation/git-cvsimport.txt
@@ -11,9 +11,9 @@
[verse]
'git cvsimport' [-o <branch-for-HEAD>] [-h] [-v] [-d <CVSROOT>]
[-A <author-conv-file>] [-p <options-for-cvsps>] [-P <file>]
- [-C <git_repository>] [-z <fuzz>] [-i] [-k] [-u] [-s <subst>]
- [-a] [-m] [-M <regex>] [-S <regex>] [-L <commitlimit>]
- [-r <remote>] [-R] [<CVS_module>]
+ [-C <git-repository>] [-z <fuzz>] [-i] [-k] [-u] [-s <subst>]
+ [-a] [-m] [-M <regex>] [-S <regex>] [-L <commit-limit>]
+ [-r <remote>] [-R] [<CVS-module>]
DESCRIPTION
@@ -59,7 +59,7 @@
from `CVS/Root`. If no such file exists, it checks for the
`CVSROOT` environment variable.
-<CVS_module>::
+<CVS-module>::
The CVS module you want to import. Relative to <CVSROOT>.
If not given, 'git cvsimport' tries to read it from
`CVS/Repository`.
diff --git a/Documentation/git-diff-files.txt b/Documentation/git-diff-files.txt
index 906774f..bf1febb 100644
--- a/Documentation/git-diff-files.txt
+++ b/Documentation/git-diff-files.txt
@@ -9,7 +9,7 @@
SYNOPSIS
--------
[verse]
-'git diff-files' [-q] [-0|-1|-2|-3|-c|--cc] [<common diff options>] [<path>...]
+'git diff-files' [-q] [-0|-1|-2|-3|-c|--cc] [<common-diff-options>] [<path>...]
DESCRIPTION
-----------
diff --git a/Documentation/git-diff-index.txt b/Documentation/git-diff-index.txt
index 27acb31..679cae2 100644
--- a/Documentation/git-diff-index.txt
+++ b/Documentation/git-diff-index.txt
@@ -9,7 +9,7 @@
SYNOPSIS
--------
[verse]
-'git diff-index' [-m] [--cached] [--merge-base] [<common diff options>] <tree-ish> [<path>...]
+'git diff-index' [-m] [--cached] [--merge-base] [<common-diff-options>] <tree-ish> [<path>...]
DESCRIPTION
-----------
diff --git a/Documentation/git-diff-tree.txt b/Documentation/git-diff-tree.txt
index 2fc24c5..274d5ea 100644
--- a/Documentation/git-diff-tree.txt
+++ b/Documentation/git-diff-tree.txt
@@ -11,7 +11,7 @@
[verse]
'git diff-tree' [--stdin] [-m] [-s] [-v] [--no-commit-id] [--pretty]
[-t] [-r] [-c | --cc] [--combined-all-paths] [--root] [--merge-base]
- [<common diff options>] <tree-ish> [<tree-ish>] [<path>...]
+ [<common-diff-options>] <tree-ish> [<tree-ish>] [<path>...]
DESCRIPTION
-----------
diff --git a/Documentation/git-fmt-merge-msg.txt b/Documentation/git-fmt-merge-msg.txt
index 6793d8f..6f28812 100644
--- a/Documentation/git-fmt-merge-msg.txt
+++ b/Documentation/git-fmt-merge-msg.txt
@@ -9,7 +9,7 @@
SYNOPSIS
--------
[verse]
-'git fmt-merge-msg' [-m <message>] [--log[=<n>] | --no-log]
+'git fmt-merge-msg' [-m <message>] [--into-name <branch>] [--log[=<n>] | --no-log]
'git fmt-merge-msg' [-m <message>] [--log[=<n>] | --no-log] -F <file>
DESCRIPTION
@@ -44,6 +44,10 @@
Use <message> instead of the branch names for the first line
of the log message. For use with `--log`.
+--into-name <branch>::
+ Prepare the merge message as if merging to the branch `<branch>`,
+ instead of the name of the real branch to which the merge is made.
+
-F <file>::
--file <file>::
Take the list of merged objects from <file> instead of
diff --git a/Documentation/git-format-patch.txt b/Documentation/git-format-patch.txt
index 113eabc..be797d7 100644
--- a/Documentation/git-format-patch.txt
+++ b/Documentation/git-format-patch.txt
@@ -18,7 +18,7 @@
[-n | --numbered | -N | --no-numbered]
[--start-number <n>] [--numbered-files]
[--in-reply-to=<message id>] [--suffix=.<sfx>]
- [--ignore-if-in-upstream]
+ [--ignore-if-in-upstream] [--always]
[--cover-from-description=<mode>]
[--rfc] [--subject-prefix=<subject prefix>]
[(--reroll-count|-v) <n>]
@@ -192,6 +192,10 @@
patches being generated, and any patch that matches is
ignored.
+--always::
+ Include patches for commits that do not introduce any change,
+ which are omitted by default.
+
--cover-from-description=<mode>::
Controls which parts of the cover letter will be automatically
populated using the branch's description.
diff --git a/Documentation/git-fsck.txt b/Documentation/git-fsck.txt
index bd59661..5088783 100644
--- a/Documentation/git-fsck.txt
+++ b/Documentation/git-fsck.txt
@@ -12,7 +12,7 @@
'git fsck' [--tags] [--root] [--unreachable] [--cache] [--no-reflogs]
[--[no-]full] [--strict] [--verbose] [--lost-found]
[--[no-]dangling] [--[no-]progress] [--connectivity-only]
- [--[no-]name-objects] [<object>*]
+ [--[no-]name-objects] [<object>...]
DESCRIPTION
-----------
diff --git a/Documentation/git-gui.txt b/Documentation/git-gui.txt
index c9d7e96..e8f3ccb 100644
--- a/Documentation/git-gui.txt
+++ b/Documentation/git-gui.txt
@@ -8,7 +8,7 @@
SYNOPSIS
--------
[verse]
-'git gui' [<command>] [arguments]
+'git gui' [<command>] [<arguments>]
DESCRIPTION
-----------
diff --git a/Documentation/git-help.txt b/Documentation/git-help.txt
index 96d5f59..44ea63c 100644
--- a/Documentation/git-help.txt
+++ b/Documentation/git-help.txt
@@ -9,14 +9,14 @@
--------
[verse]
'git help' [-a|--all [--[no-]verbose]]
- [[-i|--info] [-m|--man] [-w|--web]] [COMMAND|GUIDE]
+ [[-i|--info] [-m|--man] [-w|--web]] [<command>|<guide>]
'git help' [-g|--guides]
'git help' [-c|--config]
DESCRIPTION
-----------
-With no options and no COMMAND or GUIDE given, the synopsis of the 'git'
+With no options and no '<command>' or '<guide>' given, the synopsis of the 'git'
command and a list of the most commonly used Git commands are printed
on the standard output.
@@ -33,7 +33,7 @@
If an alias is given, git shows the definition of the alias on
standard output. To get the manual page for the aliased command, use
-`git COMMAND --help`.
+`git <command> --help`.
Note that `git --help ...` is identical to `git help ...` because the
former is internally converted into the latter.
diff --git a/Documentation/git-http-fetch.txt b/Documentation/git-http-fetch.txt
index 9fa17b6..319062c 100644
--- a/Documentation/git-http-fetch.txt
+++ b/Documentation/git-http-fetch.txt
@@ -9,7 +9,7 @@
SYNOPSIS
--------
[verse]
-'git http-fetch' [-c] [-t] [-a] [-d] [-v] [-w filename] [--recover] [--stdin | --packfile=<hash> | <commit>] <url>
+'git http-fetch' [-c] [-t] [-a] [-d] [-v] [-w <filename>] [--recover] [--stdin | --packfile=<hash> | <commit>] <URL>
DESCRIPTION
-----------
diff --git a/Documentation/git-http-push.txt b/Documentation/git-http-push.txt
index ea03a4e..7c6a6dd 100644
--- a/Documentation/git-http-push.txt
+++ b/Documentation/git-http-push.txt
@@ -9,7 +9,7 @@
SYNOPSIS
--------
[verse]
-'git http-push' [--all] [--dry-run] [--force] [--verbose] <url> <ref> [<ref>...]
+'git http-push' [--all] [--dry-run] [--force] [--verbose] <URL> <ref> [<ref>...]
DESCRIPTION
-----------
@@ -63,16 +63,15 @@
cannot have a colon in it). A single pattern '<name>' is just a
shorthand for '<name>:<name>'.
-Each pattern pair consists of the source side (before the colon)
-and the destination side (after the colon). The ref to be
-pushed is determined by finding a match that matches the source
-side, and where it is pushed is determined by using the
-destination side.
+Each pattern pair '<src>:<dst>' consists of the source side (before
+the colon) and the destination side (after the colon). The ref to be
+pushed is determined by finding a match that matches the source side,
+and where it is pushed is determined by using the destination side.
- - It is an error if <src> does not match exactly one of the
+ - It is an error if '<src>' does not match exactly one of the
local refs.
- - If <dst> does not match any remote ref, either
+ - If '<dst>' does not match any remote ref, either
* it has to start with "refs/"; <dst> is used as the
destination literally in this case.
diff --git a/Documentation/git-init-db.txt b/Documentation/git-init-db.txt
index 648a6cd..18bf1a3 100644
--- a/Documentation/git-init-db.txt
+++ b/Documentation/git-init-db.txt
@@ -9,7 +9,7 @@
SYNOPSIS
--------
[verse]
-'git init-db' [-q | --quiet] [--bare] [--template=<template_directory>] [--separate-git-dir <git dir>] [--shared[=<permissions>]]
+'git init-db' [-q | --quiet] [--bare] [--template=<template-directory>] [--separate-git-dir <git-dir>] [--shared[=<permissions>]]
DESCRIPTION
diff --git a/Documentation/git-init.txt b/Documentation/git-init.txt
index b611d80..ad921fe 100644
--- a/Documentation/git-init.txt
+++ b/Documentation/git-init.txt
@@ -9,10 +9,10 @@
SYNOPSIS
--------
[verse]
-'git init' [-q | --quiet] [--bare] [--template=<template_directory>]
- [--separate-git-dir <git dir>] [--object-format=<format>]
+'git init' [-q | --quiet] [--bare] [--template=<template-directory>]
+ [--separate-git-dir <git-dir>] [--object-format=<format>]
[-b <branch-name> | --initial-branch=<branch-name>]
- [--shared[=<permissions>]] [directory]
+ [--shared[=<permissions>]] [<directory>]
DESCRIPTION
@@ -57,12 +57,12 @@
+
include::object-format-disclaimer.txt[]
---template=<template_directory>::
+--template=<template-directory>::
Specify the directory from which templates will be used. (See the "TEMPLATE
DIRECTORY" section below.)
---separate-git-dir=<git dir>::
+--separate-git-dir=<git-dir>::
Instead of initializing the repository as a directory to either `$GIT_DIR` or
`./.git/`, create a text file there containing the path to the actual
@@ -79,7 +79,7 @@
`master`, but this is subject to change in the future; the name can be
customized via the `init.defaultBranch` configuration variable).
---shared[=(false|true|umask|group|all|world|everybody|0xxx)]::
+--shared[=(false|true|umask|group|all|world|everybody|<perm>)]::
Specify that the Git repository is to be shared amongst several users. This
allows users belonging to the same group to push into that
@@ -110,13 +110,16 @@
Same as 'group', but make the repository readable by all users.
-'0xxx'::
+'<perm>'::
-'0xxx' is an octal number and each file will have mode '0xxx'. '0xxx' will
-override users' umask(2) value (and not only loosen permissions as 'group' and
-'all' does). '0640' will create a repository which is group-readable, but not
-group-writable or accessible to others. '0660' will create a repo that is
-readable and writable to the current user and group, but inaccessible to others.
+'<perm>' is a 3-digit octal number prefixed with `0` and each file
+will have mode '<perm>'. '<perm>' will override users' umask(2)
+value (and not only loosen permissions as 'group' and 'all'
+does). '0640' will create a repository which is group-readable, but
+not group-writable or accessible to others. '0660' will create a repo
+that is readable and writable to the current user and group, but
+inaccessible to others (directories and executable files get their
+`x` bit from the `r` bit for corresponding classes of users).
--
By default, the configuration flag `receive.denyNonFastForwards` is enabled
diff --git a/Documentation/git-log.txt b/Documentation/git-log.txt
index 0498e7b..20e87ce 100644
--- a/Documentation/git-log.txt
+++ b/Documentation/git-log.txt
@@ -9,7 +9,7 @@
SYNOPSIS
--------
[verse]
-'git log' [<options>] [<revision range>] [[--] <path>...]
+'git log' [<options>] [<revision-range>] [[--] <path>...]
DESCRIPTION
-----------
@@ -81,13 +81,13 @@
include::line-range-options.txt[]
-<revision range>::
+<revision-range>::
Show only commits in the specified revision range. When no
- <revision range> is specified, it defaults to `HEAD` (i.e. the
+ <revision-range> is specified, it defaults to `HEAD` (i.e. the
whole history leading to the current commit). `origin..HEAD`
specifies all the commits reachable from the current commit
(i.e. `HEAD`), but not from `origin`. For a complete list of
- ways to spell <revision range>, see the 'Specifying Ranges'
+ ways to spell <revision-range>, see the 'Specifying Ranges'
section of linkgit:gitrevisions[7].
[--] <path>...::
diff --git a/Documentation/git-ls-files.txt b/Documentation/git-ls-files.txt
index 6d11ab5..48cc7c0 100644
--- a/Documentation/git-ls-files.txt
+++ b/Documentation/git-ls-files.txt
@@ -10,9 +10,9 @@
--------
[verse]
'git ls-files' [-z] [-t] [-v] [-f]
- (--[cached|deleted|others|ignored|stage|unmerged|killed|modified])*
- (-[c|d|o|i|s|u|k|m])*
- [--eol]
+ [-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>]
[-X <file>|--exclude-from=<file>]
@@ -187,6 +187,11 @@
and in the working tree ("w/<eolinfo>") are shown for regular files,
followed by the ("attr/<eolattr>").
+--sparse::
+ If the index is sparse, show the sparse directories without expanding
+ to the contained files. Sparse directories will be shown with a
+ trailing slash, such as "x/" for a sparse directory "x".
+
\--::
Do not interpret any more arguments as options.
diff --git a/Documentation/git-merge-file.txt b/Documentation/git-merge-file.txt
index f856032..7e9093f 100644
--- a/Documentation/git-merge-file.txt
+++ b/Documentation/git-merge-file.txt
@@ -70,6 +70,9 @@
--diff3::
Show conflicts in "diff3" style.
+--zdiff3::
+ Show conflicts in "zdiff3" style.
+
--ours::
--theirs::
--union::
diff --git a/Documentation/git-merge-index.txt b/Documentation/git-merge-index.txt
index 2ab84a9..eea56b3 100644
--- a/Documentation/git-merge-index.txt
+++ b/Documentation/git-merge-index.txt
@@ -9,7 +9,7 @@
SYNOPSIS
--------
[verse]
-'git merge-index' [-o] [-q] <merge-program> (-a | [--] <file>*)
+'git merge-index' [-o] [-q] <merge-program> (-a | ( [--] <file>...) )
DESCRIPTION
-----------
diff --git a/Documentation/git-merge.txt b/Documentation/git-merge.txt
index e4f3352..3125473 100644
--- a/Documentation/git-merge.txt
+++ b/Documentation/git-merge.txt
@@ -12,7 +12,8 @@
'git merge' [-n] [--stat] [--no-commit] [--squash] [--[no-]edit]
[--no-verify] [-s <strategy>] [-X <strategy-option>] [-S[<keyid>]]
[--[no-]allow-unrelated-histories]
- [--[no-]rerere-autoupdate] [-m <msg>] [-F <file>] [<commit>...]
+ [--[no-]rerere-autoupdate] [-m <msg>] [-F <file>]
+ [--into-name <branch>] [<commit>...]
'git merge' (--continue | --abort | --quit)
DESCRIPTION
@@ -76,6 +77,11 @@
used to give a good default for automated 'git merge'
invocations. The automated message can include the branch description.
+--into-name <branch>::
+ Prepare the default merge message as if merging to the branch
+ `<branch>`, instead of the name of the real branch to which
+ the merge is made.
+
-F <file>::
--file=<file>::
Read the commit message to be used for the merge commit (in
@@ -240,7 +246,8 @@
------------
Here are lines that are either unchanged from the common
-ancestor, or cleanly resolved because only one side changed.
+ancestor, or cleanly resolved because only one side changed,
+or cleanly resolved because both sides changed the same way.
<<<<<<< yours:sample.txt
Conflict resolution is hard;
let's go shopping.
@@ -261,16 +268,37 @@
other side wants to claim it is easy.
An alternative style can be used by setting the "merge.conflictStyle"
-configuration variable to "diff3". In "diff3" style, the above conflict
-may look like this:
+configuration variable to either "diff3" or "zdiff3". In "diff3"
+style, the above conflict may look like this:
------------
Here are lines that are either unchanged from the common
-ancestor, or cleanly resolved because only one side changed.
+ancestor, or cleanly resolved because only one side changed,
+<<<<<<< yours:sample.txt
+or cleanly resolved because both sides changed the same way.
+Conflict resolution is hard;
+let's go shopping.
+||||||| base:sample.txt
+or cleanly resolved because both sides changed identically.
+Conflict resolution is hard.
+=======
+or cleanly resolved because both sides changed the same way.
+Git makes conflict resolution easy.
+>>>>>>> theirs:sample.txt
+And here is another line that is cleanly resolved or unmodified.
+------------
+
+while in "zdiff3" style, it may look like this:
+
+------------
+Here are lines that are either unchanged from the common
+ancestor, or cleanly resolved because only one side changed,
+or cleanly resolved because both sides changed the same way.
<<<<<<< yours:sample.txt
Conflict resolution is hard;
let's go shopping.
-|||||||
+||||||| base:sample.txt
+or cleanly resolved because both sides changed identically.
Conflict resolution is hard.
=======
Git makes conflict resolution easy.
diff --git a/Documentation/git-p4.txt b/Documentation/git-p4.txt
index 38e5257..e21fcd8 100644
--- a/Documentation/git-p4.txt
+++ b/Documentation/git-p4.txt
@@ -9,10 +9,10 @@
SYNOPSIS
--------
[verse]
-'git p4 clone' [<sync options>] [<clone options>] <p4 depot path>...
-'git p4 sync' [<sync options>] [<p4 depot path>...]
+'git p4 clone' [<sync-options>] [<clone-options>] <p4-depot-path>...
+'git p4 sync' [<sync-options>] [<p4-depot-path>...]
'git p4 rebase'
-'git p4 submit' [<submit options>] [<master branch name>]
+'git p4 submit' [<submit-options>] [<master-branch-name>]
DESCRIPTION
@@ -361,7 +361,7 @@
p4/master. See the "Sync options" section above for more
information.
---commit <sha1>|<sha1..sha1>::
+--commit (<sha1>|<sha1>..<sha1>)::
Submit only the specified commit or range of commits, instead of the full
list of changes that are in the current Git branch.
diff --git a/Documentation/git-pack-objects.txt b/Documentation/git-pack-objects.txt
index dbfd1f9..f8344e1 100644
--- a/Documentation/git-pack-objects.txt
+++ b/Documentation/git-pack-objects.txt
@@ -13,8 +13,8 @@
[--no-reuse-delta] [--delta-base-offset] [--non-empty]
[--local] [--incremental] [--window=<n>] [--depth=<n>]
[--revs [--unpacked | --all]] [--keep-pack=<pack-name>]
- [--stdout [--filter=<filter-spec>] | base-name]
- [--shallow] [--keep-true-parents] [--[no-]sparse] < object-list
+ [--stdout [--filter=<filter-spec>] | <base-name>]
+ [--shallow] [--keep-true-parents] [--[no-]sparse] < <object-list>
DESCRIPTION
diff --git a/Documentation/git-pack-redundant.txt b/Documentation/git-pack-redundant.txt
index f2869da..ee7034b 100644
--- a/Documentation/git-pack-redundant.txt
+++ b/Documentation/git-pack-redundant.txt
@@ -9,7 +9,7 @@
SYNOPSIS
--------
[verse]
-'git pack-redundant' [ --verbose ] [ --alt-odb ] < --all | .pack filename ... >
+'git pack-redundant' [ --verbose ] [ --alt-odb ] ( --all | <pack-filename>... )
DESCRIPTION
-----------
diff --git a/Documentation/git-rebase.txt b/Documentation/git-rebase.txt
index a1af21f..9da4647 100644
--- a/Documentation/git-rebase.txt
+++ b/Documentation/git-rebase.txt
@@ -714,9 +714,9 @@
generates new fake commits based off limited information in the
generated patches), those commits cannot be identified; instead it has
to fall back to a commit summary. Also, when merge.conflictStyle is
-set to diff3, the apply backend will use "constructed merge base" to
-label the content from the merge base, and thus provide no information
-about the merge base commit whatsoever.
+set to diff3 or zdiff3, the apply backend will use "constructed merge
+base" to label the content from the merge base, and thus provide no
+information about the merge base commit whatsoever.
The merge backend works with the full commits on both sides of history
and thus has no such limitations.
diff --git a/Documentation/git-reflog.txt b/Documentation/git-reflog.txt
index ff487ff..5ced7ad 100644
--- a/Documentation/git-reflog.txt
+++ b/Documentation/git-reflog.txt
@@ -17,12 +17,12 @@
depending on the subcommand:
[verse]
-'git reflog' ['show'] [log-options] [<ref>]
+'git reflog' ['show'] [<log-options>] [<ref>]
'git reflog expire' [--expire=<time>] [--expire-unreachable=<time>]
[--rewrite] [--updateref] [--stale-fix]
[--dry-run | -n] [--verbose] [--all [--single-worktree] | <refs>...]
'git reflog delete' [--rewrite] [--updateref]
- [--dry-run | -n] [--verbose] ref@\{specifier\}...
+ [--dry-run | -n] [--verbose] <ref>@\{<specifier>\}...
'git reflog exists' <ref>
Reference logs, or "reflogs", record when the tips of branches and
diff --git a/Documentation/git-remote.txt b/Documentation/git-remote.txt
index 31c29c9..2bebc32 100644
--- a/Documentation/git-remote.txt
+++ b/Documentation/git-remote.txt
@@ -10,7 +10,7 @@
--------
[verse]
'git remote' [-v | --verbose]
-'git remote add' [-t <branch>] [-m <master>] [-f] [--[no-]tags] [--mirror=(fetch|push)] <name> <url>
+'git remote add' [-t <branch>] [-m <master>] [-f] [--[no-]tags] [--mirror=(fetch|push)] <name> <URL>
'git remote rename' <old> <new>
'git remote remove' <name>
'git remote set-head' <name> (-a | --auto | -d | --delete | <branch>)
@@ -18,7 +18,7 @@
'git remote get-url' [--push] [--all] <name>
'git remote set-url' [--push] <name> <newurl> [<oldurl>]
'git remote set-url --add' [--push] <name> <newurl>
-'git remote set-url --delete' [--push] <name> <url>
+'git remote set-url --delete' [--push] <name> <URL>
'git remote' [-v | --verbose] 'show' [-n] <name>...
'git remote prune' [-n | --dry-run] <name>...
'git remote' [-v | --verbose] 'update' [-p | --prune] [(<group> | <remote>)...]
@@ -47,7 +47,7 @@
'add'::
Add a remote named <name> for the repository at
-<url>. The command `git fetch <name>` can then be used to create and
+<URL>. The command `git fetch <name>` can then be used to create and
update remote-tracking branches <name>/<branch>.
+
With `-f` option, `git fetch <name>` is run immediately after
@@ -152,7 +152,7 @@
With `--add`, instead of changing existing URLs, new URL is added.
+
With `--delete`, instead of changing existing URLs, all URLs matching
-regex <url> are deleted for remote <name>. Trying to delete all
+regex <URL> are deleted for remote <name>. Trying to delete all
non-push URLs is an error.
+
Note that the push URL and the fetch URL, even though they can
diff --git a/Documentation/git-repack.txt b/Documentation/git-repack.txt
index 7183fb4..ee30edc 100644
--- a/Documentation/git-repack.txt
+++ b/Documentation/git-repack.txt
@@ -76,8 +76,9 @@
linkgit:git-pack-objects[1].
-q::
- Pass the `-q` option to 'git pack-objects'. See
- linkgit:git-pack-objects[1].
+--quiet::
+ Show no progress over the standard error stream and pass the `-q`
+ option to 'git pack-objects'. See linkgit:git-pack-objects[1].
-n::
Do not update the server information with
diff --git a/Documentation/git-request-pull.txt b/Documentation/git-request-pull.txt
index 4d4392d..fa5a426 100644
--- a/Documentation/git-request-pull.txt
+++ b/Documentation/git-request-pull.txt
@@ -8,7 +8,7 @@
SYNOPSIS
--------
[verse]
-'git request-pull' [-p] <start> <url> [<end>]
+'git request-pull' [-p] <start> <URL> [<end>]
DESCRIPTION
-----------
@@ -21,7 +21,7 @@
The upstream project is expected to have the commit named by
`<start>` and the output asks it to integrate the changes you made
since that commit, up to the commit named by `<end>`, by visiting
-the repository named by `<url>`.
+the repository named by `<URL>`.
OPTIONS
@@ -33,14 +33,14 @@
Commit to start at. This names a commit that is already in
the upstream history.
-<url>::
+<URL>::
The repository URL to be pulled from.
<end>::
Commit to end at (defaults to HEAD). This names the commit
at the tip of the history you are asking to be pulled.
+
-When the repository named by `<url>` has the commit at a tip of a
+When the repository named by `<URL>` has the commit at a tip of a
ref that is different from the ref you have locally, you can use the
`<local>:<remote>` syntax, to have its local name, a colon `:`, and
its remote name.
diff --git a/Documentation/git-restore.txt b/Documentation/git-restore.txt
index 55bde91..5964810 100644
--- a/Documentation/git-restore.txt
+++ b/Documentation/git-restore.txt
@@ -92,8 +92,7 @@
The same as `--merge` option above, but changes the way the
conflicting hunks are presented, overriding the
`merge.conflictStyle` configuration variable. Possible values
- are "merge" (default) and "diff3" (in addition to what is
- shown by "merge" style, shows the original contents).
+ are "merge" (default), "diff3", and "zdiff3".
--ignore-unmerged::
When restoring files on the working tree from the index, do
diff --git a/Documentation/git-send-email.txt b/Documentation/git-send-email.txt
index 3db4eab..41cd8cb 100644
--- a/Documentation/git-send-email.txt
+++ b/Documentation/git-send-email.txt
@@ -9,7 +9,8 @@
SYNOPSIS
--------
[verse]
-'git send-email' [<options>] <file|directory|rev-list options>...
+'git send-email' [<options>] <file|directory>...
+'git send-email' [<options>] <format-patch options>
'git send-email' --dump-aliases
@@ -19,7 +20,8 @@
Patches can be specified as files, directories (which will send all
files in the directory), or directly as a revision list. In the
last case, any format accepted by linkgit:git-format-patch[1] can
-be passed to git send-email.
+be passed to git send-email, as well as options understood by
+linkgit:git-format-patch[1].
The header of the email is configurable via command-line options. If not
specified on the command line, the user will be prompted with a ReadLine
diff --git a/Documentation/git-shortlog.txt b/Documentation/git-shortlog.txt
index c9c7f30..f64e770 100644
--- a/Documentation/git-shortlog.txt
+++ b/Documentation/git-shortlog.txt
@@ -8,7 +8,7 @@
SYNOPSIS
--------
[verse]
-'git shortlog' [<options>] [<revision range>] [[--] <path>...]
+'git shortlog' [<options>] [<revision-range>] [[--] <path>...]
git log --pretty=short | 'git shortlog' [<options>]
DESCRIPTION
@@ -89,13 +89,13 @@
If width is `0` (zero) then indent the lines of the output without wrapping
them.
-<revision range>::
+<revision-range>::
Show only commits in the specified revision range. When no
- <revision range> is specified, it defaults to `HEAD` (i.e. the
+ <revision-range> is specified, it defaults to `HEAD` (i.e. the
whole history leading to the current commit). `origin..HEAD`
specifies all the commits reachable from the current commit
(i.e. `HEAD`), but not from `origin`. For a complete list of
- ways to spell <revision range>, see the "Specifying Ranges"
+ ways to spell <revision-range>, see the "Specifying Ranges"
section of linkgit:gitrevisions[7].
[--] <path>...::
diff --git a/Documentation/git-sparse-checkout.txt b/Documentation/git-sparse-checkout.txt
index 42056ee..b81dbe0 100644
--- a/Documentation/git-sparse-checkout.txt
+++ b/Documentation/git-sparse-checkout.txt
@@ -11,7 +11,7 @@
SYNOPSIS
--------
[verse]
-'git sparse-checkout <subcommand> [options]'
+'git sparse-checkout <subcommand> [<options>]'
DESCRIPTION
@@ -30,28 +30,36 @@
'list'::
Describe the patterns in the sparse-checkout file.
-'init'::
- Enable the `core.sparseCheckout` setting. If the
- sparse-checkout file does not exist, then populate it with
- patterns that match every file in the root directory and
- no other directories, then will remove all directories tracked
- by Git. Add patterns to the sparse-checkout file to
- repopulate the working directory.
+'set'::
+ Enable the necessary config settings
+ (extensions.worktreeConfig, core.sparseCheckout,
+ core.sparseCheckoutCone) if they are not already enabled, and
+ write a set of patterns to the sparse-checkout file from the
+ list of arguments following the 'set' subcommand. Update the
+ working directory to match the new patterns.
+
-To avoid interfering with other worktrees, it first enables the
-`extensions.worktreeConfig` setting and makes sure to set the
-`core.sparseCheckout` setting in the worktree-specific config file.
+When the `--stdin` option is provided, the patterns are read from
+standard in as a newline-delimited list instead of from the arguments.
+
-When `--cone` is provided, the `core.sparseCheckoutCone` setting is
-also set, allowing for better performance with a limited set of
-patterns (see 'CONE PATTERN SET' below).
+When `--cone` is passed or `core.sparseCheckoutCone` is enabled, the
+input list is considered a list of directories instead of
+sparse-checkout patterns. This allows for better performance with a
+limited set of patterns (see 'CONE PATTERN SET' below). Note that the
+set command will write patterns to the sparse-checkout file to include
+all files contained in those directories (recursively) as well as
+files that are siblings of ancestor directories. The input format
+matches the output of `git ls-tree --name-only`. This includes
+interpreting pathnames that begin with a double quote (") as C-style
+quoted strings. This may become the default in the future; --no-cone
+can be passed to request non-cone mode.
+
-Use the `--[no-]sparse-index` option to toggle the use of the sparse
-index format. This reduces the size of the index to be more closely
-aligned with your sparse-checkout definition. This can have significant
-performance advantages for commands such as `git status` or `git add`.
-This feature is still experimental. Some commands might be slower with
-a sparse index until they are properly integrated with the feature.
+Use the `--[no-]sparse-index` option to use a sparse index (the
+default is to not use it). A sparse index reduces the size of the
+index to be more closely aligned with your sparse-checkout
+definition. This can have significant performance advantages for
+commands such as `git status` or `git add`. This feature is still
+experimental. Some commands might be slower with a sparse index until
+they are properly integrated with the feature.
+
**WARNING:** Using a sparse index requires modifying the index in a way
that is not completely understood by external tools. If you have trouble
@@ -60,23 +68,6 @@
understand the sparse directory entries index extension and may fail to
interact with your repository until it is disabled.
-'set'::
- Write a set of patterns to the sparse-checkout file, as given as
- a list of arguments following the 'set' subcommand. Update the
- working directory to match the new patterns. Enable the
- core.sparseCheckout config setting if it is not already enabled.
-+
-When the `--stdin` option is provided, the patterns are read from
-standard in as a newline-delimited list instead of from the arguments.
-+
-When `core.sparseCheckoutCone` is enabled, the input list is considered a
-list of directories instead of sparse-checkout patterns. The command writes
-patterns to the sparse-checkout file to include all files contained in those
-directories (recursively) as well as files that are siblings of ancestor
-directories. The input format matches the output of `git ls-tree --name-only`.
-This includes interpreting pathnames that begin with a double quote (") as
-C-style quoted strings.
-
'add'::
Update the sparse-checkout file to include additional patterns.
By default, these patterns are read from the command-line arguments,
@@ -93,12 +84,35 @@
cases, it can make sense to run `git sparse-checkout reapply` later
after cleaning up affected paths (e.g. resolving conflicts, undoing
or committing changes, etc.).
++
+The `reapply` command can also take `--[no-]cone` and `--[no-]sparse-index`
+flags, with the same meaning as the flags from the `set` command, in order
+to change which sparsity mode you are using without needing to also respecify
+all sparsity paths.
'disable'::
Disable the `core.sparseCheckout` config setting, and restore the
- working directory to include all files. Leaves the sparse-checkout
- file intact so a later 'git sparse-checkout init' command may
- return the working directory to the same state.
+ working directory to include all files.
+
+'init'::
+ Deprecated command that behaves like `set` with no specified paths.
+ May be removed in the future.
++
+Historically, `set` did not handle all the necessary config settings,
+which meant that both `init` and `set` had to be called. Invoking
+both meant the `init` step would first remove nearly all tracked files
+(and in cone mode, ignored files too), then the `set` step would add
+many of the tracked files (but not ignored files) back. In addition
+to the lost files, the performance and UI of this combination was
+poor.
++
+Also, historically, `init` would not actually initialize the
+sparse-checkout file if it already existed. This meant it was
+possible to return to a sparse-checkout without remembering which
+paths to pass to a subsequent 'set' or 'add' command. However,
+`--cone` and `--sparse-index` options would not be remembered across
+the disable command, so the easy restore of calling a plain `init`
+decreased in utility.
SPARSE CHECKOUT
---------------
@@ -107,7 +121,7 @@
It uses the skip-worktree bit (see linkgit:git-update-index[1]) to tell
Git whether a file in the working directory is worth looking at. If
the skip-worktree bit is set, then the file is ignored in the working
-directory. Git will not populate the contents of those files, which
+directory. Git will avoid populating the contents of those files, which
makes a sparse checkout helpful when working in a repository with many
files, but only a few are important to the current user.
@@ -117,10 +131,8 @@
on this file. The files matching the patterns in the file will
appear in the working directory, and the rest will not.
-To enable the sparse-checkout feature, run `git sparse-checkout init` to
-initialize a simple sparse-checkout file and enable the `core.sparseCheckout`
-config setting. Then, run `git sparse-checkout set` to modify the patterns in
-the sparse-checkout file.
+To enable the sparse-checkout feature, run `git sparse-checkout set` to
+set the patterns you want to use.
To repopulate the working directory with all files, use the
`git sparse-checkout disable` command.
diff --git a/Documentation/git-stage.txt b/Documentation/git-stage.txt
index 25bcda9..2f6aaa7 100644
--- a/Documentation/git-stage.txt
+++ b/Documentation/git-stage.txt
@@ -9,7 +9,7 @@
SYNOPSIS
--------
[verse]
-'git stage' args...
+'git stage' <arg>...
DESCRIPTION
diff --git a/Documentation/git-stash.txt b/Documentation/git-stash.txt
index be6084c..6e15f47 100644
--- a/Documentation/git-stash.txt
+++ b/Documentation/git-stash.txt
@@ -13,7 +13,7 @@
'git stash' drop [-q|--quiet] [<stash>]
'git stash' ( pop | apply ) [--index] [-q|--quiet] [<stash>]
'git stash' branch <branchname> [<stash>]
-'git stash' [push [-p|--patch] [-k|--[no-]keep-index] [-q|--quiet]
+'git stash' [push [-p|--patch] [-S|--staged] [-k|--[no-]keep-index] [-q|--quiet]
[-u|--include-untracked] [-a|--all] [-m|--message <message>]
[--pathspec-from-file=<file> [--pathspec-file-nul]]
[--] [<pathspec>...]]
@@ -47,7 +47,7 @@
COMMANDS
--------
-push [-p|--patch] [-k|--[no-]keep-index] [-u|--include-untracked] [-a|--all] [-q|--quiet] [-m|--message <message>] [--pathspec-from-file=<file> [--pathspec-file-nul]] [--] [<pathspec>...]::
+push [-p|--patch] [-S|--staged] [-k|--[no-]keep-index] [-u|--include-untracked] [-a|--all] [-q|--quiet] [-m|--message <message>] [--pathspec-from-file=<file> [--pathspec-file-nul]] [--] [<pathspec>...]::
Save your local modifications to a new 'stash entry' and roll them
back to HEAD (in the working tree and in the index).
@@ -60,7 +60,7 @@
are `stash -p` which acts as alias for `stash push -p` and pathspec elements,
which are allowed after a double hyphen `--` for disambiguation.
-save [-p|--patch] [-k|--[no-]keep-index] [-u|--include-untracked] [-a|--all] [-q|--quiet] [<message>]::
+save [-p|--patch] [-S|--staged] [-k|--[no-]keep-index] [-u|--include-untracked] [-a|--all] [-q|--quiet] [<message>]::
This option is deprecated in favour of 'git stash push'. It
differs from "stash push" in that it cannot take pathspec.
@@ -205,6 +205,16 @@
The `--patch` option implies `--keep-index`. You can use
`--no-keep-index` to override this.
+-S::
+--staged::
+ This option is only valid for `push` and `save` commands.
++
+Stash only the changes that are currently staged. This is similar to
+basic `git commit` except the state is committed to the stash instead
+of current branch.
++
+The `--patch` option has priority over this one.
+
--pathspec-from-file=<file>::
This option is only valid for `push` command.
+
@@ -341,6 +351,24 @@
$ git commit foo -m 'Remaining parts'
----------------------------------------------------------------
+Saving unrelated changes for future use::
+
+When you are in the middle of massive changes and you find some
+unrelated issue that you don't want to forget to fix, you can do the
+change(s), stage them, and use `git stash push --staged` to stash them
+out for future use. This is similar to committing the staged changes,
+only the commit ends-up being in the stash and not on the current branch.
++
+----------------------------------------------------------------
+# ... hack hack hack ...
+$ git add --patch foo # add unrelated changes to the index
+$ git stash push --staged # save these changes to the stash
+# ... hack hack hack, finish curent changes ...
+$ git commit -m 'Massive' # commit fully tested changes
+$ git switch fixup-branch # switch to another branch
+$ git stash pop # to finish work on the saved changes
+----------------------------------------------------------------
+
Recovering stash entries that were cleared/dropped erroneously::
If you mistakenly drop or clear stash entries, they cannot be recovered
diff --git a/Documentation/git-status.txt b/Documentation/git-status.txt
index 4a2c3e0..54a4b29 100644
--- a/Documentation/git-status.txt
+++ b/Documentation/git-status.txt
@@ -314,6 +314,14 @@
------------------------------------------------------------
....
+Stash Information
+^^^^^^^^^^^^^^^^^
+
+If `--show-stash` is given, one line is printed showing the number of stash
+entries if non-zero:
+
+ # stash <N>
+
Changed Tracked Entries
^^^^^^^^^^^^^^^^^^^^^^^
diff --git a/Documentation/git-svn.txt b/Documentation/git-svn.txt
index 222b556..4e92308 100644
--- a/Documentation/git-svn.txt
+++ b/Documentation/git-svn.txt
@@ -575,7 +575,7 @@
-------
--shared[=(false|true|umask|group|all|world|everybody)]::
---template=<template_directory>::
+--template=<template-directory>::
Only used with the 'init' command.
These are passed directly to 'git init'.
diff --git a/Documentation/git-switch.txt b/Documentation/git-switch.txt
index 5c438cd..bbcbdce 100644
--- a/Documentation/git-switch.txt
+++ b/Documentation/git-switch.txt
@@ -137,8 +137,7 @@
The same as `--merge` option above, but changes the way the
conflicting hunks are presented, overriding the
`merge.conflictStyle` configuration variable. Possible values are
- "merge" (default) and "diff3" (in addition to what is shown by
- "merge" style, shows the original contents).
+ "merge" (default), "diff3", and "zdiff3".
-q::
--quiet::
@@ -152,7 +151,7 @@
attached to a terminal, regardless of `--quiet`.
-t::
---track::
+--track [direct|inherit]::
When creating a new branch, set up "upstream" configuration.
`-c` is implied. See `--track` in linkgit:git-branch[1] for
details.
diff --git a/Documentation/git-var.txt b/Documentation/git-var.txt
index 6072f93..387cc1b 100644
--- a/Documentation/git-var.txt
+++ b/Documentation/git-var.txt
@@ -59,6 +59,9 @@
The build you are using chose '{git-default-pager}' as the default.
endif::git-default-pager[]
+GIT_DEFAULT_BRANCH::
+ The name of the first branch created in newly initialized repositories.
+
SEE ALSO
--------
linkgit:git-commit-tree[1]
diff --git a/Documentation/git-web--browse.txt b/Documentation/git-web--browse.txt
index 8d162b5..f2f996c 100644
--- a/Documentation/git-web--browse.txt
+++ b/Documentation/git-web--browse.txt
@@ -8,7 +8,7 @@
SYNOPSIS
--------
[verse]
-'git web{litdd}browse' [<options>] <url|file>...
+'git web{litdd}browse' [<options>] (<URL>|<file>)...
DESCRIPTION
-----------
diff --git a/Documentation/git-worktree.txt b/Documentation/git-worktree.txt
index 8a7cbdd..9e862fb 100644
--- a/Documentation/git-worktree.txt
+++ b/Documentation/git-worktree.txt
@@ -10,7 +10,7 @@
--------
[verse]
'git worktree add' [-f] [--detach] [--checkout] [--lock [--reason <string>]] [-b <new-branch>] <path> [<commit-ish>]
-'git worktree list' [--porcelain]
+'git worktree list' [-v | --porcelain]
'git worktree lock' [--reason <string>] <worktree>
'git worktree move' <worktree> <new-path>
'git worktree prune' [-n] [-v] [--expire <expire>]
diff --git a/Documentation/git.txt b/Documentation/git.txt
index 281c5f8..13f83a2 100644
--- a/Documentation/git.txt
+++ b/Documentation/git.txt
@@ -832,8 +832,9 @@
`GIT_TRACE_REDACT`::
By default, when tracing is activated, Git redacts the values of
- cookies, the "Authorization:" header, and the "Proxy-Authorization:"
- header. Set this variable to `0` to prevent this redaction.
+ cookies, the "Authorization:" header, the "Proxy-Authorization:"
+ header and packfile URIs. Set this variable to `0` to prevent this
+ redaction.
`GIT_LITERAL_PATHSPECS`::
Setting this variable to `1` will cause Git to treat all
diff --git a/Documentation/gitcredentials.txt b/Documentation/gitcredentials.txt
index 758bf39..80517b4 100644
--- a/Documentation/gitcredentials.txt
+++ b/Documentation/gitcredentials.txt
@@ -132,7 +132,7 @@
compares hostnames exactly, without considering whether two hosts are part of
the same domain. Likewise, a config entry for `http://example.com` would not
match: Git compares the protocols exactly. However, you may use wildcards in
-the domain name and other pattern matching techniques as with the `http.<url>.*`
+the domain name and other pattern matching techniques as with the `http.<URL>.*`
options.
If the "pattern" URL does include a path component, then this too must match
@@ -147,7 +147,7 @@
Options for a credential context can be configured either in
`credential.*` (which applies to all credentials), or
-`credential.<url>.*`, where <url> matches the context as described
+`credential.<URL>.*`, where <URL> matches the context as described
above.
The following options are available in either location:
diff --git a/Documentation/gitsubmodules.txt b/Documentation/gitsubmodules.txt
index 891c8da..941858a 100644
--- a/Documentation/gitsubmodules.txt
+++ b/Documentation/gitsubmodules.txt
@@ -226,7 +226,7 @@
----------------------------------
# Add a submodule
- git submodule add <url> <path>
+ git submodule add <URL> <path>
# Occasionally update the submodule to a new version:
git -C <path> checkout <new version>
diff --git a/Documentation/gitworkflows.txt b/Documentation/gitworkflows.txt
index 47cf97f..5930526 100644
--- a/Documentation/gitworkflows.txt
+++ b/Documentation/gitworkflows.txt
@@ -394,7 +394,7 @@
-------------------------------------
Please pull from
- <url> <branch>
+ <URL> <branch>
-------------------------------------
In that case, 'git pull' can do the fetch and merge in one go, as
@@ -403,7 +403,7 @@
.Push/pull: Merging remote topics
[caption="Recipe: "]
=====================================
-`git pull <url> <branch>`
+`git pull <URL> <branch>`
=====================================
Occasionally, the maintainer may get merge conflicts when they try to
@@ -440,7 +440,7 @@
.format-patch/am: Keeping topics up to date
[caption="Recipe: "]
=====================================
-`git pull --rebase <url> <branch>`
+`git pull --rebase <URL> <branch>`
=====================================
You can then fix the conflicts during the rebase. Presumably you have
diff --git a/Documentation/pretty-formats.txt b/Documentation/pretty-formats.txt
index ef6bd42..0b4c1c8 100644
--- a/Documentation/pretty-formats.txt
+++ b/Documentation/pretty-formats.txt
@@ -20,7 +20,7 @@
* 'oneline'
- <hash> <title line>
+ <hash> <title-line>
+
This is designed to be as compact as possible.
@@ -29,17 +29,17 @@
commit <hash>
Author: <author>
- <title line>
+ <title-line>
* 'medium'
commit <hash>
Author: <author>
- Date: <author date>
+ Date: <author-date>
- <title line>
+ <title-line>
- <full commit message>
+ <full-commit-message>
* 'full'
@@ -47,25 +47,25 @@
Author: <author>
Commit: <committer>
- <title line>
+ <title-line>
- <full commit message>
+ <full-commit-message>
* 'fuller'
commit <hash>
Author: <author>
- AuthorDate: <author date>
+ AuthorDate: <author-date>
Commit: <committer>
- CommitDate: <committer date>
+ CommitDate: <committer-date>
- <title line>
+ <title-line>
- <full commit message>
+ <full-commit-message>
* 'reference'
- <abbrev hash> (<title line>, <short author date>)
+ <abbrev-hash> (<title-line>, <short-author-date>)
+
This format is used to refer to another commit in a commit message and
is the same as `--pretty='format:%C(auto)%h (%s, %ad)'`. By default,
@@ -78,10 +78,10 @@
From <hash> <date>
From: <author>
- Date: <author date>
- Subject: [PATCH] <title line>
+ Date: <author-date>
+ Subject: [PATCH] <title-line>
- <full commit message>
+ <full-commit-message>
* 'mboxrd'
+
@@ -101,9 +101,9 @@
`git log --raw`. To get full object names in a raw diff format,
use `--no-abbrev`.
-* 'format:<string>'
+* 'format:<format-string>'
+
-The 'format:<string>' format allows you to specify which information
+The 'format:<format-string>' format allows you to specify which information
you want to show. It works a little bit like printf format,
with the notable exception that you get a newline with '%n'
instead of '\n'.
@@ -220,6 +220,12 @@
inconsistent when tags are added or removed at
the same time.
+
+** 'tags[=<bool-value>]': Instead of only considering annotated tags,
+ consider lightweight tags as well.
+** 'abbrev=<number>': Instead of using the default number of hexadecimal digits
+ (which will vary according to the number of objects in the repository with a
+ default of 7) of the abbreviated object name, use <number> digits, or as many
+ digits as needed to form a unique object name.
** 'match=<pattern>': Only consider tags matching the given
`glob(7)` pattern, excluding the "refs/tags/" prefix.
** 'exclude=<pattern>': Do not consider tags matching the given
@@ -273,12 +279,7 @@
If any option is provided multiple times the
last occurrence wins.
+
-The boolean options accept an optional value `[=<BOOL>]`. The values
-`true`, `false`, `on`, `off` etc. are all accepted. See the "boolean"
-sub-section in "EXAMPLES" in linkgit:git-config[1]. If a boolean
-option is given with no value, it's enabled.
-+
-** 'key=<K>': only show trailers with specified key. Matching is done
+** 'key=<key>': only show trailers with specified <key>. Matching is done
case-insensitively and trailing colon is optional. If option is
given multiple times trailer lines matching any of the keys are
shown. This option automatically enables the `only` option so that
@@ -286,25 +287,25 @@
desired it can be disabled with `only=false`. E.g.,
`%(trailers:key=Reviewed-by)` shows trailer lines with key
`Reviewed-by`.
-** 'only[=<BOOL>]': select whether non-trailer lines from the trailer
+** 'only[=<bool>]': select whether non-trailer lines from the trailer
block should be included.
-** 'separator=<SEP>': specify a separator inserted between trailer
+** 'separator=<sep>': specify a separator inserted between trailer
lines. When this option is not given each trailer line is
- terminated with a line feed character. The string SEP may contain
+ terminated with a line feed character. The string <sep> may contain
the literal formatting codes described above. To use comma as
separator one must use `%x2C` as it would otherwise be parsed as
next option. E.g., `%(trailers:key=Ticket,separator=%x2C )`
shows all trailer lines whose key is "Ticket" separated by a comma
and a space.
-** 'unfold[=<BOOL>]': make it behave as if interpret-trailer's `--unfold`
+** 'unfold[=<bool>]': make it behave as if interpret-trailer's `--unfold`
option was given. E.g.,
`%(trailers:only,unfold=true)` unfolds and shows all trailer lines.
-** 'keyonly[=<BOOL>]': only show the key part of the trailer.
-** 'valueonly[=<BOOL>]': only show the value part of the trailer.
-** 'key_value_separator=<SEP>': specify a separator inserted between
+** 'keyonly[=<bool>]': only show the key part of the trailer.
+** 'valueonly[=<bool>]': only show the value part of the trailer.
+** 'key_value_separator=<sep>': specify a separator inserted between
trailer lines. When this option is not given each trailer key-value
pair is separated by ": ". Otherwise it shares the same semantics
- as 'separator=<SEP>' above.
+ as 'separator=<sep>' above.
NOTE: Some placeholders may depend on other options given to the
revision traversal engine. For example, the `%g*` reflog options will
@@ -313,6 +314,11 @@
decoration format if `--decorate` was not already provided on the command
line.
+The boolean options accept an optional value `[=<bool-value>]`. The values
+`true`, `false`, `on`, `off` etc. are all accepted. See the "boolean"
+sub-section in "EXAMPLES" in linkgit:git-config[1]. If a boolean
+option is given with no value, it's enabled.
+
If you add a `+` (plus sign) after '%' of a placeholder, a line-feed
is inserted immediately before the expansion if and only if the
placeholder expands to a non-empty string.
diff --git a/Documentation/rev-list-options.txt b/Documentation/rev-list-options.txt
index 24569b0..43a86fa 100644
--- a/Documentation/rev-list-options.txt
+++ b/Documentation/rev-list-options.txt
@@ -1047,7 +1047,7 @@
has no effect.
`--date=format:...` feeds the format `...` to your system `strftime`,
-except for %z and %Z, which are handled internally.
+except for %s, %z, and %Z, which are handled internally.
Use `--date=format:%c` to show the date in your system locale's
preferred format. See the `strftime` manual for a complete list of
format placeholders. When using `-local`, the correct syntax is
diff --git a/Documentation/technical/multi-pack-index.txt b/Documentation/technical/multi-pack-index.txt
index 86f40f2..b39c69d 100644
--- a/Documentation/technical/multi-pack-index.txt
+++ b/Documentation/technical/multi-pack-index.txt
@@ -17,12 +17,12 @@
The multi-pack-index (MIDX for short) stores a list of objects
and their offsets into multiple packfiles. It contains:
-- A list of packfile names.
-- A sorted list of object IDs.
-- A list of metadata for the ith object ID including:
- - A value j referring to the jth packfile.
- - An offset within the jth packfile for the object.
-- If large offsets are required, we use another list of large
+* A list of packfile names.
+* A sorted list of object IDs.
+* A list of metadata for the ith object ID including:
+** A value j referring to the jth packfile.
+** An offset within the jth packfile for the object.
+* If large offsets are required, we use another list of large
offsets similar to version 2 pack-indexes.
Thus, we can provide O(log N) lookup time for any number
@@ -87,11 +87,6 @@
helpful to organize packfiles by object type (commit, tree, blob,
etc.) and use this metadata to help that maintenance.
-- The partial clone feature records special "promisor" packs that
- may point to objects that are not stored locally, but available
- on request to a server. The multi-pack-index does not currently
- track these promisor packs.
-
Related Links
-------------
[0] https://bugs.chromium.org/p/git/issues/detail?id=6
diff --git a/Documentation/technical/protocol-v2.txt b/Documentation/technical/protocol-v2.txt
index 21e8258..8a877d2 100644
--- a/Documentation/technical/protocol-v2.txt
+++ b/Documentation/technical/protocol-v2.txt
@@ -125,11 +125,11 @@
empty-request = flush-pkt
command-request = command
capability-list
- [command-args]
+ delim-pkt
+ command-args
flush-pkt
command = PKT-LINE("command=" key LF)
- command-args = delim-pkt
- *command-specific-arg
+ command-args = *command-specific-arg
command-specific-args are packet line framed arguments defined by
each individual command.
diff --git a/Documentation/technical/rerere.txt b/Documentation/technical/rerere.txt
index af5f9fc..35d4541 100644
--- a/Documentation/technical/rerere.txt
+++ b/Documentation/technical/rerere.txt
@@ -14,9 +14,9 @@
Different conflict styles and branch names are normalized by stripping
the labels from the conflict markers, and removing the common ancestor
-version from the `diff3` conflict style. Branches that are merged
-in different order are normalized by sorting the conflict hunks. More
-on each of those steps in the following sections.
+version from the `diff3` or `zdiff3` conflict styles. Branches that
+are merged in different order are normalized by sorting the conflict
+hunks. More on each of those steps in the following sections.
Once these two normalization operations are applied, a conflict ID is
calculated based on the normalized conflict, which is later used by
@@ -42,8 +42,8 @@
>>>>>>> AC
Doing the analogous with AC2 (forking a branch ABAC2 off of branch AB
-and then merging branch AC2 into it), using the diff3 conflict style,
-we get a conflict like the following:
+and then merging branch AC2 into it), using the diff3 or zdiff3
+conflict style, we get a conflict like the following:
<<<<<<< HEAD
B
diff --git a/Documentation/urls-remotes.txt b/Documentation/urls-remotes.txt
index bd184cd..86d0008 100644
--- a/Documentation/urls-remotes.txt
+++ b/Documentation/urls-remotes.txt
@@ -26,14 +26,14 @@
------------
[remote "<name>"]
- url = <url>
+ url = <URL>
pushurl = <pushurl>
push = <refspec>
fetch = <refspec>
------------
The `<pushurl>` is used for pushes only. It is optional and defaults
-to `<url>`.
+to `<URL>`.
Named file in `$GIT_DIR/remotes`
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@@ -67,10 +67,10 @@
------------
- <url>#<head>
+ <URL>#<head>
------------
-`<url>` is required; `#<head>` is optional.
+`<URL>` is required; `#<head>` is optional.
Depending on the operation, git will use one of the following
refspecs, if you don't provide one on the command line.
diff --git a/GIT-VERSION-GEN b/GIT-VERSION-GEN
index 3ae7610..002b1d7 100755
--- a/GIT-VERSION-GEN
+++ b/GIT-VERSION-GEN
@@ -1,7 +1,7 @@
#!/bin/sh
GVF=GIT-VERSION-FILE
-DEF_VER=v2.34.3
+DEF_VER=v2.35.3
LF='
'
diff --git a/Makefile b/Makefile
index 12be39a..5580859 100644
--- a/Makefile
+++ b/Makefile
@@ -256,6 +256,8 @@
#
# Define NO_DEFLATE_BOUND if your zlib does not have deflateBound.
#
+# Define NO_UNCOMPRESS2 if your zlib does not have uncompress2.
+#
# Define NO_NORETURN if using buggy versions of gcc 4.6+ and profile feedback,
# as the compiler can crash (http://gcc.gnu.org/bugzilla/show_bug.cgi?id=49299)
#
@@ -305,9 +307,6 @@
#
# Define NO_TCLTK if you do not want Tcl/Tk GUI.
#
-# Define SANE_TEXT_GREP to "-a" if you use recent versions of GNU grep
-# and egrep that are pickier when their input contains non-ASCII data.
-#
# The TCL_PATH variable governs the location of the Tcl interpreter
# used to optimize git-gui for your system. Only used if NO_TCLTK
# is not set. Defaults to the bare 'tclsh'.
@@ -735,6 +734,7 @@
TEST_BUILTINS_OBJS += test-read-graph.o
TEST_BUILTINS_OBJS += test-read-midx.o
TEST_BUILTINS_OBJS += test-ref-store.o
+TEST_BUILTINS_OBJS += test-reftable.o
TEST_BUILTINS_OBJS += test-regex.o
TEST_BUILTINS_OBJS += test-repository.o
TEST_BUILTINS_OBJS += test-revision-walking.o
@@ -813,6 +813,8 @@
LIB_FILE = libgit.a
XDIFF_LIB = xdiff/lib.a
+REFTABLE_LIB = reftable/libreftable.a
+REFTABLE_TEST_LIB = reftable/libreftable_test.a
GENERATED_H += command-list.h
GENERATED_H += config-list.h
@@ -1192,7 +1194,7 @@
THIRD_PARTY_SOURCES += sha1collisiondetection/%
THIRD_PARTY_SOURCES += sha1dc/%
-GITLIBS = common-main.o $(LIB_FILE) $(XDIFF_LIB)
+GITLIBS = common-main.o $(LIB_FILE) $(XDIFF_LIB) $(REFTABLE_LIB)
EXTLIBS =
GIT_USER_AGENT = git/$(GIT_VERSION)
@@ -1204,6 +1206,7 @@
# Set CFLAGS, LDFLAGS and other *FLAGS variables. These might be
# tweaked by config.* below as well as the command-line, both of
# which'll override these defaults.
+# Older versions of GCC may require adding "-std=gnu99" at the end.
CFLAGS = -g -O2 -Wall
LDFLAGS =
CC_LD_DYNPATH = -Wl,-rpath,
@@ -1215,7 +1218,7 @@
PTHREAD_CFLAGS =
# For the 'sparse' target
-SPARSE_FLAGS ?=
+SPARSE_FLAGS ?= -std=gnu99
SP_EXTRA_FLAGS = -Wno-universal-initializer
# For informing GIT-BUILD-OPTIONS of the SANITIZE=leak target
@@ -1723,6 +1726,11 @@
BASIC_CFLAGS += -DNO_DEFLATE_BOUND
endif
+ifdef NO_UNCOMPRESS2
+ BASIC_CFLAGS += -DNO_UNCOMPRESS2
+ REFTABLE_OBJS += compat/zlib-uncompress2.o
+endif
+
ifdef NO_POSIX_GOODIES
BASIC_CFLAGS += -DNO_POSIX_GOODIES
endif
@@ -1873,7 +1881,7 @@
endif
ifndef NO_MSGFMT_EXTENDED_OPTIONS
- MSGFMT += --check --statistics
+ MSGFMT += --check
endif
ifdef HAVE_CLOCK_GETTIME
@@ -2104,11 +2112,6 @@
BASIC_CFLAGS += -DDEFAULT_HELP_FORMAT='"$(DEFAULT_HELP_FORMAT)"'
endif
-PAGER_ENV_SQ = $(subst ','\'',$(PAGER_ENV))
-PAGER_ENV_CQ = "$(subst ",\",$(subst \,\\,$(PAGER_ENV)))"
-PAGER_ENV_CQ_SQ = $(subst ','\'',$(PAGER_ENV_CQ))
-BASIC_CFLAGS += -DPAGER_ENV='$(PAGER_ENV_CQ_SQ)'
-
ALL_CFLAGS += $(BASIC_CFLAGS)
ALL_LDFLAGS += $(BASIC_LDFLAGS)
@@ -2215,14 +2218,20 @@
$(filter %.o,$^) $(LIBS)
help.sp help.s help.o: command-list.h
-hook.sp hook.s hook.o: hook-list.h
+builtin/bugreport.sp builtin/bugreport.s builtin/bugreport.o: hook-list.h
-builtin/help.sp builtin/help.s builtin/help.o: config-list.h hook-list.h GIT-PREFIX
+builtin/help.sp builtin/help.s builtin/help.o: config-list.h GIT-PREFIX
builtin/help.sp builtin/help.s builtin/help.o: EXTRA_CPPFLAGS = \
'-DGIT_HTML_PATH="$(htmldir_relative_SQ)"' \
'-DGIT_MAN_PATH="$(mandir_relative_SQ)"' \
'-DGIT_INFO_PATH="$(infodir_relative_SQ)"'
+PAGER_ENV_SQ = $(subst ','\'',$(PAGER_ENV))
+PAGER_ENV_CQ = "$(subst ",\",$(subst \,\\,$(PAGER_ENV)))"
+PAGER_ENV_CQ_SQ = $(subst ','\'',$(PAGER_ENV_CQ))
+pager.sp pager.s pager.o: EXTRA_CPPFLAGS = \
+ -DPAGER_ENV='$(PAGER_ENV_CQ_SQ)'
+
version.sp version.s version.o: GIT-VERSION-FILE GIT-USER-AGENT
version.sp version.s version.o: EXTRA_CPPFLAGS = \
'-DGIT_VERSION="$(GIT_VERSION)"' \
@@ -2252,25 +2261,10 @@
hook-list.h: generate-hooklist.sh Documentation/githooks.txt
$(QUIET_GEN)$(SHELL_PATH) ./generate-hooklist.sh >$@
-SCRIPT_DEFINES = $(SHELL_PATH_SQ):$(DIFF_SQ):$(GIT_VERSION):\
- $(localedir_SQ):$(NO_CURL):$(USE_GETTEXT_SCHEME):$(SANE_TOOL_PATH_SQ):\
- $(gitwebdir_SQ):$(PERL_PATH_SQ):$(SANE_TEXT_GREP):$(PAGER_ENV):\
+SCRIPT_DEFINES = $(SHELL_PATH_SQ):$(DIFF_SQ):\
+ $(localedir_SQ):$(USE_GETTEXT_SCHEME):$(SANE_TOOL_PATH_SQ):\
+ $(gitwebdir_SQ):$(PERL_PATH_SQ):$(PAGER_ENV):\
$(perllibdir_SQ)
-define cmd_munge_script
-sed -e '1s|#!.*/sh|#!$(SHELL_PATH_SQ)|' \
- -e 's|@SHELL_PATH@|$(SHELL_PATH_SQ)|' \
- -e 's|@@DIFF@@|$(DIFF_SQ)|' \
- -e 's|@@LOCALEDIR@@|$(localedir_SQ)|g' \
- -e 's/@@NO_CURL@@/$(NO_CURL)/g' \
- -e 's/@@USE_GETTEXT_SCHEME@@/$(USE_GETTEXT_SCHEME)/g' \
- -e $(BROKEN_PATH_FIX) \
- -e 's|@@GITWEBDIR@@|$(gitwebdir_SQ)|g' \
- -e 's|@@PERL@@|$(PERL_PATH_SQ)|g' \
- -e 's|@@SANE_TEXT_GREP@@|$(SANE_TEXT_GREP)|g' \
- -e 's|@@PAGER_ENV@@|$(PAGER_ENV_SQ)|g' \
- $@.sh >$@+
-endef
-
GIT-SCRIPT-DEFINES: FORCE
@FLAGS='$(SCRIPT_DEFINES)'; \
if test x"$$FLAGS" != x"`cat $@ 2>/dev/null`" ; then \
@@ -2278,6 +2272,18 @@
echo "$$FLAGS" >$@; \
fi
+define cmd_munge_script
+sed -e '1s|#!.*/sh|#!$(SHELL_PATH_SQ)|' \
+ -e 's|@SHELL_PATH@|$(SHELL_PATH_SQ)|' \
+ -e 's|@@DIFF@@|$(DIFF_SQ)|' \
+ -e 's|@@LOCALEDIR@@|$(localedir_SQ)|g' \
+ -e 's/@@USE_GETTEXT_SCHEME@@/$(USE_GETTEXT_SCHEME)/g' \
+ -e $(BROKEN_PATH_FIX) \
+ -e 's|@@GITWEBDIR@@|$(gitwebdir_SQ)|g' \
+ -e 's|@@PERL@@|$(PERL_PATH_SQ)|g' \
+ -e 's|@@PAGER_ENV@@|$(PAGER_ENV_SQ)|g' \
+ $@.sh >$@+
+endef
$(SCRIPT_SH_GEN) : % : %.sh GIT-SCRIPT-DEFINES
$(QUIET_GEN)$(cmd_munge_script) && \
@@ -2437,7 +2443,36 @@
.PHONY: xdiff-objs
xdiff-objs: $(XDIFF_OBJS)
+REFTABLE_OBJS += reftable/basics.o
+REFTABLE_OBJS += reftable/error.o
+REFTABLE_OBJS += reftable/block.o
+REFTABLE_OBJS += reftable/blocksource.o
+REFTABLE_OBJS += reftable/iter.o
+REFTABLE_OBJS += reftable/publicbasics.o
+REFTABLE_OBJS += reftable/merged.o
+REFTABLE_OBJS += reftable/pq.o
+REFTABLE_OBJS += reftable/reader.o
+REFTABLE_OBJS += reftable/record.o
+REFTABLE_OBJS += reftable/refname.o
+REFTABLE_OBJS += reftable/generic.o
+REFTABLE_OBJS += reftable/stack.o
+REFTABLE_OBJS += reftable/tree.o
+REFTABLE_OBJS += reftable/writer.o
+
+REFTABLE_TEST_OBJS += reftable/basics_test.o
+REFTABLE_TEST_OBJS += reftable/block_test.o
+REFTABLE_TEST_OBJS += reftable/dump.o
+REFTABLE_TEST_OBJS += reftable/merged_test.o
+REFTABLE_TEST_OBJS += reftable/pq_test.o
+REFTABLE_TEST_OBJS += reftable/record_test.o
+REFTABLE_TEST_OBJS += reftable/readwrite_test.o
+REFTABLE_TEST_OBJS += reftable/refname_test.o
+REFTABLE_TEST_OBJS += reftable/stack_test.o
+REFTABLE_TEST_OBJS += reftable/test_framework.o
+REFTABLE_TEST_OBJS += reftable/tree_test.o
+
TEST_OBJS := $(patsubst %$X,%.o,$(TEST_PROGRAMS)) $(patsubst %,t/helper/%,$(TEST_BUILTINS_OBJS))
+
.PHONY: test-objs
test-objs: $(TEST_OBJS)
@@ -2453,9 +2488,16 @@
OBJECTS += $(TEST_OBJS)
OBJECTS += $(XDIFF_OBJS)
OBJECTS += $(FUZZ_OBJS)
+OBJECTS += $(REFTABLE_OBJS) $(REFTABLE_TEST_OBJS)
+
ifndef NO_CURL
OBJECTS += http.o http-walker.o remote-curl.o
endif
+
+SCALAR_SOURCES := contrib/scalar/scalar.c
+SCALAR_OBJECTS := $(SCALAR_SOURCES:c=o)
+OBJECTS += $(SCALAR_OBJECTS)
+
.PHONY: objects
objects: $(OBJECTS)
@@ -2589,12 +2631,22 @@
$(QUIET_LINK)$(CC) $(ALL_CFLAGS) -o $@ $(ALL_LDFLAGS) $(filter %.o,$^) \
$(CURL_LIBCURL) $(EXPAT_LIBEXPAT) $(LIBS)
+contrib/scalar/scalar$X: $(SCALAR_OBJECTS) GIT-LDFLAGS $(GITLIBS)
+ $(QUIET_LINK)$(CC) $(ALL_CFLAGS) -o $@ $(ALL_LDFLAGS) \
+ $(filter %.o,$^) $(LIBS)
+
$(LIB_FILE): $(LIB_OBJS)
$(QUIET_AR)$(RM) $@ && $(AR) $(ARFLAGS) $@ $^
$(XDIFF_LIB): $(XDIFF_OBJS)
$(QUIET_AR)$(RM) $@ && $(AR) $(ARFLAGS) $@ $^
+$(REFTABLE_LIB): $(REFTABLE_OBJS)
+ $(QUIET_AR)$(RM) $@ && $(AR) $(ARFLAGS) $@ $^
+
+$(REFTABLE_TEST_LIB): $(REFTABLE_TEST_OBJS)
+ $(QUIET_AR)$(RM) $@ && $(AR) $(ARFLAGS) $@ $^
+
export DEFAULT_EDITOR DEFAULT_PAGER
Documentation/GIT-EXCLUDED-PROGRAMS: FORCE
@@ -2893,7 +2945,7 @@
t/helper/test-tool$X: $(patsubst %,t/helper/%,$(TEST_BUILTINS_OBJS))
-t/helper/test-%$X: t/helper/test-%.o GIT-LDFLAGS $(GITLIBS)
+t/helper/test-%$X: t/helper/test-%.o GIT-LDFLAGS $(GITLIBS) $(REFTABLE_TEST_LIB)
$(QUIET_LINK)$(CC) $(ALL_CFLAGS) -o $@ $(ALL_LDFLAGS) $(filter %.o,$^) $(filter %.a,$^) $(LIBS)
check-sha1:: t/helper/test-tool$X
@@ -3231,7 +3283,7 @@
clean: profile-clean coverage-clean cocciclean
$(RM) *.res
$(RM) $(OBJECTS)
- $(RM) $(LIB_FILE) $(XDIFF_LIB)
+ $(RM) $(LIB_FILE) $(XDIFF_LIB) $(REFTABLE_LIB) $(REFTABLE_TEST_LIB)
$(RM) $(ALL_PROGRAMS) $(SCRIPT_LIB) $(BUILT_INS) git$X
$(RM) $(TEST_PROGRAMS)
$(RM) $(FUZZ_PROGRAMS)
diff --git a/README.md b/README.md
index eb8115e..f6f43e7 100644
--- a/README.md
+++ b/README.md
@@ -1,4 +1,4 @@
-[![Build status](https://github.com/git/git/workflows/CI/PR/badge.svg)](https://github.com/git/git/actions?query=branch%3Amaster+event%3Apush)
+[![Build status](https://github.com/git/git/workflows/CI/badge.svg)](https://github.com/git/git/actions?query=branch%3Amaster+event%3Apush)
Git - fast, scalable, distributed revision control system
=========================================================
diff --git a/RelNotes b/RelNotes
index 1065723..e271ea5 120000
--- a/RelNotes
+++ b/RelNotes
@@ -1 +1 @@
-Documentation/RelNotes/2.34.3.txt
\ No newline at end of file
+Documentation/RelNotes/2.35.3.txt
\ No newline at end of file
diff --git a/add-patch.c b/add-patch.c
index 8c41cdf..573eef0 100644
--- a/add-patch.c
+++ b/add-patch.c
@@ -413,7 +413,7 @@
strvec_push(&args, ps->items[i].original);
setup_child_process(s, &cp, NULL);
- cp.argv = args.v;
+ strvec_pushv(&cp.args, args.v);
res = capture_command(&cp, plain, 0);
if (res) {
strvec_clear(&args);
@@ -431,7 +431,7 @@
setup_child_process(s, &colored_cp, NULL);
xsnprintf((char *)args.v[color_arg_index], 8, "--color");
- colored_cp.argv = args.v;
+ strvec_pushv(&colored_cp.args, args.v);
colored = &s->colored;
res = capture_command(&colored_cp, colored, 0);
strvec_clear(&args);
diff --git a/apply.c b/apply.c
index 43a0aeb..7ffadc3 100644
--- a/apply.c
+++ b/apply.c
@@ -133,10 +133,10 @@
int is_not_gitdir = !startup_info->have_repository;
if (state->apply_with_reject && state->threeway)
- return error(_("--reject and --3way cannot be used together."));
+ return error(_("options '%s' and '%s' cannot be used together"), "--reject", "--3way");
if (state->threeway) {
if (is_not_gitdir)
- return error(_("--3way outside a repository"));
+ return error(_("'%s' outside a repository"), "--3way");
state->check_index = 1;
}
if (state->apply_with_reject) {
@@ -147,10 +147,10 @@
if (!force_apply && (state->diffstat || state->numstat || state->summary || state->check || state->fake_ancestor))
state->apply = 0;
if (state->check_index && is_not_gitdir)
- return error(_("--index outside a repository"));
+ return error(_("'%s' outside a repository"), "--index");
if (state->cached) {
if (is_not_gitdir)
- return error(_("--cached outside a repository"));
+ return error(_("'%s' outside a repository"), "--cached");
state->check_index = 1;
}
if (state->ita_only && (state->check_index || is_not_gitdir))
@@ -3582,7 +3582,9 @@
/* No point falling back to 3-way merge in these cases */
if (patch->is_delete ||
- S_ISGITLINK(patch->old_mode) || S_ISGITLINK(patch->new_mode))
+ S_ISGITLINK(patch->old_mode) || S_ISGITLINK(patch->new_mode) ||
+ (patch->is_new && !patch->direct_to_threeway) ||
+ (patch->is_rename && !patch->lines_added && !patch->lines_deleted))
return -1;
/* Preimage the patch was prepared for */
@@ -4752,8 +4754,10 @@
}
if (!list && !skipped_patch) {
- error(_("unrecognized input"));
- res = -128;
+ if (!state->allow_empty) {
+ error(_("No valid patches in input (allow with \"--allow-empty\")"));
+ res = -128;
+ }
goto end;
}
@@ -5071,7 +5075,7 @@
N_("leave the rejected hunks in corresponding *.rej files")),
OPT_BOOL(0, "allow-overlap", &state->allow_overlap,
N_("allow overlapping hunks")),
- OPT__VERBOSE(&state->apply_verbosity, N_("be verbose")),
+ OPT__VERBOSITY(&state->apply_verbosity),
OPT_BIT(0, "inaccurate-eof", options,
N_("tolerate incorrectly detected missing new-line at the end of file"),
APPLY_OPT_INACCURATE_EOF),
@@ -5081,6 +5085,8 @@
OPT_CALLBACK(0, "directory", state, N_("root"),
N_("prepend <root> to all filenames"),
apply_option_parse_directory),
+ OPT_BOOL(0, "allow-empty", &state->allow_empty,
+ N_("don't return error for empty patches")),
OPT_END()
};
diff --git a/apply.h b/apply.h
index da3d95f..16202da 100644
--- a/apply.h
+++ b/apply.h
@@ -66,6 +66,7 @@
int threeway;
int unidiff_zero;
int unsafe_paths;
+ int allow_empty;
/* Other non boolean parameters */
struct repository *repo;
diff --git a/archive-tar.c b/archive-tar.c
index 05d2455..3c74db1 100644
--- a/archive-tar.c
+++ b/archive-tar.c
@@ -430,7 +430,6 @@
{
struct strbuf cmd = STRBUF_INIT;
struct child_process filter = CHILD_PROCESS_INIT;
- const char *argv[2];
int r;
if (!ar->data)
@@ -440,14 +439,12 @@
if (args->compression_level >= 0)
strbuf_addf(&cmd, " -%d", args->compression_level);
- argv[0] = cmd.buf;
- argv[1] = NULL;
- filter.argv = argv;
+ strvec_push(&filter.args, cmd.buf);
filter.use_shell = 1;
filter.in = -1;
if (start_command(&filter) < 0)
- die_errno(_("unable to start '%s' filter"), argv[0]);
+ die_errno(_("unable to start '%s' filter"), cmd.buf);
close(1);
if (dup2(filter.in, 1) < 0)
die_errno(_("unable to redirect descriptor"));
@@ -457,7 +454,7 @@
close(1);
if (finish_command(&filter) != 0)
- die(_("'%s' filter reported error"), argv[0]);
+ die(_("'%s' filter reported error"), cmd.buf);
strbuf_release(&cmd);
return r;
diff --git a/archive.c b/archive.c
index a3bbb09..d571249 100644
--- a/archive.c
+++ b/archive.c
@@ -185,7 +185,7 @@
buffer = object_file_to_archive(args, path.buf, oid, mode, &type, &size);
if (!buffer)
- return error(_("cannot read %s"), oid_to_hex(oid));
+ return error(_("cannot read '%s'"), oid_to_hex(oid));
err = write_entry(args, oid, path.buf, path.len, mode, buffer, size);
free(buffer);
return err;
@@ -338,7 +338,7 @@
strbuf_reset(&content);
if (strbuf_read_file(&content, path, info->stat.st_size) < 0)
- err = error_errno(_("could not read '%s'"), path);
+ err = error_errno(_("cannot read '%s'"), path);
else
err = write_entry(args, &fake_oid, path_in_archive.buf,
path_in_archive.len,
@@ -577,11 +577,11 @@
if (remote)
die(_("Unexpected option --remote"));
if (exec)
- die(_("Option --exec can only be used together with --remote"));
+ die(_("the option '%s' requires '%s'"), "--exec", "--remote");
if (output)
die(_("Unexpected option --output"));
if (is_remote && args->extra_files.nr)
- die(_("Options --add-file and --remote cannot be used together"));
+ die(_("options '%s' and '%s' cannot be used together"), "--add-file", "--remote");
if (!base)
base = "";
diff --git a/branch.c b/branch.c
index 07a4643..5d20a2e 100644
--- a/branch.c
+++ b/branch.c
@@ -11,7 +11,7 @@
struct tracking {
struct refspec_item spec;
- char *src;
+ struct string_list *srcs;
const char *remote;
int matches;
};
@@ -22,11 +22,11 @@
if (!remote_find_tracking(remote, &tracking->spec)) {
if (++tracking->matches == 1) {
- tracking->src = tracking->spec.src;
+ string_list_append(tracking->srcs, tracking->spec.src);
tracking->remote = remote->name;
} else {
free(tracking->spec.src);
- FREE_AND_NULL(tracking->src);
+ string_list_clear(tracking->srcs, 0);
}
tracking->spec.src = NULL;
}
@@ -49,25 +49,46 @@
return 0;
}
-static const char tracking_advice[] =
-N_("\n"
-"After fixing the error cause you may try to fix up\n"
-"the remote tracking information by invoking\n"
-"\"git branch --set-upstream-to=%s%s%s\".");
-
-int install_branch_config(int flag, const char *local, const char *origin, const char *remote)
+/**
+ * Install upstream tracking configuration for a branch; specifically, add
+ * `branch.<name>.remote` and `branch.<name>.merge` entries.
+ *
+ * `flag` contains integer flags for options; currently only
+ * BRANCH_CONFIG_VERBOSE is checked.
+ *
+ * `local` is the name of the branch whose configuration we're installing.
+ *
+ * `origin` is the name of the remote owning the upstream branches. NULL means
+ * the upstream branches are local to this repo.
+ *
+ * `remotes` is a list of refs that are upstream of local
+ */
+static int install_branch_config_multiple_remotes(int flag, const char *local,
+ const char *origin, struct string_list *remotes)
{
const char *shortname = NULL;
struct strbuf key = STRBUF_INIT;
+ struct string_list_item *item;
int rebasing = should_setup_rebase(origin);
- if (skip_prefix(remote, "refs/heads/", &shortname)
- && !strcmp(local, shortname)
- && !origin) {
- warning(_("Not setting branch %s as its own upstream."),
- local);
- return 0;
- }
+ if (!remotes->nr)
+ BUG("must provide at least one remote for branch config");
+ if (rebasing && remotes->nr > 1)
+ die(_("cannot inherit upstream tracking configuration of "
+ "multiple refs when rebasing is requested"));
+
+ /*
+ * If the new branch is trying to track itself, something has gone
+ * wrong. Warn the user and don't proceed any further.
+ */
+ if (!origin)
+ for_each_string_list_item(item, remotes)
+ if (skip_prefix(item->string, "refs/heads/", &shortname)
+ && !strcmp(local, shortname)) {
+ warning(_("not setting branch '%s' as its own upstream"),
+ local);
+ return 0;
+ }
strbuf_addf(&key, "branch.%s.remote", local);
if (git_config_set_gently(key.buf, origin ? origin : ".") < 0)
@@ -75,8 +96,17 @@
strbuf_reset(&key);
strbuf_addf(&key, "branch.%s.merge", local);
- if (git_config_set_gently(key.buf, remote) < 0)
+ /*
+ * We want to overwrite any existing config with all the branches in
+ * "remotes". Override any existing config, then write our branches. If
+ * more than one is provided, use CONFIG_REGEX_NONE to preserve what
+ * we've written so far.
+ */
+ if (git_config_set_gently(key.buf, NULL) < 0)
goto out_err;
+ for_each_string_list_item(item, remotes)
+ if (git_config_set_multivar_gently(key.buf, item->string, CONFIG_REGEX_NONE, 0) < 0)
+ goto out_err;
if (rebasing) {
strbuf_reset(&key);
@@ -87,45 +117,106 @@
strbuf_release(&key);
if (flag & BRANCH_CONFIG_VERBOSE) {
- if (shortname) {
- if (origin)
- printf_ln(rebasing ?
- _("Branch '%s' set up to track remote branch '%s' from '%s' by rebasing.") :
- _("Branch '%s' set up to track remote branch '%s' from '%s'."),
- local, shortname, origin);
- else
- printf_ln(rebasing ?
- _("Branch '%s' set up to track local branch '%s' by rebasing.") :
- _("Branch '%s' set up to track local branch '%s'."),
- local, shortname);
- } else {
- if (origin)
- printf_ln(rebasing ?
- _("Branch '%s' set up to track remote ref '%s' by rebasing.") :
- _("Branch '%s' set up to track remote ref '%s'."),
- local, remote);
- else
- printf_ln(rebasing ?
- _("Branch '%s' set up to track local ref '%s' by rebasing.") :
- _("Branch '%s' set up to track local ref '%s'."),
- local, remote);
+ struct strbuf tmp_ref_name = STRBUF_INIT;
+ struct string_list friendly_ref_names = STRING_LIST_INIT_DUP;
+
+ for_each_string_list_item(item, remotes) {
+ shortname = item->string;
+ skip_prefix(shortname, "refs/heads/", &shortname);
+ if (origin) {
+ strbuf_addf(&tmp_ref_name, "%s/%s",
+ origin, shortname);
+ string_list_append_nodup(
+ &friendly_ref_names,
+ strbuf_detach(&tmp_ref_name, NULL));
+ } else {
+ string_list_append(
+ &friendly_ref_names, shortname);
+ }
}
+
+ if (remotes->nr == 1) {
+ /*
+ * Rebasing is only allowed in the case of a single
+ * upstream branch.
+ */
+ printf_ln(rebasing ?
+ _("branch '%s' set up to track '%s' by rebasing.") :
+ _("branch '%s' set up to track '%s'."),
+ local, friendly_ref_names.items[0].string);
+ } else {
+ printf_ln(_("branch '%s' set up to track:"), local);
+ for_each_string_list_item(item, &friendly_ref_names)
+ printf_ln(" %s", item->string);
+ }
+
+ string_list_clear(&friendly_ref_names, 0);
}
return 0;
out_err:
strbuf_release(&key);
- error(_("Unable to write upstream branch configuration"));
+ error(_("unable to write upstream branch configuration"));
- advise(_(tracking_advice),
- origin ? origin : "",
- origin ? "/" : "",
- shortname ? shortname : remote);
+ advise(_("\nAfter fixing the error cause you may try to fix up\n"
+ "the remote tracking information by invoking:"));
+ if (remotes->nr == 1)
+ advise(" git branch --set-upstream-to=%s%s%s",
+ origin ? origin : "",
+ origin ? "/" : "",
+ remotes->items[0].string);
+ else {
+ advise(" git config --add branch.\"%s\".remote %s",
+ local, origin ? origin : ".");
+ for_each_string_list_item(item, remotes)
+ advise(" git config --add branch.\"%s\".merge %s",
+ local, item->string);
+ }
return -1;
}
+int install_branch_config(int flag, const char *local, const char *origin,
+ const char *remote)
+{
+ int ret;
+ struct string_list remotes = STRING_LIST_INIT_DUP;
+
+ string_list_append(&remotes, remote);
+ ret = install_branch_config_multiple_remotes(flag, local, origin, &remotes);
+ string_list_clear(&remotes, 0);
+ return ret;
+}
+
+static int inherit_tracking(struct tracking *tracking, const char *orig_ref)
+{
+ const char *bare_ref;
+ struct branch *branch;
+ int i;
+
+ bare_ref = orig_ref;
+ skip_prefix(orig_ref, "refs/heads/", &bare_ref);
+
+ branch = branch_get(bare_ref);
+ if (!branch->remote_name) {
+ warning(_("asked to inherit tracking from '%s', but no remote is set"),
+ bare_ref);
+ return -1;
+ }
+
+ if (branch->merge_nr < 1 || !branch->merge_name || !branch->merge_name[0]) {
+ warning(_("asked to inherit tracking from '%s', but no merge configuration is set"),
+ bare_ref);
+ return -1;
+ }
+
+ tracking->remote = xstrdup(branch->remote_name);
+ for (i = 0; i < branch->merge_nr; i++)
+ string_list_append(tracking->srcs, branch->merge_name[i]);
+ return 0;
+}
+
/*
* This is called when new_ref is branched off of orig_ref, and tries
* to infer the settings for branch.<new_ref>.{remote,merge} from the
@@ -135,11 +226,15 @@
enum branch_track track, int quiet)
{
struct tracking tracking;
+ struct string_list tracking_srcs = STRING_LIST_INIT_DUP;
int config_flags = quiet ? 0 : BRANCH_CONFIG_VERBOSE;
memset(&tracking, 0, sizeof(tracking));
tracking.spec.dst = (char *)orig_ref;
- if (for_each_remote(find_tracked_branch, &tracking))
+ tracking.srcs = &tracking_srcs;
+ if (track != BRANCH_TRACK_INHERIT)
+ for_each_remote(find_tracked_branch, &tracking);
+ else if (inherit_tracking(&tracking, orig_ref))
return;
if (!tracking.matches)
@@ -147,20 +242,23 @@
case BRANCH_TRACK_ALWAYS:
case BRANCH_TRACK_EXPLICIT:
case BRANCH_TRACK_OVERRIDE:
+ case BRANCH_TRACK_INHERIT:
break;
default:
return;
}
if (tracking.matches > 1)
- die(_("Not tracking: ambiguous information for ref %s"),
+ die(_("not tracking: ambiguous information for ref %s"),
orig_ref);
- if (install_branch_config(config_flags, new_ref, tracking.remote,
- tracking.src ? tracking.src : orig_ref) < 0)
+ if (tracking.srcs->nr < 1)
+ string_list_append(tracking.srcs, orig_ref);
+ if (install_branch_config_multiple_remotes(config_flags, new_ref,
+ tracking.remote, tracking.srcs) < 0)
exit(-1);
- free(tracking.src);
+ string_list_clear(tracking.srcs, 0);
}
int read_branch_desc(struct strbuf *buf, const char *branch_name)
@@ -186,7 +284,7 @@
int validate_branchname(const char *name, struct strbuf *ref)
{
if (strbuf_check_branch_ref(ref, name))
- die(_("'%s' is not a valid branch name."), name);
+ die(_("'%s' is not a valid branch name"), name);
return ref_exists(ref->buf);
}
@@ -199,18 +297,23 @@
*/
int validate_new_branchname(const char *name, struct strbuf *ref, int force)
{
- const char *head;
+ struct worktree **worktrees;
+ const struct worktree *wt;
if (!validate_branchname(name, ref))
return 0;
if (!force)
- die(_("A branch named '%s' already exists."),
+ die(_("a branch named '%s' already exists"),
ref->buf + strlen("refs/heads/"));
- head = resolve_ref_unsafe("HEAD", 0, NULL, NULL);
- if (!is_bare_repository() && head && !strcmp(head, ref->buf))
- die(_("Cannot force update the current branch."));
+ worktrees = get_worktrees();
+ wt = find_shared_symref(worktrees, "HEAD", ref->buf);
+ if (wt && !wt->is_bare)
+ die(_("cannot force update the branch '%s' "
+ "checked out at '%s'"),
+ ref->buf + strlen("refs/heads/"), wt->path);
+ free_worktrees(worktrees);
return 1;
}
@@ -230,7 +333,7 @@
}
static const char upstream_not_branch[] =
-N_("Cannot setup tracking information; starting point '%s' is not a branch.");
+N_("cannot set up tracking information; starting point '%s' is not a branch");
static const char upstream_missing[] =
N_("the requested upstream branch '%s' does not exist");
static const char upstream_advice[] =
@@ -278,7 +381,7 @@
}
die(_(upstream_missing), start_name);
}
- die(_("Not a valid object name: '%s'."), start_name);
+ die(_("not a valid object name: '%s'"), start_name);
}
switch (dwim_ref(start_name, strlen(start_name), &oid, &real_ref, 0)) {
@@ -298,12 +401,12 @@
}
break;
default:
- die(_("Ambiguous object name: '%s'."), start_name);
+ die(_("ambiguous object name: '%s'"), start_name);
break;
}
if ((commit = lookup_commit_reference(r, &oid)) == NULL)
- die(_("Not a valid branch point: '%s'."), start_name);
+ die(_("not a valid branch point: '%s'"), start_name);
oidcpy(&oid, &commit->object.oid);
if (reflog)
@@ -357,14 +460,16 @@
void die_if_checked_out(const char *branch, int ignore_current_worktree)
{
+ struct worktree **worktrees = get_worktrees();
const struct worktree *wt;
- wt = find_shared_symref("HEAD", branch);
- if (!wt || (ignore_current_worktree && wt->is_current))
- return;
- skip_prefix(branch, "refs/heads/", &branch);
- die(_("'%s' is already checked out at '%s'"),
- branch, wt->path);
+ wt = find_shared_symref(worktrees, "HEAD", branch);
+ if (wt && (!ignore_current_worktree || !wt->is_current)) {
+ skip_prefix(branch, "refs/heads/", &branch);
+ die(_("'%s' is already checked out at '%s'"), branch, wt->path);
+ }
+
+ free_worktrees(worktrees);
}
int replace_each_worktree_head_symref(const char *oldref, const char *newref,
diff --git a/branch.h b/branch.h
index df0be61..815dcd4 100644
--- a/branch.h
+++ b/branch.h
@@ -10,7 +10,8 @@
BRANCH_TRACK_REMOTE,
BRANCH_TRACK_ALWAYS,
BRANCH_TRACK_EXPLICIT,
- BRANCH_TRACK_OVERRIDE
+ BRANCH_TRACK_OVERRIDE,
+ BRANCH_TRACK_INHERIT,
};
extern enum branch_track git_branch_track;
diff --git a/builtin/add.c b/builtin/add.c
index ef6b619..84dff3e 100644
--- a/builtin/add.c
+++ b/builtin/add.c
@@ -302,15 +302,11 @@
static int edit_patch(int argc, const char **argv, const char *prefix)
{
char *file = git_pathdup("ADD_EDIT.patch");
- const char *apply_argv[] = { "apply", "--recount", "--cached",
- NULL, NULL };
struct child_process child = CHILD_PROCESS_INIT;
struct rev_info rev;
int out;
struct stat st;
- apply_argv[3] = file;
-
git_config(git_diff_basic_config, NULL); /* no "diff" UI options */
if (read_cache() < 0)
@@ -338,7 +334,8 @@
die(_("Empty patch. Aborted."));
child.git_cmd = 1;
- child.argv = apply_argv;
+ strvec_pushl(&child.args, "apply", "--recount", "--cached", file,
+ NULL);
if (run_command(&child))
die(_("Could not apply '%s'"), file);
@@ -510,9 +507,9 @@
add_interactive = 1;
if (add_interactive) {
if (show_only)
- die(_("--dry-run is incompatible with --interactive/--patch"));
+ die(_("options '%s' and '%s' cannot be used together"), "--dry-run", "--interactive/--patch");
if (pathspec_from_file)
- die(_("--pathspec-from-file is incompatible with --interactive/--patch"));
+ die(_("options '%s' and '%s' cannot be used together"), "--pathspec-from-file", "--interactive/--patch");
exit(interactive_add(argv + 1, prefix, patch_interactive));
}
if (legacy_stash_p) {
@@ -529,7 +526,7 @@
if (edit_interactive) {
if (pathspec_from_file)
- die(_("--pathspec-from-file is incompatible with --edit"));
+ die(_("options '%s' and '%s' cannot be used together"), "--pathspec-from-file", "--edit");
return(edit_patch(argc, argv, prefix));
}
argc--;
@@ -541,10 +538,10 @@
addremove = 0; /* "-u" was given but not "-A" */
if (addremove && take_worktree_changes)
- die(_("-A and -u are mutually incompatible"));
+ die(_("options '%s' and '%s' cannot be used together"), "-A", "-u");
if (!show_only && ignore_missing)
- die(_("Option --ignore-missing can only be used together with --dry-run"));
+ die(_("the option '%s' requires '%s'"), "--ignore-missing", "--dry-run");
if (chmod_arg && ((chmod_arg[0] != '-' && chmod_arg[0] != '+') ||
chmod_arg[1] != 'x' || chmod_arg[2]))
@@ -569,14 +566,14 @@
if (pathspec_from_file) {
if (pathspec.nr)
- die(_("--pathspec-from-file is incompatible with pathspec arguments"));
+ die(_("'%s' and pathspec arguments cannot be used together"), "--pathspec-from-file");
parse_pathspec_file(&pathspec, PATHSPEC_ATTR,
PATHSPEC_PREFER_FULL |
PATHSPEC_SYMLINK_LEADING_PATH,
prefix, pathspec_from_file, pathspec_file_nul);
} else if (pathspec_file_nul) {
- die(_("--pathspec-file-nul requires --pathspec-from-file"));
+ die(_("the option '%s' requires '%s'"), "--pathspec-file-nul", "--pathspec-from-file");
}
if (require_pathspec && pathspec.nr == 0) {
diff --git a/builtin/am.c b/builtin/am.c
index 8677ea2..b6be1f1 100644
--- a/builtin/am.c
+++ b/builtin/am.c
@@ -87,6 +87,12 @@
SHOW_PATCH_DIFF = 1,
};
+enum empty_action {
+ STOP_ON_EMPTY_COMMIT = 0, /* output errors and stop in the middle of an am session */
+ DROP_EMPTY_COMMIT, /* skip with a notice message, unless "--quiet" has been passed */
+ KEEP_EMPTY_COMMIT, /* keep recording as empty commits */
+};
+
struct am_state {
/* state directory path */
char *dir;
@@ -118,6 +124,7 @@
int message_id;
int scissors; /* enum scissors_type */
int quoted_cr; /* enum quoted_cr_action */
+ int empty_type; /* enum empty_action */
struct strvec git_apply_opts;
const char *resolvemsg;
int committer_date_is_author_date;
@@ -178,6 +185,25 @@
return 0;
}
+static int am_option_parse_empty(const struct option *opt,
+ const char *arg, int unset)
+{
+ int *opt_value = opt->value;
+
+ BUG_ON_OPT_NEG(unset);
+
+ if (!strcmp(arg, "stop"))
+ *opt_value = STOP_ON_EMPTY_COMMIT;
+ else if (!strcmp(arg, "drop"))
+ *opt_value = DROP_EMPTY_COMMIT;
+ else if (!strcmp(arg, "keep"))
+ *opt_value = KEEP_EMPTY_COMMIT;
+ else
+ return error(_("Invalid value for --empty: %s"), arg);
+
+ return 0;
+}
+
/**
* Returns path relative to the am_state directory.
*/
@@ -1126,6 +1152,12 @@
printf_ln(_("When you have resolved this problem, run \"%s --continue\"."), cmdline);
printf_ln(_("If you prefer to skip this patch, run \"%s --skip\" instead."), cmdline);
+
+ if (advice_enabled(ADVICE_AM_WORK_DIR) &&
+ is_empty_or_missing_file(am_path(state, "patch")) &&
+ !repo_index_has_changes(the_repository, NULL, NULL))
+ printf_ln(_("To record the empty patch as an empty commit, run \"%s --allow-empty\"."), cmdline);
+
printf_ln(_("To restore the original branch and stop patching, run \"%s --abort\"."), cmdline);
}
@@ -1248,11 +1280,6 @@
goto finish;
}
- if (is_empty_or_missing_file(am_path(state, "patch"))) {
- printf_ln(_("Patch is empty."));
- die_user_resolve(state);
- }
-
strbuf_addstr(&msg, "\n\n");
strbuf_addbuf(&msg, &mi.log_message);
strbuf_stripspace(&msg, 0);
@@ -1763,6 +1790,7 @@
while (state->cur <= state->last) {
const char *mail = am_path(state, msgnum(state));
int apply_status;
+ int to_keep;
reset_ident_date();
@@ -1792,8 +1820,29 @@
if (state->interactive && do_interactive(state))
goto next;
+ to_keep = 0;
+ if (is_empty_or_missing_file(am_path(state, "patch"))) {
+ switch (state->empty_type) {
+ case DROP_EMPTY_COMMIT:
+ say(state, stdout, _("Skipping: %.*s"), linelen(state->msg), state->msg);
+ goto next;
+ break;
+ case KEEP_EMPTY_COMMIT:
+ to_keep = 1;
+ say(state, stdout, _("Creating an empty commit: %.*s"),
+ linelen(state->msg), state->msg);
+ break;
+ case STOP_ON_EMPTY_COMMIT:
+ printf_ln(_("Patch is empty."));
+ die_user_resolve(state);
+ break;
+ }
+ }
+
if (run_applypatch_msg_hook(state))
exit(1);
+ if (to_keep)
+ goto commit;
say(state, stdout, _("Applying: %.*s"), linelen(state->msg), state->msg);
@@ -1827,6 +1876,7 @@
die_user_resolve(state);
}
+commit:
do_commit(state);
next:
@@ -1856,19 +1906,24 @@
/**
* Resume the current am session after patch application failure. The user did
* all the hard work, and we do not have to do any patch application. Just
- * trust and commit what the user has in the index and working tree.
+ * trust and commit what the user has in the index and working tree. If `allow_empty`
+ * is true, commit as an empty commit when index has not changed and lacking a patch.
*/
-static void am_resolve(struct am_state *state)
+static void am_resolve(struct am_state *state, int allow_empty)
{
validate_resume_state(state);
say(state, stdout, _("Applying: %.*s"), linelen(state->msg), state->msg);
if (!repo_index_has_changes(the_repository, NULL, NULL)) {
- printf_ln(_("No changes - did you forget to use 'git add'?\n"
- "If there is nothing left to stage, chances are that something else\n"
- "already introduced the same changes; you might want to skip this patch."));
- die_user_resolve(state);
+ if (allow_empty && is_empty_or_missing_file(am_path(state, "patch"))) {
+ printf_ln(_("No changes - recorded it as an empty commit."));
+ } else {
+ printf_ln(_("No changes - did you forget to use 'git add'?\n"
+ "If there is nothing left to stage, chances are that something else\n"
+ "already introduced the same changes; you might want to skip this patch."));
+ die_user_resolve(state);
+ }
}
if (unmerged_cache()) {
@@ -2195,7 +2250,8 @@
RESUME_SKIP,
RESUME_ABORT,
RESUME_QUIT,
- RESUME_SHOW_PATCH
+ RESUME_SHOW_PATCH,
+ RESUME_ALLOW_EMPTY,
};
struct resume_mode {
@@ -2230,9 +2286,9 @@
}
if (resume->mode == RESUME_SHOW_PATCH && new_value != resume->sub_mode)
- return error(_("--show-current-patch=%s is incompatible with "
- "--show-current-patch=%s"),
- arg, valid_modes[resume->sub_mode]);
+ return error(_("options '%s=%s' and '%s=%s' "
+ "cannot be used together"),
+ "--show-current-patch", "--show-current-patch", arg, valid_modes[resume->sub_mode]);
resume->mode = RESUME_SHOW_PATCH;
resume->sub_mode = new_value;
@@ -2348,6 +2404,9 @@
N_("show the patch being applied"),
PARSE_OPT_CMDMODE | PARSE_OPT_OPTARG | PARSE_OPT_NONEG | PARSE_OPT_LITERAL_ARGHELP,
parse_opt_show_current_patch, RESUME_SHOW_PATCH },
+ OPT_CMDMODE(0, "allow-empty", &resume.mode,
+ N_("record the empty patch as an empty commit"),
+ RESUME_ALLOW_EMPTY),
OPT_BOOL(0, "committer-date-is-author-date",
&state.committer_date_is_author_date,
N_("lie about committer date")),
@@ -2357,6 +2416,9 @@
{ OPTION_STRING, 'S', "gpg-sign", &state.sign_commit, N_("key-id"),
N_("GPG-sign commits"),
PARSE_OPT_OPTARG, NULL, (intptr_t) "" },
+ OPT_CALLBACK_F(STOP_ON_EMPTY_COMMIT, "empty", &state.empty_type, "{stop,drop,keep}",
+ N_("how to handle empty patches"),
+ PARSE_OPT_NONEG, am_option_parse_empty),
OPT_HIDDEN_BOOL(0, "rebasing", &state.rebasing,
N_("(internal use for git-rebase)")),
OPT_END()
@@ -2453,7 +2515,8 @@
am_run(&state, 1);
break;
case RESUME_RESOLVED:
- am_resolve(&state);
+ case RESUME_ALLOW_EMPTY:
+ am_resolve(&state, resume.mode == RESUME_ALLOW_EMPTY ? 1 : 0);
break;
case RESUME_SKIP:
am_skip(&state);
diff --git a/builtin/blame.c b/builtin/blame.c
index f9ee3f8..7fafeac 100644
--- a/builtin/blame.c
+++ b/builtin/blame.c
@@ -939,6 +939,9 @@
revs.diffopt.flags.follow_renames = 0;
argc = parse_options_end(&ctx);
+ prepare_repo_settings(the_repository);
+ the_repository->settings.command_requires_full_index = 0;
+
if (incremental || (output_option & OUTPUT_PORCELAIN)) {
if (show_progress > 0)
die(_("--progress can't be used with --incremental or porcelain formats"));
diff --git a/builtin/branch.c b/builtin/branch.c
index 7a1d1ee..4ce2a24 100644
--- a/builtin/branch.c
+++ b/builtin/branch.c
@@ -77,12 +77,11 @@
static int git_branch_config(const char *var, const char *value, void *cb)
{
const char *slot_name;
- struct ref_sorting **sorting_tail = (struct ref_sorting **)cb;
if (!strcmp(var, "branch.sort")) {
if (!value)
return config_error_nonbool(var);
- parse_ref_sorting(sorting_tail, value);
+ string_list_append(cb, value);
return 0;
}
@@ -193,6 +192,7 @@
static int delete_branches(int argc, const char **argv, int force, int kinds,
int quiet)
{
+ struct worktree **worktrees;
struct commit *head_rev = NULL;
struct object_id oid;
char *name = NULL;
@@ -229,6 +229,9 @@
if (!head_rev)
die(_("Couldn't look up commit object for HEAD"));
}
+
+ worktrees = get_worktrees();
+
for (i = 0; i < argc; i++, strbuf_reset(&bname)) {
char *target = NULL;
int flags = 0;
@@ -239,7 +242,7 @@
if (kinds == FILTER_REFS_BRANCHES) {
const struct worktree *wt =
- find_shared_symref("HEAD", name);
+ find_shared_symref(worktrees, "HEAD", name);
if (wt) {
error(_("Cannot delete branch '%s' "
"checked out at '%s'"),
@@ -300,6 +303,7 @@
free(name);
strbuf_release(&bname);
+ free_worktrees(worktrees);
return ret;
}
@@ -625,7 +629,8 @@
enum branch_track track;
struct ref_filter filter;
int icase = 0;
- static struct ref_sorting *sorting = NULL, **sorting_tail = &sorting;
+ static struct ref_sorting *sorting;
+ struct string_list sorting_options = STRING_LIST_INIT_DUP;
struct ref_format format = REF_FORMAT_INIT;
struct option options[] = {
@@ -633,8 +638,10 @@
OPT__VERBOSE(&filter.verbose,
N_("show hash and subject, give twice for upstream branch")),
OPT__QUIET(&quiet, N_("suppress informational messages")),
- OPT_SET_INT('t', "track", &track, N_("set up tracking mode (see git-pull(1))"),
- BRANCH_TRACK_EXPLICIT),
+ OPT_CALLBACK_F('t', "track", &track, "(direct|inherit)",
+ N_("set branch tracking configuration"),
+ PARSE_OPT_OPTARG,
+ parse_opt_tracking_mode),
OPT_SET_INT_F(0, "set-upstream", &track, N_("do not use"),
BRANCH_TRACK_OVERRIDE, PARSE_OPT_HIDDEN),
OPT_STRING('u', "set-upstream-to", &new_upstream, N_("upstream"), N_("change the upstream info")),
@@ -666,7 +673,7 @@
OPT_MERGED(&filter, N_("print only branches that are merged")),
OPT_NO_MERGED(&filter, N_("print only branches that are not merged")),
OPT_COLUMN(0, "column", &colopts, N_("list branches in columns")),
- OPT_REF_SORT(sorting_tail),
+ OPT_REF_SORT(&sorting_options),
OPT_CALLBACK(0, "points-at", &filter.points_at, N_("object"),
N_("print only branches of the object"), parse_opt_object_name),
OPT_BOOL('i', "ignore-case", &icase, N_("sorting and filtering are case insensitive")),
@@ -683,7 +690,7 @@
if (argc == 2 && !strcmp(argv[1], "-h"))
usage_with_options(builtin_branch_usage, options);
- git_config(git_branch_config, sorting_tail);
+ git_config(git_branch_config, &sorting_options);
track = git_branch_track;
@@ -717,7 +724,7 @@
finalize_colopts(&colopts, -1);
if (filter.verbose) {
if (explicitly_enable_column(colopts))
- die(_("--column and --verbose are incompatible"));
+ die(_("options '%s' and '%s' cannot be used together"), "--column", "--verbose");
colopts = 0;
}
@@ -749,8 +756,7 @@
* local branches 'refs/heads/...' and finally remote-tracking
* branches 'refs/remotes/...'.
*/
- if (!sorting)
- sorting = ref_default_sorting();
+ sorting = ref_sorting_options(&sorting_options);
ref_sorting_set_sort_flags_all(sorting, REF_SORTING_ICASE, icase);
ref_sorting_set_sort_flags_all(
sorting, REF_SORTING_DETACHED_HEAD_FIRST, 1);
diff --git a/builtin/cat-file.c b/builtin/cat-file.c
index 86fc032..d94050e 100644
--- a/builtin/cat-file.c
+++ b/builtin/cat-file.c
@@ -729,7 +729,7 @@
}
if (force_path && batch.enabled) {
- error("--path=<path> incompatible with --batch");
+ error("options '--path=<path>' and '--batch' cannot be used together");
usage_with_options(cat_file_usage, options);
}
diff --git a/builtin/checkout.c b/builtin/checkout.c
index cbf73b8..cc804ba 100644
--- a/builtin/checkout.c
+++ b/builtin/checkout.c
@@ -91,8 +91,8 @@
};
struct branch_info {
- const char *name; /* The short name used */
- const char *path; /* The full name of a real branch */
+ char *name; /* The short name used */
+ char *path; /* The full name of a real branch */
struct commit *commit; /* The named commit */
char *refname; /* The full name of the ref being checked out. */
struct object_id oid; /* The object ID of the commit being checked out. */
@@ -103,6 +103,14 @@
char *checkout;
};
+static void branch_info_release(struct branch_info *info)
+{
+ free(info->name);
+ free(info->path);
+ free(info->refname);
+ free(info->checkout);
+}
+
static int post_checkout_hook(struct commit *old_commit, struct commit *new_commit,
int changed)
{
@@ -456,10 +464,10 @@
die(_("'%s' cannot be used with updating paths"), "--detach");
if (opts->merge && opts->patch_mode)
- die(_("'%s' cannot be used with %s"), "--merge", "--patch");
+ die(_("options '%s' and '%s' cannot be used together"), "--merge", "--patch");
if (opts->ignore_unmerged && opts->merge)
- die(_("'%s' cannot be used with %s"),
+ die(_("options '%s' and '%s' cannot be used together"),
opts->ignore_unmerged_opt, "-m");
if (opts->new_branch)
@@ -688,9 +696,12 @@
repo_get_oid_committish(the_repository, branch->name, &branch->oid);
strbuf_branchname(&buf, branch->name, INTERPRET_BRANCH_LOCAL);
- if (strcmp(buf.buf, branch->name))
+ if (strcmp(buf.buf, branch->name)) {
+ free(branch->name);
branch->name = xstrdup(buf.buf);
+ }
strbuf_splice(&buf, 0, 0, "refs/heads/", 11);
+ free(branch->path);
branch->path = strbuf_detach(&buf, NULL);
}
@@ -874,7 +885,7 @@
int ret;
struct strbuf err = STRBUF_INIT;
- ret = safe_create_reflog(refname, 1, &err);
+ ret = safe_create_reflog(refname, &err);
if (ret) {
fprintf(stderr, _("Can not do reflog for '%s': %s\n"),
opts->new_orphan_branch, err.buf);
@@ -894,7 +905,9 @@
opts->new_branch_log,
opts->quiet,
opts->track);
- new_branch_info->name = opts->new_branch;
+ free(new_branch_info->name);
+ free(new_branch_info->refname);
+ new_branch_info->name = xstrdup(opts->new_branch);
setup_branch_path(new_branch_info);
}
@@ -1062,8 +1075,7 @@
struct branch_info *new_branch_info)
{
int ret = 0;
- struct branch_info old_branch_info;
- void *path_to_free;
+ struct branch_info old_branch_info = { 0 };
struct object_id rev;
int flag, writeout_error = 0;
int do_merge = 1;
@@ -1071,25 +1083,29 @@
trace2_cmd_mode("branch");
memset(&old_branch_info, 0, sizeof(old_branch_info));
- old_branch_info.path = path_to_free = resolve_refdup("HEAD", 0, &rev, &flag);
+ old_branch_info.path = resolve_refdup("HEAD", 0, &rev, &flag);
if (old_branch_info.path)
old_branch_info.commit = lookup_commit_reference_gently(the_repository, &rev, 1);
if (!(flag & REF_ISSYMREF))
- old_branch_info.path = NULL;
+ FREE_AND_NULL(old_branch_info.path);
- if (old_branch_info.path)
- skip_prefix(old_branch_info.path, "refs/heads/", &old_branch_info.name);
+ if (old_branch_info.path) {
+ const char *const prefix = "refs/heads/";
+ const char *p;
+ if (skip_prefix(old_branch_info.path, prefix, &p))
+ old_branch_info.name = xstrdup(p);
+ }
if (opts->new_orphan_branch && opts->orphan_from_empty_tree) {
if (new_branch_info->name)
BUG("'switch --orphan' should never accept a commit as starting point");
new_branch_info->commit = NULL;
- new_branch_info->name = "(empty)";
+ new_branch_info->name = xstrdup("(empty)");
do_merge = 1;
}
if (!new_branch_info->name) {
- new_branch_info->name = "HEAD";
+ new_branch_info->name = xstrdup("HEAD");
new_branch_info->commit = old_branch_info.commit;
if (!new_branch_info->commit)
die(_("You are on a branch yet to be born"));
@@ -1102,7 +1118,7 @@
if (do_merge) {
ret = merge_working_tree(opts, &old_branch_info, new_branch_info, &writeout_error);
if (ret) {
- free(path_to_free);
+ branch_info_release(&old_branch_info);
return ret;
}
}
@@ -1113,7 +1129,8 @@
update_refs_for_switch(opts, &old_branch_info, new_branch_info);
ret = post_checkout_hook(old_branch_info.commit, new_branch_info->commit, 1);
- free(path_to_free);
+ branch_info_release(&old_branch_info);
+
return ret || writeout_error;
}
@@ -1145,16 +1162,15 @@
struct tree **source_tree = &opts->source_tree;
struct object_id branch_rev;
- new_branch_info->name = arg;
+ new_branch_info->name = xstrdup(arg);
setup_branch_path(new_branch_info);
if (!check_refname_format(new_branch_info->path, 0) &&
!read_ref(new_branch_info->path, &branch_rev))
oidcpy(rev, &branch_rev);
- else {
- free((char *)new_branch_info->path);
- new_branch_info->path = NULL; /* not an existing branch */
- }
+ else
+ /* not an existing branch */
+ FREE_AND_NULL(new_branch_info->path);
new_branch_info->commit = lookup_commit_reference_gently(the_repository, rev, 1);
if (!new_branch_info->commit) {
@@ -1517,7 +1533,7 @@
OPT_BOOL(0, "progress", &opts->show_progress, N_("force progress reporting")),
OPT_BOOL('m', "merge", &opts->merge, N_("perform a 3-way merge with the new branch")),
OPT_STRING(0, "conflict", &opts->conflict_style, N_("style"),
- N_("conflict style (merge or diff3)")),
+ N_("conflict style (merge, diff3, or zdiff3)")),
OPT_END()
};
struct option *newopts = parse_options_concat(prevopts, options);
@@ -1530,8 +1546,10 @@
{
struct option options[] = {
OPT_BOOL('d', "detach", &opts->force_detach, N_("detach HEAD at named commit")),
- OPT_SET_INT('t', "track", &opts->track, N_("set upstream info for new branch"),
- BRANCH_TRACK_EXPLICIT),
+ OPT_CALLBACK_F('t', "track", &opts->track, "(direct|inherit)",
+ N_("set branch tracking configuration"),
+ PARSE_OPT_OPTARG,
+ parse_opt_tracking_mode),
OPT__FORCE(&opts->force, N_("force checkout (throw away local modifications)"),
PARSE_OPT_NOCOMPLETE),
OPT_STRING(0, "orphan", &opts->new_orphan_branch, N_("new-branch"), N_("new unparented branch")),
@@ -1574,12 +1592,11 @@
static int checkout_main(int argc, const char **argv, const char *prefix,
struct checkout_opts *opts, struct option *options,
- const char * const usagestr[])
+ const char * const usagestr[],
+ struct branch_info *new_branch_info)
{
- struct branch_info new_branch_info;
int parseopt_flags = 0;
- memset(&new_branch_info, 0, sizeof(new_branch_info));
opts->overwrite_ignore = 1;
opts->prefix = prefix;
opts->show_progress = -1;
@@ -1617,11 +1634,11 @@
}
if ((!!opts->new_branch + !!opts->new_branch_force + !!opts->new_orphan_branch) > 1)
- die(_("-%c, -%c and --orphan are mutually exclusive"),
- cb_option, toupper(cb_option));
+ die(_("options '-%c', '-%c', and '%s' cannot be used together"),
+ cb_option, toupper(cb_option), "--orphan");
if (opts->overlay_mode == 1 && opts->patch_mode)
- die(_("-p and --overlay are mutually exclusive"));
+ die(_("options '%s' and '%s' cannot be used together"), "-p", "--overlay");
if (opts->checkout_index >= 0 || opts->checkout_worktree >= 0) {
if (opts->checkout_index < 0)
@@ -1688,7 +1705,7 @@
opts->track == BRANCH_TRACK_UNSPECIFIED &&
!opts->new_branch;
int n = parse_branchname_arg(argc, argv, dwim_ok,
- &new_branch_info, opts, &rev);
+ new_branch_info, opts, &rev);
argv += n;
argc -= n;
} else if (!opts->accept_ref && opts->from_treeish) {
@@ -1697,7 +1714,7 @@
if (get_oid_mb(opts->from_treeish, &rev))
die(_("could not resolve %s"), opts->from_treeish);
- setup_new_branch_info_and_source_tree(&new_branch_info,
+ setup_new_branch_info_and_source_tree(new_branch_info,
opts, &rev,
opts->from_treeish);
@@ -1717,7 +1734,7 @@
* Try to give more helpful suggestion.
* new_branch && argc > 1 will be caught later.
*/
- if (opts->new_branch && argc == 1 && !new_branch_info.commit)
+ if (opts->new_branch && argc == 1 && !new_branch_info->commit)
die(_("'%s' is not a commit and a branch '%s' cannot be created from it"),
argv[0], opts->new_branch);
@@ -1728,19 +1745,19 @@
if (opts->pathspec_from_file) {
if (opts->pathspec.nr)
- die(_("--pathspec-from-file is incompatible with pathspec arguments"));
+ die(_("'%s' and pathspec arguments cannot be used together"), "--pathspec-from-file");
if (opts->force_detach)
- die(_("--pathspec-from-file is incompatible with --detach"));
+ die(_("options '%s' and '%s' cannot be used together"), "--pathspec-from-file", "--detach");
if (opts->patch_mode)
- die(_("--pathspec-from-file is incompatible with --patch"));
+ die(_("options '%s' and '%s' cannot be used together"), "--pathspec-from-file", "--patch");
parse_pathspec_file(&opts->pathspec, 0,
0,
prefix, opts->pathspec_from_file, opts->pathspec_file_nul);
} else if (opts->pathspec_file_nul) {
- die(_("--pathspec-file-nul requires --pathspec-from-file"));
+ die(_("the option '%s' requires '%s'"), "--pathspec-file-nul", "--pathspec-from-file");
}
opts->pathspec.recursive = 1;
@@ -1766,11 +1783,10 @@
strbuf_release(&buf);
}
- UNLEAK(opts);
if (opts->patch_mode || opts->pathspec.nr)
- return checkout_paths(opts, &new_branch_info);
+ return checkout_paths(opts, new_branch_info);
else
- return checkout_branch(opts, &new_branch_info);
+ return checkout_branch(opts, new_branch_info);
}
int cmd_checkout(int argc, const char **argv, const char *prefix)
@@ -1789,6 +1805,7 @@
OPT_END()
};
int ret;
+ struct branch_info new_branch_info = { 0 };
memset(&opts, 0, sizeof(opts));
opts.dwim_new_local_branch = 1;
@@ -1819,7 +1836,9 @@
options = add_checkout_path_options(&opts, options);
ret = checkout_main(argc, argv, prefix, &opts,
- options, checkout_usage);
+ options, checkout_usage, &new_branch_info);
+ branch_info_release(&new_branch_info);
+ clear_pathspec(&opts.pathspec);
FREE_AND_NULL(options);
return ret;
}
@@ -1840,6 +1859,7 @@
OPT_END()
};
int ret;
+ struct branch_info new_branch_info = { 0 };
memset(&opts, 0, sizeof(opts));
opts.dwim_new_local_branch = 1;
@@ -1859,7 +1879,8 @@
cb_option = 'c';
ret = checkout_main(argc, argv, prefix, &opts,
- options, switch_branch_usage);
+ options, switch_branch_usage, &new_branch_info);
+ branch_info_release(&new_branch_info);
FREE_AND_NULL(options);
return ret;
}
@@ -1881,6 +1902,7 @@
OPT_END()
};
int ret;
+ struct branch_info new_branch_info = { 0 };
memset(&opts, 0, sizeof(opts));
opts.accept_ref = 0;
@@ -1896,7 +1918,8 @@
options = add_checkout_path_options(&opts, options);
ret = checkout_main(argc, argv, prefix, &opts,
- options, restore_usage);
+ options, restore_usage, &new_branch_info);
+ branch_info_release(&new_branch_info);
FREE_AND_NULL(options);
return ret;
}
diff --git a/builtin/clean.c b/builtin/clean.c
index 98a2860..3ff02bb 100644
--- a/builtin/clean.c
+++ b/builtin/clean.c
@@ -36,6 +36,8 @@
static const char *msg_would_skip_git_dir = N_("Would skip repository %s\n");
static const char *msg_warn_remove_failed = N_("failed to remove %s");
static const char *msg_warn_lstat_failed = N_("could not lstat %s\n");
+static const char *msg_skip_cwd = N_("Refusing to remove current working directory\n");
+static const char *msg_would_skip_cwd = N_("Would refuse to remove current working directory\n");
enum color_clean {
CLEAN_COLOR_RESET = 0,
@@ -153,6 +155,8 @@
{
DIR *dir;
struct strbuf quoted = STRBUF_INIT;
+ struct strbuf realpath = STRBUF_INIT;
+ struct strbuf real_ocwd = STRBUF_INIT;
struct dirent *e;
int res = 0, ret = 0, gone = 1, original_len = path->len, len;
struct string_list dels = STRING_LIST_INIT_DUP;
@@ -231,16 +235,36 @@
strbuf_setlen(path, original_len);
if (*dir_gone) {
- res = dry_run ? 0 : rmdir(path->buf);
- if (!res)
- *dir_gone = 1;
- else {
- int saved_errno = errno;
- quote_path(path->buf, prefix, "ed, 0);
- errno = saved_errno;
- warning_errno(_(msg_warn_remove_failed), quoted.buf);
+ /*
+ * Normalize path components in path->buf, e.g. change '\' to
+ * '/' on Windows.
+ */
+ strbuf_realpath(&realpath, path->buf, 1);
+
+ /*
+ * path and realpath are absolute; for comparison, we would
+ * like to transform startup_info->original_cwd to an absolute
+ * path too.
+ */
+ if (startup_info->original_cwd)
+ strbuf_realpath(&real_ocwd,
+ startup_info->original_cwd, 1);
+
+ if (!strbuf_cmp(&realpath, &real_ocwd)) {
+ printf("%s", dry_run ? _(msg_would_skip_cwd) : _(msg_skip_cwd));
*dir_gone = 0;
- ret = 1;
+ } else {
+ res = dry_run ? 0 : rmdir(path->buf);
+ if (!res)
+ *dir_gone = 1;
+ else {
+ int saved_errno = errno;
+ quote_path(path->buf, prefix, "ed, 0);
+ errno = saved_errno;
+ warning_errno(_(msg_warn_remove_failed), quoted.buf);
+ *dir_gone = 0;
+ ret = 1;
+ }
}
}
@@ -250,6 +274,8 @@
printf(dry_run ? _(msg_would_remove) : _(msg_remove), dels.items[i].string);
}
out:
+ strbuf_release(&realpath);
+ strbuf_release(&real_ocwd);
strbuf_release("ed);
string_list_clear(&dels, 0);
return ret;
diff --git a/builtin/clone.c b/builtin/clone.c
index fb377b2..727e16e 100644
--- a/builtin/clone.c
+++ b/builtin/clone.c
@@ -633,7 +633,7 @@
{
struct strvec argv = STRVEC_INIT;
int result = 0;
- strvec_pushl(&argv, "-C", repo, "sparse-checkout", "init", NULL);
+ strvec_pushl(&argv, "-C", repo, "sparse-checkout", "set", NULL);
/*
* We must apply the setting in the current process
@@ -900,10 +900,10 @@
if (option_bare) {
if (option_origin)
- die(_("--bare and --origin %s options are incompatible."),
- option_origin);
+ die(_("options '%s' and '%s %s' cannot be used together"),
+ "--bare", "--origin", option_origin);
if (real_git_dir)
- die(_("--bare and --separate-git-dir are incompatible."));
+ die(_("options '%s' and '%s' cannot be used together"), "--bare", "--separate-git-dir");
option_no_checkout = 1;
}
@@ -1290,7 +1290,7 @@
*/
submodule_progress = transport->progress;
- transport_unlock_pack(transport);
+ transport_unlock_pack(transport, 0);
transport_disconnect(transport);
if (option_dissociate) {
diff --git a/builtin/commit.c b/builtin/commit.c
index 883c162..b9ed037 100644
--- a/builtin/commit.c
+++ b/builtin/commit.c
@@ -355,19 +355,19 @@
if (pathspec_from_file) {
if (interactive)
- die(_("--pathspec-from-file is incompatible with --interactive/--patch"));
+ die(_("options '%s' and '%s' cannot be used together"), "--pathspec-from-file", "--interactive/--patch");
if (all)
- die(_("--pathspec-from-file with -a does not make sense"));
+ die(_("options '%s' and '%s' cannot be used together"), "--pathspec-from-file", "-a");
if (pathspec.nr)
- die(_("--pathspec-from-file is incompatible with pathspec arguments"));
+ die(_("'%s' and pathspec arguments cannot be used together"), "--pathspec-from-file");
parse_pathspec_file(&pathspec, 0,
PATHSPEC_PREFER_FULL,
prefix, pathspec_from_file, pathspec_file_nul);
} else if (pathspec_file_nul) {
- die(_("--pathspec-file-nul requires --pathspec-from-file"));
+ die(_("the option '%s' requires '%s'"), "--pathspec-file-nul", "--pathspec-from-file");
}
if (!pathspec.nr && (also || (only && !allow_empty &&
@@ -799,7 +799,7 @@
if (!strcmp(fixup_prefix, "amend")) {
if (have_option_m)
- die(_("cannot combine -m with --fixup:%s"), fixup_message);
+ die(_("options '%s' and '%s:%s' cannot be used together"), "-m", "--fixup", fixup_message);
prepare_amend_commit(commit, &sb, &ctx);
}
} else if (!stat(git_path_merge_msg(the_repository), &statbuf)) {
@@ -1193,7 +1193,7 @@
status_format == STATUS_FORMAT_UNSPECIFIED)
status_format = STATUS_FORMAT_PORCELAIN;
else if (status_format == STATUS_FORMAT_LONG)
- die(_("--long and -z are incompatible"));
+ die(_("options '%s' and '%s' cannot be used together"), "--long", "-z");
}
if (use_deferred_config && status_format == STATUS_FORMAT_UNSPECIFIED)
@@ -1229,9 +1229,10 @@
die(_("You are in the middle of a cherry-pick -- cannot reword."));
}
if (argc)
- die(_("cannot combine reword option of --fixup with path '%s'"), *argv);
+ die(_("reword option of '%s' and path '%s' cannot be used together"), "--fixup", *argv);
if (patch_interactive || interactive || all || also || only)
- die(_("reword option of --fixup is mutually exclusive with --patch/--interactive/--all/--include/--only"));
+ die(_("reword option of '%s' and '%s' cannot be used together"),
+ "--fixup", "--patch/--interactive/--all/--include/--only");
}
static int parse_and_validate_options(int argc, const char *argv[],
diff --git a/builtin/credential.c b/builtin/credential.c
index d75dcdc..d7b304f 100644
--- a/builtin/credential.c
+++ b/builtin/credential.c
@@ -4,7 +4,7 @@
#include "config.h"
static const char usage_msg[] =
- "git credential [fill|approve|reject]";
+ "git credential (fill|approve|reject)";
int cmd_credential(int argc, const char **argv, const char *prefix)
{
diff --git a/builtin/describe.c b/builtin/describe.c
index e912ba5..42159cd 100644
--- a/builtin/describe.c
+++ b/builtin/describe.c
@@ -590,7 +590,7 @@
save_commit_buffer = 0;
if (longformat && abbrev == 0)
- die(_("--long is incompatible with --abbrev=0"));
+ die(_("options '%s' and '%s' cannot be used together"), "--long", "--abbrev=0");
if (contains) {
struct string_list_item *item;
@@ -670,9 +670,9 @@
}
describe("HEAD", 1);
} else if (dirty) {
- die(_("--dirty is incompatible with commit-ishes"));
+ die(_("option '%s' and commit-ishes cannot be used together"), "--dirty");
} else if (broken) {
- die(_("--broken is incompatible with commit-ishes"));
+ die(_("option '%s' and commit-ishes cannot be used together"), "--broken");
} else {
while (argc-- > 0)
describe(*argv++, argc == 0);
diff --git a/builtin/diff-tree.c b/builtin/diff-tree.c
index f33d30d..0e0ac1f 100644
--- a/builtin/diff-tree.c
+++ b/builtin/diff-tree.c
@@ -152,7 +152,7 @@
}
if (read_stdin && merge_base)
- die(_("--stdin and --merge-base are mutually exclusive"));
+ die(_("options '%s' and '%s' cannot be used together"), "--stdin", "--merge-base");
if (merge_base && opt->pending.nr != 2)
die(_("--merge-base only works with two commits"));
diff --git a/builtin/diff.c b/builtin/diff.c
index dd8ce68..fa46833 100644
--- a/builtin/diff.c
+++ b/builtin/diff.c
@@ -437,6 +437,11 @@
prefix = setup_git_directory_gently(&nongit);
+ if (!nongit) {
+ prepare_repo_settings(the_repository);
+ the_repository->settings.command_requires_full_index = 0;
+ }
+
if (!no_index) {
/*
* Treat git diff with at least one path outside of the
diff --git a/builtin/difftool.c b/builtin/difftool.c
index 4931c10..c79fbbf 100644
--- a/builtin/difftool.c
+++ b/builtin/difftool.c
@@ -202,15 +202,10 @@
{
struct child_process update_index = CHILD_PROCESS_INIT;
struct child_process diff_files = CHILD_PROCESS_INIT;
- struct strbuf index_env = STRBUF_INIT, buf = STRBUF_INIT;
- const char *git_dir = absolute_path(get_git_dir()), *env[] = {
- NULL, NULL
- };
+ struct strbuf buf = STRBUF_INIT;
+ const char *git_dir = absolute_path(get_git_dir());
FILE *fp;
- strbuf_addf(&index_env, "GIT_INDEX_FILE=%s", index_path);
- env[0] = index_env.buf;
-
strvec_pushl(&update_index.args,
"--git-dir", git_dir, "--work-tree", workdir,
"update-index", "--really-refresh", "-q",
@@ -222,7 +217,7 @@
update_index.use_shell = 0;
update_index.clean_on_exit = 1;
update_index.dir = workdir;
- update_index.env = env;
+ strvec_pushf(&update_index.env_array, "GIT_INDEX_FILE=%s", index_path);
/* Ignore any errors of update-index */
run_command(&update_index);
@@ -235,7 +230,7 @@
diff_files.clean_on_exit = 1;
diff_files.out = -1;
diff_files.dir = workdir;
- diff_files.env = env;
+ strvec_pushf(&diff_files.env_array, "GIT_INDEX_FILE=%s", index_path);
if (start_command(&diff_files))
die("could not obtain raw diff");
fp = xfdopen(diff_files.out, "r");
@@ -248,7 +243,6 @@
fclose(fp);
if (finish_command(&diff_files))
die("diff-files did not exit properly");
- strbuf_release(&index_env);
strbuf_release(&buf);
}
@@ -736,10 +730,10 @@
setenv(GIT_DIR_ENVIRONMENT, absolute_path(get_git_dir()), 1);
setenv(GIT_WORK_TREE_ENVIRONMENT, absolute_path(get_git_work_tree()), 1);
} else if (dir_diff)
- die(_("--dir-diff is incompatible with --no-index"));
+ die(_("options '%s' and '%s' cannot be used together"), "--dir-diff", "--no-index");
if (use_gui_tool + !!difftool_cmd + !!extcmd > 1)
- die(_("--gui, --tool and --extcmd are mutually exclusive"));
+ die(_("options '%s', '%s', and '%s' cannot be used together"), "--gui", "--tool", "--extcmd");
if (use_gui_tool)
setenv("GIT_MERGETOOL_GUI", "true", 1);
diff --git a/builtin/fast-export.c b/builtin/fast-export.c
index 8e2caf7..9f1c730 100644
--- a/builtin/fast-export.c
+++ b/builtin/fast-export.c
@@ -107,18 +107,6 @@
static struct decoration idnums;
static uint32_t last_idnum;
-
-static int has_unshown_parent(struct commit *commit)
-{
- struct commit_list *parent;
-
- for (parent = commit->parents; parent; parent = parent->next)
- if (!(parent->item->object.flags & SHOWN) &&
- !(parent->item->object.flags & UNINTERESTING))
- return 1;
- return 0;
-}
-
struct anonymized_entry {
struct hashmap_entry hash;
const char *anon;
@@ -752,20 +740,6 @@
return strbuf_detach(&out, NULL);
}
-static void handle_tail(struct object_array *commits, struct rev_info *revs,
- struct string_list *paths_of_changed_objects)
-{
- struct commit *commit;
- while (commits->nr) {
- commit = (struct commit *)object_array_pop(commits);
- if (has_unshown_parent(commit)) {
- /* Queue again, to be handled later */
- add_object_array(&commit->object, NULL, commits);
- return;
- }
- handle_commit(commit, revs, paths_of_changed_objects);
- }
-}
static void handle_tag(const char *name, struct tag *tag)
{
@@ -1185,7 +1159,6 @@
int cmd_fast_export(int argc, const char **argv, const char *prefix)
{
struct rev_info revs;
- struct object_array commits = OBJECT_ARRAY_INIT;
struct commit *commit;
char *export_filename = NULL,
*import_filename = NULL,
@@ -1254,7 +1227,7 @@
usage_with_options (fast_export_usage, options);
if (anonymized_seeds.cmpfn && !anonymize)
- die(_("--anonymize-map without --anonymize does not make sense"));
+ die(_("the option '%s' requires '%s'"), "--anonymize-map", "--anonymize");
if (refspecs_list.nr) {
int i;
@@ -1269,7 +1242,7 @@
printf("feature done\n");
if (import_filename && import_filename_if_exists)
- die(_("Cannot pass both --import-marks and --import-marks-if-exists"));
+ die(_("options '%s' and '%s' cannot be used together"), "--import-marks", "--import-marks-if-exists");
if (import_filename)
import_marks(import_filename, 0);
else if (import_filename_if_exists)
@@ -1283,18 +1256,13 @@
if (prepare_revision_walk(&revs))
die("revision walk setup failed");
+
+ revs.reverse = 1;
revs.diffopt.format_callback = show_filemodify;
revs.diffopt.format_callback_data = &paths_of_changed_objects;
revs.diffopt.flags.recursive = 1;
- while ((commit = get_revision(&revs))) {
- if (has_unshown_parent(commit)) {
- add_object_array(&commit->object, NULL, &commits);
- }
- else {
- handle_commit(commit, &revs, &paths_of_changed_objects);
- handle_tail(&commits, &revs, &paths_of_changed_objects);
- }
- }
+ while ((commit = get_revision(&revs)))
+ handle_commit(commit, &revs, &paths_of_changed_objects);
handle_tags_and_duplicates(&extra_refs);
handle_tags_and_duplicates(&tag_refs);
diff --git a/builtin/fast-import.c b/builtin/fast-import.c
index 20406f6..2b2e28b 100644
--- a/builtin/fast-import.c
+++ b/builtin/fast-import.c
@@ -401,16 +401,18 @@
static NORETURN void die_nicely(const char *err, va_list params)
{
+ va_list cp;
static int zombie;
- char message[2 * PATH_MAX];
+ report_fn die_message_fn = get_die_message_routine();
- vsnprintf(message, sizeof(message), err, params);
- fputs("fatal: ", stderr);
- fputs(message, stderr);
- fputc('\n', stderr);
+ va_copy(cp, params);
+ die_message_fn(err, params);
if (!zombie) {
+ char message[2 * PATH_MAX];
+
zombie = 1;
+ vsnprintf(message, sizeof(message), err, cp);
write_crash_report(message);
end_packfile();
unkeep_all_packs();
diff --git a/builtin/fetch.c b/builtin/fetch.c
index f7abbc3..5f06b21 100644
--- a/builtin/fetch.c
+++ b/builtin/fetch.c
@@ -28,6 +28,7 @@
#include "promisor-remote.h"
#include "commit-graph.h"
#include "shallow.h"
+#include "worktree.h"
#define FORCED_UPDATES_DELAY_WARNING_IN_MS (10 * 1000)
@@ -222,17 +223,22 @@
OPT_END()
};
-static void unlock_pack(void)
+static void unlock_pack(unsigned int flags)
{
if (gtransport)
- transport_unlock_pack(gtransport);
+ transport_unlock_pack(gtransport, flags);
if (gsecondary)
- transport_unlock_pack(gsecondary);
+ transport_unlock_pack(gsecondary, flags);
+}
+
+static void unlock_pack_atexit(void)
+{
+ unlock_pack(0);
}
static void unlock_pack_on_signal(int signo)
{
- unlock_pack();
+ unlock_pack(TRANSPORT_UNLOCK_PACK_IN_SIGNAL_HANDLER);
sigchain_pop(signo);
raise(signo);
}
@@ -552,7 +558,7 @@
for (i = 0; i < fetch_refspec->nr; i++)
get_fetch_map(ref_map, &fetch_refspec->items[i], &oref_tail, 1);
} else if (refmap.nr) {
- die("--refmap option is only meaningful with command-line refspec(s).");
+ die("--refmap option is only meaningful with command-line refspec(s)");
} else {
/* Use the defaults */
struct branch *branch = branch_get(NULL);
@@ -583,7 +589,7 @@
} else if (!prefetch) {
ref_map = get_remote_ref(remote_refs, "HEAD");
if (!ref_map)
- die(_("Couldn't find remote ref HEAD"));
+ die(_("couldn't find remote ref HEAD"));
ref_map->fetch_head_status = FETCH_HEAD_MERGE;
tail = &ref_map->next;
}
@@ -848,13 +854,12 @@
static int update_local_ref(struct ref *ref,
struct ref_transaction *transaction,
- const char *remote,
- const struct ref *remote_ref,
- struct strbuf *display,
- int summary_width)
+ const char *remote, const struct ref *remote_ref,
+ struct strbuf *display, int summary_width,
+ struct worktree **worktrees)
{
struct commit *current = NULL, *updated;
- struct branch *current_branch = branch_get(NULL);
+ const struct worktree *wt;
const char *pretty_ref = prettify_refname(ref->name);
int fast_forward = 0;
@@ -868,16 +873,17 @@
return 0;
}
- if (current_branch &&
- !strcmp(ref->name, current_branch->name) &&
- !(update_head_ok || is_bare_repository()) &&
- !is_null_oid(&ref->old_oid)) {
+ if (!update_head_ok &&
+ (wt = find_shared_symref(worktrees, "HEAD", ref->name)) &&
+ !wt->is_bare && !is_null_oid(&ref->old_oid)) {
/*
* If this is the head, and it's not okay to update
* the head, and the old value of the head isn't empty...
*/
format_display(display, '!', _("[rejected]"),
- _("can't fetch in current branch"),
+ wt->is_current ?
+ _("can't fetch in current branch") :
+ _("checked out in another worktree"),
remote, pretty_ref, summary_width);
return 1;
}
@@ -995,7 +1001,7 @@
if (write_fetch_head) {
fetch_head->fp = fopen(filename, "a");
if (!fetch_head->fp)
- return error_errno(_("cannot open %s"), filename);
+ return error_errno(_("cannot open '%s'"), filename);
strbuf_init(&fetch_head->buf, 0);
} else {
fetch_head->fp = NULL;
@@ -1067,16 +1073,17 @@
}
static const char warn_show_forced_updates[] =
-N_("Fetch normally indicates which branches had a forced update,\n"
- "but that check has been disabled. To re-enable, use '--show-forced-updates'\n"
- "flag or run 'git config fetch.showForcedUpdates true'.");
+N_("fetch normally indicates which branches had a forced update,\n"
+ "but that check has been disabled; to re-enable, use '--show-forced-updates'\n"
+ "flag or run 'git config fetch.showForcedUpdates true'");
static const char warn_time_show_forced_updates[] =
-N_("It took %.2f seconds to check forced updates. You can use\n"
+N_("it took %.2f seconds to check forced updates; you can use\n"
"'--no-show-forced-updates' or run 'git config fetch.showForcedUpdates false'\n"
- " to avoid this check.\n");
+ "to avoid this check\n");
static int store_updated_refs(const char *raw_url, const char *remote_name,
- int connectivity_checked, struct ref *ref_map)
+ int connectivity_checked, struct ref *ref_map,
+ struct worktree **worktrees)
{
struct fetch_head fetch_head;
int url_len, i, rc = 0;
@@ -1205,7 +1212,8 @@
strbuf_reset(¬e);
if (ref) {
rc |= update_local_ref(ref, transaction, what,
- rm, ¬e, summary_width);
+ rm, ¬e, summary_width,
+ worktrees);
free(ref);
} else if (write_fetch_head || dry_run) {
/*
@@ -1298,7 +1306,9 @@
return check_connected(iterate_ref_map, &rm, &opt);
}
-static int fetch_and_consume_refs(struct transport *transport, struct ref *ref_map)
+static int fetch_and_consume_refs(struct transport *transport,
+ struct ref *ref_map,
+ struct worktree **worktrees)
{
int connectivity_checked = 1;
int ret;
@@ -1319,14 +1329,12 @@
}
trace2_region_enter("fetch", "consume_refs", the_repository);
- ret = store_updated_refs(transport->url,
- transport->remote->name,
- connectivity_checked,
- ref_map);
+ ret = store_updated_refs(transport->url, transport->remote->name,
+ connectivity_checked, ref_map, worktrees);
trace2_region_leave("fetch", "consume_refs", the_repository);
out:
- transport_unlock_pack(transport);
+ transport_unlock_pack(transport, 0);
return ret;
}
@@ -1385,18 +1393,18 @@
return result;
}
-static void check_not_current_branch(struct ref *ref_map)
+static void check_not_current_branch(struct ref *ref_map,
+ struct worktree **worktrees)
{
- struct branch *current_branch = branch_get(NULL);
-
- if (is_bare_repository() || !current_branch)
- return;
-
+ const struct worktree *wt;
for (; ref_map; ref_map = ref_map->next)
- if (ref_map->peer_ref && !strcmp(current_branch->refname,
- ref_map->peer_ref->name))
- die(_("Refusing to fetch into current branch %s "
- "of non-bare repository"), current_branch->refname);
+ if (ref_map->peer_ref &&
+ (wt = find_shared_symref(worktrees, "HEAD",
+ ref_map->peer_ref->name)) &&
+ !wt->is_bare)
+ die(_("refusing to fetch into branch '%s' "
+ "checked out at '%s'"),
+ ref_map->peer_ref->name, wt->path);
}
static int truncate_fetch_head(void)
@@ -1405,7 +1413,7 @@
FILE *fp = fopen_for_writing(filename);
if (!fp)
- return error_errno(_("cannot open %s"), filename);
+ return error_errno(_("cannot open '%s'"), filename);
fclose(fp);
return 0;
}
@@ -1414,10 +1422,10 @@
{
int r = transport_set_option(transport, name, value);
if (r < 0)
- die(_("Option \"%s\" value \"%s\" is not valid for %s"),
+ die(_("option \"%s\" value \"%s\" is not valid for %s"),
name, value, transport->url);
if (r > 0)
- warning(_("Option \"%s\" is ignored for %s\n"),
+ warning(_("option \"%s\" is ignored for %s\n"),
name, transport->url);
}
@@ -1451,7 +1459,7 @@
old_nr = oids->nr;
for_each_glob_ref(add_oid, s, oids);
if (old_nr == oids->nr)
- warning("Ignoring --negotiation-tip=%s because it does not match any refs",
+ warning("ignoring --negotiation-tip=%s because it does not match any refs",
s);
}
smart_options->negotiation_tips = oids;
@@ -1489,12 +1497,13 @@
if (transport->smart_options)
add_negotiation_tips(transport->smart_options);
else
- warning("Ignoring --negotiation-tip because the protocol does not support it.");
+ warning("ignoring --negotiation-tip because the protocol does not support it");
}
return transport;
}
-static void backfill_tags(struct transport *transport, struct ref *ref_map)
+static void backfill_tags(struct transport *transport, struct ref *ref_map,
+ struct worktree **worktrees)
{
int cannot_reuse;
@@ -1515,7 +1524,7 @@
transport_set_option(transport, TRANS_OPT_FOLLOWTAGS, NULL);
transport_set_option(transport, TRANS_OPT_DEPTH, "0");
transport_set_option(transport, TRANS_OPT_DEEPEN_RELATIVE, NULL);
- fetch_and_consume_refs(transport, ref_map);
+ fetch_and_consume_refs(transport, ref_map, worktrees);
if (gsecondary) {
transport_disconnect(gsecondary);
@@ -1533,6 +1542,7 @@
struct transport_ls_refs_options transport_ls_refs_options =
TRANSPORT_LS_REFS_OPTIONS_INIT;
int must_list_refs = 1;
+ struct worktree **worktrees = get_worktrees();
if (tags == TAGS_DEFAULT) {
if (transport->remote->fetch_tags == 2)
@@ -1588,7 +1598,7 @@
ref_map = get_ref_map(transport->remote, remote_refs, rs,
tags, &autotags);
if (!update_head_ok)
- check_not_current_branch(ref_map);
+ check_not_current_branch(ref_map, worktrees);
if (tags == TAGS_DEFAULT && autotags)
transport_set_option(transport, TRANS_OPT_FOLLOWTAGS, "1");
@@ -1606,7 +1616,7 @@
transport->url);
}
}
- if (fetch_and_consume_refs(transport, ref_map)) {
+ if (fetch_and_consume_refs(transport, ref_map, worktrees)) {
free_refs(ref_map);
retcode = 1;
goto cleanup;
@@ -1638,6 +1648,16 @@
}
}
if (source_ref) {
+ if (!branch) {
+ const char *shortname = source_ref->name;
+ skip_prefix(shortname, "refs/heads/", &shortname);
+
+ warning(_("could not set upstream of HEAD to '%s' from '%s' when "
+ "it does not point to any branch."),
+ shortname, transport->remote->name);
+ goto skip;
+ }
+
if (!strcmp(source_ref->name, "HEAD") ||
starts_with(source_ref->name, "refs/heads/"))
install_branch_config(0,
@@ -1651,11 +1671,11 @@
else
warning(_("unknown branch type"));
} else {
- warning(_("no source branch found.\n"
- "you need to specify exactly one branch with the --set-upstream option."));
+ warning(_("no source branch found;\n"
+ "you need to specify exactly one branch with the --set-upstream option"));
}
}
- skip:
+skip:
free_refs(ref_map);
/* if neither --no-tags nor --tags was specified, do automated tag
@@ -1665,11 +1685,12 @@
ref_map = NULL;
find_non_local_tags(remote_refs, &ref_map, &tail);
if (ref_map)
- backfill_tags(transport, ref_map);
+ backfill_tags(transport, ref_map, worktrees);
free_refs(ref_map);
}
- cleanup:
+cleanup:
+ free_worktrees(worktrees);
return retcode;
}
@@ -1790,7 +1811,7 @@
struct parallel_fetch_state *state = cb;
const char *remote = task_cb;
- state->result = error(_("Could not fetch %s"), remote);
+ state->result = error(_("could not fetch %s"), remote);
return 0;
}
@@ -1845,7 +1866,7 @@
if (verbosity >= 0)
printf(_("Fetching %s\n"), name);
if (run_command_v_opt(argv.v, RUN_GIT_CMD)) {
- error(_("Could not fetch %s"), name);
+ error(_("could not fetch %s"), name);
result = 1;
}
strvec_pop(&argv);
@@ -1906,8 +1927,8 @@
int remote_via_config = remote_is_configured(remote, 0);
if (!remote)
- die(_("No remote repository specified. Please, specify either a URL or a\n"
- "remote name from which new revisions should be fetched."));
+ die(_("no remote repository specified; please specify either a URL or a\n"
+ "remote name from which new revisions should be fetched"));
gtransport = prepare_transport(remote, 1);
@@ -1942,7 +1963,7 @@
if (!strcmp(argv[i], "tag")) {
i++;
if (i >= argc)
- die(_("You need to specify a tag name."));
+ die(_("you need to specify a tag name"));
refspec_appendf(&rs, "refs/tags/%s:refs/tags/%s",
argv[i], argv[i]);
@@ -1962,7 +1983,7 @@
gtransport->server_options = &server_options;
sigchain_push_common(unlock_pack_on_signal);
- atexit(unlock_pack);
+ atexit(unlock_pack_atexit);
sigchain_push(SIGPIPE, SIG_IGN);
exit_code = do_fetch(gtransport, &rs);
sigchain_pop(SIGPIPE);
@@ -1993,6 +2014,8 @@
}
git_config(git_fetch_config, NULL);
+ prepare_repo_settings(the_repository);
+ the_repository->settings.command_requires_full_index = 0;
argc = parse_options(argc, argv, prefix,
builtin_fetch_options, builtin_fetch_usage, 0);
@@ -2010,14 +2033,14 @@
if (deepen_relative) {
if (deepen_relative < 0)
- die(_("Negative depth in --deepen is not supported"));
+ die(_("negative depth in --deepen is not supported"));
if (depth)
- die(_("--deepen and --depth are mutually exclusive"));
+ die(_("options '%s' and '%s' cannot be used together"), "--deepen", "--depth");
depth = xstrfmt("%d", deepen_relative);
}
if (unshallow) {
if (depth)
- die(_("--depth and --unshallow cannot be used together"));
+ die(_("options '%s' and '%s' cannot be used together"), "--depth", "--unshallow");
else if (!is_repository_shallow(the_repository))
die(_("--unshallow on a complete repository does not make sense"));
else
@@ -2047,14 +2070,15 @@
/* All arguments are assumed to be remotes or groups */
for (i = 0; i < argc; i++)
if (!add_remote_or_group(argv[i], &list))
- die(_("No such remote or remote group: %s"), argv[i]);
+ die(_("no such remote or remote group: %s"),
+ argv[i]);
} else {
/* Single remote or group */
(void) add_remote_or_group(argv[0], &list);
if (list.nr > 1) {
/* More than one remote */
if (argc > 1)
- die(_("Fetching a group and specifying refspecs does not make sense"));
+ die(_("fetching a group and specifying refspecs does not make sense"));
} else {
/* Zero or one remotes */
remote = remote_get(argv[0]);
@@ -2075,7 +2099,7 @@
if (gtransport->smart_options) {
gtransport->smart_options->acked_commits = &acked_commits;
} else {
- warning(_("Protocol does not support --negotiate-only, exiting."));
+ warning(_("protocol does not support --negotiate-only, exiting"));
return 1;
}
if (server_options.nr)
diff --git a/builtin/fmt-merge-msg.c b/builtin/fmt-merge-msg.c
index 48a8699..8d8fd39 100644
--- a/builtin/fmt-merge-msg.c
+++ b/builtin/fmt-merge-msg.c
@@ -12,6 +12,7 @@
{
const char *inpath = NULL;
const char *message = NULL;
+ char *into_name = NULL;
int shortlog_len = -1;
struct option options[] = {
{ OPTION_INTEGER, 0, "log", &shortlog_len, N_("n"),
@@ -23,6 +24,8 @@
DEFAULT_MERGE_LOG_LEN },
OPT_STRING('m', "message", &message, N_("text"),
N_("use <text> as start of message")),
+ OPT_STRING(0, "into-name", &into_name, N_("name"),
+ N_("use <name> instead of the real target branch")),
OPT_FILENAME('F', "file", &inpath, N_("file to read from")),
OPT_END()
};
@@ -56,6 +59,7 @@
opts.add_title = !message;
opts.credit_people = 1;
opts.shortlog_len = shortlog_len;
+ opts.into_name = into_name;
ret = fmt_merge_msg(&input, &output, &opts);
if (ret)
diff --git a/builtin/for-each-ref.c b/builtin/for-each-ref.c
index 16a2c7d..6f62f40 100644
--- a/builtin/for-each-ref.c
+++ b/builtin/for-each-ref.c
@@ -17,7 +17,8 @@
int cmd_for_each_ref(int argc, const char **argv, const char *prefix)
{
int i;
- struct ref_sorting *sorting = NULL, **sorting_tail = &sorting;
+ struct ref_sorting *sorting;
+ struct string_list sorting_options = STRING_LIST_INIT_DUP;
int maxcount = 0, icase = 0;
struct ref_array array;
struct ref_filter filter;
@@ -39,7 +40,7 @@
OPT_INTEGER( 0 , "count", &maxcount, N_("show only <n> matched refs")),
OPT_STRING( 0 , "format", &format.format, N_("format"), N_("format to use for the output")),
OPT__COLOR(&format.use_color, N_("respect format colors")),
- OPT_REF_SORT(sorting_tail),
+ OPT_REF_SORT(&sorting_options),
OPT_CALLBACK(0, "points-at", &filter.points_at,
N_("object"), N_("print only refs which points at the given object"),
parse_opt_object_name),
@@ -70,8 +71,7 @@
if (verify_ref_format(&format))
usage_with_options(for_each_ref_usage, opts);
- if (!sorting)
- sorting = ref_default_sorting();
+ sorting = ref_sorting_options(&sorting_options);
ref_sorting_set_sort_flags_all(sorting, REF_SORTING_ICASE, icase);
filter.ignore_case = icase;
diff --git a/builtin/fsck.c b/builtin/fsck.c
index 27b9e78..9e54892 100644
--- a/builtin/fsck.c
+++ b/builtin/fsck.c
@@ -944,15 +944,13 @@
if (the_repository->settings.core_commit_graph) {
struct child_process commit_graph_verify = CHILD_PROCESS_INIT;
- const char *verify_argv[] = { "commit-graph", "verify", NULL, NULL, NULL };
prepare_alt_odb(the_repository);
for (odb = the_repository->objects->odb; odb; odb = odb->next) {
child_process_init(&commit_graph_verify);
- commit_graph_verify.argv = verify_argv;
commit_graph_verify.git_cmd = 1;
- verify_argv[2] = "--object-dir";
- verify_argv[3] = odb->path;
+ strvec_pushl(&commit_graph_verify.args, "commit-graph",
+ "verify", "--object-dir", odb->path, NULL);
if (run_command(&commit_graph_verify))
errors_found |= ERROR_COMMIT_GRAPH;
}
@@ -960,15 +958,13 @@
if (the_repository->settings.core_multi_pack_index) {
struct child_process midx_verify = CHILD_PROCESS_INIT;
- const char *midx_argv[] = { "multi-pack-index", "verify", NULL, NULL, NULL };
prepare_alt_odb(the_repository);
for (odb = the_repository->objects->odb; odb; odb = odb->next) {
child_process_init(&midx_verify);
- midx_verify.argv = midx_argv;
midx_verify.git_cmd = 1;
- midx_argv[2] = "--object-dir";
- midx_argv[3] = odb->path;
+ strvec_pushl(&midx_verify.args, "multi-pack-index",
+ "verify", "--object-dir", odb->path, NULL);
if (run_command(&midx_verify))
errors_found |= ERROR_MULTI_PACK_INDEX;
}
diff --git a/builtin/gc.c b/builtin/gc.c
index bcef6a4..8e60ef1 100644
--- a/builtin/gc.c
+++ b/builtin/gc.c
@@ -470,7 +470,8 @@
/*
* Returns 0 if there was no previous error and gc can proceed, 1 if
* gc should not proceed due to an error in the last run. Prints a
- * message and returns -1 if an error occurred while reading gc.log
+ * message and returns with a non-[01] status code if an error occurred
+ * while reading gc.log
*/
static int report_last_gc_error(void)
{
@@ -484,7 +485,7 @@
if (errno == ENOENT)
goto done;
- ret = error_errno(_("cannot stat '%s'"), gc_log_path);
+ ret = die_message_errno(_("cannot stat '%s'"), gc_log_path);
goto done;
}
@@ -493,7 +494,7 @@
len = strbuf_read_file(&sb, gc_log_path, 0);
if (len < 0)
- ret = error_errno(_("cannot read '%s'"), gc_log_path);
+ ret = die_message_errno(_("cannot read '%s'"), gc_log_path);
else if (len > 0) {
/*
* A previous gc failed. Report the error, and don't
@@ -611,12 +612,13 @@
}
if (detach_auto) {
int ret = report_last_gc_error();
- if (ret < 0)
- /* an I/O error occurred, already reported */
- exit(128);
+
if (ret == 1)
/* Last gc --auto failed. Skip this one. */
return 0;
+ else if (ret)
+ /* an I/O error occurred, already reported */
+ return ret;
if (lock_repo_for_gc(force, &pid))
return 0;
diff --git a/builtin/help.c b/builtin/help.c
index 75cd2fb..d387131 100644
--- a/builtin/help.c
+++ b/builtin/help.c
@@ -212,11 +212,10 @@
{
struct strbuf buffer = STRBUF_INIT;
struct child_process ec_process = CHILD_PROCESS_INIT;
- const char *argv_ec[] = { "emacsclient", "--version", NULL };
int version;
/* emacsclient prints its version number on stderr */
- ec_process.argv = argv_ec;
+ strvec_pushl(&ec_process.args, "emacsclient", "--version", NULL);
ec_process.err = -1;
ec_process.stdout_to_stderr = 1;
if (start_command(&ec_process))
diff --git a/builtin/index-pack.c b/builtin/index-pack.c
index c23d01d..3c2e6ae 100644
--- a/builtin/index-pack.c
+++ b/builtin/index-pack.c
@@ -1845,11 +1845,11 @@
if (!pack_name && !from_stdin)
usage(index_pack_usage);
if (fix_thin_pack && !from_stdin)
- die(_("--fix-thin cannot be used without --stdin"));
+ die(_("the option '%s' requires '%s'"), "--fix-thin", "--stdin");
if (from_stdin && !startup_info->have_repository)
die(_("--stdin requires a git repository"));
if (from_stdin && hash_algo)
- die(_("--object-format cannot be used with --stdin"));
+ die(_("options '%s' and '%s' cannot be used together"), "--object-format", "--stdin");
if (!index_name && pack_name)
index_name = derive_filename(pack_name, "pack", "idx", &index_name_buf);
diff --git a/builtin/init-db.c b/builtin/init-db.c
index 2167796..546f9c5 100644
--- a/builtin/init-db.c
+++ b/builtin/init-db.c
@@ -557,7 +557,7 @@
argc = parse_options(argc, argv, prefix, init_db_options, init_db_usage, 0);
if (real_git_dir && is_bare_repository_cfg == 1)
- die(_("--separate-git-dir and --bare are mutually exclusive"));
+ die(_("options '%s' and '%s' cannot be used together"), "--separate-git-dir", "--bare");
if (real_git_dir && !is_absolute_path(real_git_dir))
real_git_dir = real_pathdup(real_git_dir, 1);
diff --git a/builtin/log.c b/builtin/log.c
index f75d87e..4b49340 100644
--- a/builtin/log.c
+++ b/builtin/log.c
@@ -245,10 +245,24 @@
rev->abbrev_commit = 0;
}
- if (rev->commit_format == CMIT_FMT_USERFORMAT && !w.decorate)
- decoration_style = 0;
+ if (rev->commit_format == CMIT_FMT_USERFORMAT) {
+ if (!w.decorate) {
+ /*
+ * Disable decoration loading if the format will not
+ * show them anyway.
+ */
+ decoration_style = 0;
+ } else if (!decoration_style) {
+ /*
+ * If we are going to show them, make sure we do load
+ * them here, but taking care not to override a
+ * specific style set by config or --decorate.
+ */
+ decoration_style = DECORATE_SHORT_REFS;
+ }
+ }
- if (decoration_style) {
+ if (decoration_style || rev->simplify_by_decoration) {
const struct string_list *config_exclude =
repo_config_get_value_multi(the_repository,
"log.excludeDecoration");
@@ -260,7 +274,8 @@
item->string);
}
- rev->show_decorations = 1;
+ if (decoration_style)
+ rev->show_decorations = 1;
load_ref_decorations(&decoration_filter, decoration_style);
}
@@ -1928,9 +1943,9 @@
numbered = 0;
if (numbered && keep_subject)
- die(_("-n and -k are mutually exclusive"));
+ die(_("options '%s' and '%s' cannot be used together"), "-n", "-k");
if (keep_subject && subject_prefix)
- die(_("--subject-prefix/--rfc and -k are mutually exclusive"));
+ die(_("options '%s' and '%s' cannot be used together"), "--subject-prefix/--rfc", "-k");
rev.preserve_subject = keep_subject;
argc = setup_revisions(argc, argv, &rev, &s_r_opt);
@@ -1964,7 +1979,7 @@
load_display_notes(&rev.notes_opt);
if (use_stdout + rev.diffopt.close_file + !!output_directory > 1)
- die(_("--stdout, --output, and --output-directory are mutually exclusive"));
+ die(_("options '%s', '%s', and '%s' cannot be used together"), "--stdout", "--output", "--output-directory");
if (use_stdout) {
setup_pager();
@@ -2097,7 +2112,7 @@
if (creation_factor < 0)
creation_factor = RANGE_DIFF_CREATION_FACTOR_DEFAULT;
else if (!rdiff_prev)
- die(_("--creation-factor requires --range-diff"));
+ die(_("the option '%s' requires '%s'"), "--creation-factor", "--range-diff");
if (rdiff_prev) {
if (!cover_letter && total != 1)
@@ -2241,6 +2256,7 @@
strbuf_release(&rdiff1);
strbuf_release(&rdiff2);
strbuf_release(&rdiff_title);
+ UNLEAK(rev);
return 0;
}
diff --git a/builtin/ls-files.c b/builtin/ls-files.c
index 031fef1..f7ea56c 100644
--- a/builtin/ls-files.c
+++ b/builtin/ls-files.c
@@ -37,6 +37,7 @@
static int show_eol;
static int recurse_submodules;
static int skipping_duplicates;
+static int show_sparse_dirs;
static const char *prefix;
static int max_prefix_len;
@@ -315,8 +316,10 @@
if (!(show_cached || show_stage || show_deleted || show_modified))
return;
- /* TODO: audit for interaction with sparse-index. */
- ensure_full_index(repo->index);
+
+ if (!show_sparse_dirs)
+ ensure_full_index(repo->index);
+
for (i = 0; i < repo->index->cache_nr; i++) {
const struct cache_entry *ce = repo->index->cache[i];
struct stat st;
@@ -670,6 +673,8 @@
OPT_BOOL(0, "debug", &debug_mode, N_("show debugging data")),
OPT_BOOL(0, "deduplicate", &skipping_duplicates,
N_("suppress duplicate entries")),
+ OPT_BOOL(0, "sparse", &show_sparse_dirs,
+ N_("show sparse directories in the presence of a sparse index")),
OPT_END()
};
int ret = 0;
@@ -677,6 +682,9 @@
if (argc == 2 && !strcmp(argv[1], "-h"))
usage_with_options(ls_files_usage, builtin_ls_files_options);
+ prepare_repo_settings(the_repository);
+ the_repository->settings.command_requires_full_index = 0;
+
prefix = cmd_prefix;
if (prefix)
prefix_len = strlen(prefix);
@@ -767,7 +775,7 @@
* would not make any sense with this option.
*/
if (show_stage || show_unmerged)
- die("ls-files --with-tree is incompatible with -s or -u");
+ die(_("options '%s' and '%s' cannot be used together"), "ls-files --with-tree", "-s/-u");
overlay_tree_on_index(the_repository->index, with_tree, max_prefix);
}
diff --git a/builtin/ls-remote.c b/builtin/ls-remote.c
index 318949c..44448fa 100644
--- a/builtin/ls-remote.c
+++ b/builtin/ls-remote.c
@@ -54,7 +54,7 @@
struct transport *transport;
const struct ref *ref;
struct ref_array ref_array;
- static struct ref_sorting *sorting = NULL, **sorting_tail = &sorting;
+ struct string_list sorting_options = STRING_LIST_INIT_DUP;
struct option options[] = {
OPT__QUIET(&quiet, N_("do not print remote URL")),
@@ -68,7 +68,7 @@
OPT_BIT(0, "refs", &flags, N_("do not show peeled tags"), REF_NORMAL),
OPT_BOOL(0, "get-url", &get_url,
N_("take url.<base>.insteadOf into account")),
- OPT_REF_SORT(sorting_tail),
+ OPT_REF_SORT(&sorting_options),
OPT_SET_INT_F(0, "exit-code", &status,
N_("exit with exit code 2 if no matching refs are found"),
2, PARSE_OPT_NOCOMPLETE),
@@ -86,8 +86,6 @@
packet_trace_identity("ls-remote");
- UNLEAK(sorting);
-
if (argc > 1) {
int i;
CALLOC_ARRAY(pattern, argc);
@@ -139,8 +137,13 @@
item->symref = xstrdup_or_null(ref->symref);
}
- if (sorting)
+ if (sorting_options.nr) {
+ struct ref_sorting *sorting;
+
+ sorting = ref_sorting_options(&sorting_options);
ref_array_sort(sorting, &ref_array);
+ ref_sorting_release(sorting);
+ }
for (i = 0; i < ref_array.nr; i++) {
const struct ref_array_item *ref = ref_array.items[i];
diff --git a/builtin/merge-file.c b/builtin/merge-file.c
index 06a2f90..e695867 100644
--- a/builtin/merge-file.c
+++ b/builtin/merge-file.c
@@ -34,6 +34,8 @@
struct option options[] = {
OPT_BOOL('p', "stdout", &to_stdout, N_("send results to standard output")),
OPT_SET_INT(0, "diff3", &xmp.style, N_("use a diff3 based merge"), XDL_MERGE_DIFF3),
+ OPT_SET_INT(0, "zdiff3", &xmp.style, N_("use a zealous diff3 based merge"),
+ XDL_MERGE_ZEALOUS_DIFF3),
OPT_SET_INT(0, "ours", &xmp.favor, N_("for conflicts, use our version"),
XDL_MERGE_FAVOR_OURS),
OPT_SET_INT(0, "theirs", &xmp.favor, N_("for conflicts, use their version"),
diff --git a/builtin/merge.c b/builtin/merge.c
index ea3112e..74e53cf 100644
--- a/builtin/merge.c
+++ b/builtin/merge.c
@@ -87,6 +87,7 @@
static const char *sign_commit;
static int autostash;
static int no_verify;
+static char *into_name;
static struct strategy all_strategy[] = {
{ "recursive", NO_TRIVIAL },
@@ -286,6 +287,8 @@
{ OPTION_LOWLEVEL_CALLBACK, 'F', "file", &merge_msg, N_("path"),
N_("read message from file"), PARSE_OPT_NONEG,
NULL, 0, option_read_message },
+ OPT_STRING(0, "into-name", &into_name, N_("name"),
+ N_("use <name> instead of the real target")),
OPT__VERBOSITY(&verbosity),
OPT_BOOL(0, "abort", &abort_current_merge,
N_("abort the current in-progress merge")),
@@ -310,10 +313,9 @@
int len;
struct child_process cp = CHILD_PROCESS_INIT;
struct strbuf buffer = STRBUF_INIT;
- const char *argv[] = {"stash", "create", NULL};
int rc = -1;
- cp.argv = argv;
+ strvec_pushl(&cp.args, "stash", "create", NULL);
cp.out = -1;
cp.git_cmd = 1;
@@ -1122,6 +1124,7 @@
opts.add_title = !have_message;
opts.shortlog_len = shortlog_len;
opts.credit_people = (0 < option_edit);
+ opts.into_name = into_name;
fmt_merge_msg(merge_names, merge_msg, &opts);
if (merge_msg->len)
@@ -1397,9 +1400,9 @@
if (squash) {
if (fast_forward == FF_NO)
- die(_("You cannot combine --squash with --no-ff."));
+ die(_("options '%s' and '%s' cannot be used together"), "--squash", "--no-ff.");
if (option_commit > 0)
- die(_("You cannot combine --squash with --commit."));
+ die(_("options '%s' and '%s' cannot be used together"), "--squash", "--commit.");
/*
* squash can now silently disable option_commit - this is not
* a problem as it is only overriding the default, not a user
diff --git a/builtin/multi-pack-index.c b/builtin/multi-pack-index.c
index 075d15d..4480ba3 100644
--- a/builtin/multi-pack-index.c
+++ b/builtin/multi-pack-index.c
@@ -167,6 +167,8 @@
usage_with_options(builtin_multi_pack_index_verify_usage,
options);
+ FREE_AND_NULL(options);
+
return verify_midx_file(the_repository, opts.object_dir, opts.flags);
}
@@ -191,6 +193,8 @@
usage_with_options(builtin_multi_pack_index_expire_usage,
options);
+ FREE_AND_NULL(options);
+
return expire_midx_packs(the_repository, opts.object_dir, opts.flags);
}
diff --git a/builtin/name-rev.c b/builtin/name-rev.c
index b221d30..27f6015 100644
--- a/builtin/name-rev.c
+++ b/builtin/name-rev.c
@@ -44,11 +44,20 @@
return is_valid_rev_name(name) ? name : NULL;
}
+static int effective_distance(int distance, int generation)
+{
+ return distance + (generation > 0 ? MERGE_TRAVERSAL_WEIGHT : 0);
+}
+
static int is_better_name(struct rev_name *name,
timestamp_t taggerdate,
+ int generation,
int distance,
int from_tag)
{
+ int name_distance = effective_distance(name->distance, name->generation);
+ int new_distance = effective_distance(distance, generation);
+
/*
* When comparing names based on tags, prefer names
* based on the older tag, even if it is farther away.
@@ -56,7 +65,7 @@
if (from_tag && name->from_tag)
return (name->taggerdate > taggerdate ||
(name->taggerdate == taggerdate &&
- name->distance > distance));
+ name_distance > new_distance));
/*
* We know that at least one of them is a non-tag at this point.
@@ -69,8 +78,8 @@
* We are now looking at two non-tags. Tiebreak to favor
* shorter hops.
*/
- if (name->distance != distance)
- return name->distance > distance;
+ if (name_distance != new_distance)
+ return name_distance > new_distance;
/* ... or tiebreak to favor older date */
if (name->taggerdate != taggerdate)
@@ -88,7 +97,7 @@
struct rev_name *name = commit_rev_name_at(&rev_names, commit);
if (is_valid_rev_name(name)) {
- if (!is_better_name(name, taggerdate, distance, from_tag))
+ if (!is_better_name(name, taggerdate, generation, distance, from_tag))
return NULL;
/*
diff --git a/builtin/notes.c b/builtin/notes.c
index 71c5958..05d6048 100644
--- a/builtin/notes.c
+++ b/builtin/notes.c
@@ -134,14 +134,13 @@
static void write_commented_object(int fd, const struct object_id *object)
{
- const char *show_args[5] =
- {"show", "--stat", "--no-notes", oid_to_hex(object), NULL};
struct child_process show = CHILD_PROCESS_INIT;
struct strbuf buf = STRBUF_INIT;
struct strbuf cbuf = STRBUF_INIT;
/* Invoke "git show --stat --no-notes $object" */
- show.argv = show_args;
+ strvec_pushl(&show.args, "show", "--stat", "--no-notes",
+ oid_to_hex(object), NULL);
show.no_stdin = 1;
show.out = -1;
show.err = 0;
@@ -201,11 +200,12 @@
static void write_note_data(struct note_data *d, struct object_id *oid)
{
if (write_object_file(d->buf.buf, d->buf.len, blob_type, oid)) {
- error(_("unable to write note object"));
+ int status = die_message(_("unable to write note object"));
+
if (d->edit_path)
- error(_("the note contents have been left in %s"),
- d->edit_path);
- exit(128);
+ die_message(_("the note contents have been left in %s"),
+ d->edit_path);
+ exit(status);
}
}
@@ -861,15 +861,19 @@
update_ref(msg.buf, default_notes_ref(), &result_oid, NULL, 0,
UPDATE_REFS_DIE_ON_ERR);
else { /* Merge has unresolved conflicts */
+ struct worktree **worktrees;
const struct worktree *wt;
/* Update .git/NOTES_MERGE_PARTIAL with partial merge result */
update_ref(msg.buf, "NOTES_MERGE_PARTIAL", &result_oid, NULL,
0, UPDATE_REFS_DIE_ON_ERR);
/* Store ref-to-be-updated into .git/NOTES_MERGE_REF */
- wt = find_shared_symref("NOTES_MERGE_REF", default_notes_ref());
+ worktrees = get_worktrees();
+ wt = find_shared_symref(worktrees, "NOTES_MERGE_REF",
+ default_notes_ref());
if (wt)
die(_("a notes merge into %s is already in-progress at %s"),
default_notes_ref(), wt->path);
+ free_worktrees(worktrees);
if (create_symref("NOTES_MERGE_REF", default_notes_ref(), NULL))
die(_("failed to store link to current notes ref (%s)"),
default_notes_ref());
diff --git a/builtin/pack-objects.c b/builtin/pack-objects.c
index 1a3dd44..ba2006f 100644
--- a/builtin/pack-objects.c
+++ b/builtin/pack-objects.c
@@ -3397,7 +3397,7 @@
if (feof(stdin))
break;
if (!ferror(stdin))
- die("BUG: fgets returned NULL, not EOF, not error!");
+ BUG("fgets returned NULL, not EOF, not error!");
if (errno != EINTR)
die_errno("fgets");
clearerr(stdin);
@@ -4070,7 +4070,7 @@
die(_("--thin cannot be used to build an indexable pack"));
if (keep_unreachable && unpack_unreachable)
- die(_("--keep-unreachable and --unpack-unreachable are incompatible"));
+ die(_("options '%s' and '%s' cannot be used together"), "--keep-unreachable", "--unpack-unreachable");
if (!rev_list_all || !rev_list_reflog || !rev_list_index)
unpack_unreachable_expiration = 0;
@@ -4148,11 +4148,10 @@
read_packs_list_from_stdin();
if (rev_list_unpacked)
add_unreachable_loose_objects();
- } else if (!use_internal_rev_list)
+ } else if (!use_internal_rev_list) {
read_object_list_from_stdin();
- else {
+ } else {
get_object_list(rp.nr, rp.v);
- strvec_clear(&rp);
}
cleanup_preferred_base();
if (include_tag && nr_result)
@@ -4162,7 +4161,7 @@
the_repository);
if (non_empty && !nr_result)
- return 0;
+ goto cleanup;
if (nr_result) {
trace2_region_enter("pack-objects", "prepare-pack",
the_repository);
@@ -4183,5 +4182,9 @@
" pack-reused %"PRIu32),
written, written_delta, reused, reused_delta,
reuse_packfile_objects);
+
+cleanup:
+ strvec_clear(&rp);
+
return 0;
}
diff --git a/builtin/prune.c b/builtin/prune.c
index 485c9a3..c2bcdc0 100644
--- a/builtin/prune.c
+++ b/builtin/prune.c
@@ -26,10 +26,22 @@
return error("Could not stat '%s'", fullpath);
if (st.st_mtime > expire)
return 0;
- if (show_only || verbose)
- printf("Removing stale temporary file %s\n", fullpath);
- if (!show_only)
- unlink_or_warn(fullpath);
+ if (S_ISDIR(st.st_mode)) {
+ if (show_only || verbose)
+ printf("Removing stale temporary directory %s\n", fullpath);
+ if (!show_only) {
+ struct strbuf remove_dir_buf = STRBUF_INIT;
+
+ strbuf_addstr(&remove_dir_buf, fullpath);
+ remove_dir_recursively(&remove_dir_buf, 0);
+ strbuf_release(&remove_dir_buf);
+ }
+ } else {
+ if (show_only || verbose)
+ printf("Removing stale temporary file %s\n", fullpath);
+ if (!show_only)
+ unlink_or_warn(fullpath);
+ }
return 0;
}
diff --git a/builtin/pull.c b/builtin/pull.c
index 1cfaf9f..100cbf9 100644
--- a/builtin/pull.c
+++ b/builtin/pull.c
@@ -970,7 +970,7 @@
"You can do so by running one of the following commands sometime before\n"
"your next pull:\n"
"\n"
- " git config pull.rebase false # merge (the default strategy)\n"
+ " git config pull.rebase false # merge\n"
" git config pull.rebase true # rebase\n"
" git config pull.ff only # fast-forward only\n"
"\n"
@@ -994,6 +994,8 @@
set_reflog_message(argc, argv);
git_config(git_pull_config, NULL);
+ prepare_repo_settings(the_repository);
+ the_repository->settings.command_requires_full_index = 0;
argc = parse_options(argc, argv, prefix, pull_options, pull_usage, 0);
diff --git a/builtin/push.c b/builtin/push.c
index 4b026ce..359db90 100644
--- a/builtin/push.c
+++ b/builtin/push.c
@@ -589,7 +589,7 @@
set_push_cert_flags(&flags, push_cert);
if (deleterefs && (tags || (flags & (TRANSPORT_PUSH_ALL | TRANSPORT_PUSH_MIRROR))))
- die(_("--delete is incompatible with --all, --mirror and --tags"));
+ die(_("options '%s' and '%s' cannot be used together"), "--delete", "--all/--mirror/--tags");
if (deleterefs && argc < 2)
die(_("--delete doesn't make sense without any refs"));
@@ -627,18 +627,18 @@
if (flags & TRANSPORT_PUSH_ALL) {
if (tags)
- die(_("--all and --tags are incompatible"));
+ die(_("options '%s' and '%s' cannot be used together"), "--all", "--tags");
if (argc >= 2)
die(_("--all can't be combined with refspecs"));
}
if (flags & TRANSPORT_PUSH_MIRROR) {
if (tags)
- die(_("--mirror and --tags are incompatible"));
+ die(_("options '%s' and '%s' cannot be used together"), "--mirror", "--tags");
if (argc >= 2)
die(_("--mirror can't be combined with refspecs"));
}
if ((flags & TRANSPORT_PUSH_ALL) && (flags & TRANSPORT_PUSH_MIRROR))
- die(_("--all and --mirror are incompatible"));
+ die(_("options '%s' and '%s' cannot be used together"), "--all", "--mirror");
if (!is_empty_cas(&cas) && (flags & TRANSPORT_PUSH_FORCE_IF_INCLUDES))
cas.use_force_if_includes = 1;
diff --git a/builtin/rebase.c b/builtin/rebase.c
index 34b4744..36490d0 100644
--- a/builtin/rebase.c
+++ b/builtin/rebase.c
@@ -1190,13 +1190,13 @@
if (keep_base) {
if (options.onto_name)
- die(_("cannot combine '--keep-base' with '--onto'"));
+ die(_("options '%s' and '%s' cannot be used together"), "--keep-base", "--onto");
if (options.root)
- die(_("cannot combine '--keep-base' with '--root'"));
+ die(_("options '%s' and '%s' cannot be used together"), "--keep-base", "--root");
}
if (options.root && options.fork_point > 0)
- die(_("cannot combine '--root' with '--fork-point'"));
+ die(_("options '%s' and '%s' cannot be used together"), "--root", "--fork-point");
if (action != ACTION_NONE && !in_progress)
die(_("No rebase in progress?"));
@@ -1460,8 +1460,8 @@
if (i >= 0) {
if (is_merge(&options))
- die(_("cannot combine apply options with "
- "merge options"));
+ die(_("apply options and merge options "
+ "cannot be used together"));
else
options.type = REBASE_APPLY;
}
diff --git a/builtin/receive-pack.c b/builtin/receive-pack.c
index 49b846d..9f4a0b8 100644
--- a/builtin/receive-pack.c
+++ b/builtin/receive-pack.c
@@ -175,7 +175,7 @@
strbuf_addf(&fsck_msg_types, "%c%s=%s",
fsck_msg_types.len ? ',' : '=', var, value);
else
- warning("Skipping unknown msg id '%s'", var);
+ warning("skipping unknown msg id '%s'", var);
return 0;
}
@@ -769,8 +769,10 @@
memset(&sigcheck, '\0', sizeof(sigcheck));
bogs = parse_signed_buffer(push_cert.buf, push_cert.len);
- check_signature(push_cert.buf, bogs, push_cert.buf + bogs,
- push_cert.len - bogs, &sigcheck);
+ sigcheck.payload = xmemdupz(push_cert.buf, bogs);
+ sigcheck.payload_len = bogs;
+ check_signature(&sigcheck, push_cert.buf + bogs,
+ push_cert.len - bogs);
nonce_status = check_nonce(push_cert.buf, bogs);
}
@@ -812,16 +814,13 @@
{
struct child_process proc = CHILD_PROCESS_INIT;
struct async muxer;
- const char *argv[2];
int code;
+ const char *hook_path = find_hook(hook_name);
- argv[0] = find_hook(hook_name);
- if (!argv[0])
+ if (!hook_path)
return 0;
- argv[1] = NULL;
-
- proc.argv = argv;
+ strvec_push(&proc.args, hook_path);
proc.in = -1;
proc.stdout_to_stderr = 1;
proc.trace2_hook_name = hook_name;
@@ -943,23 +942,21 @@
static int run_update_hook(struct command *cmd)
{
- const char *argv[5];
struct child_process proc = CHILD_PROCESS_INIT;
int code;
+ const char *hook_path = find_hook("update");
- argv[0] = find_hook("update");
- if (!argv[0])
+ if (!hook_path)
return 0;
- argv[1] = cmd->ref_name;
- argv[2] = oid_to_hex(&cmd->old_oid);
- argv[3] = oid_to_hex(&cmd->new_oid);
- argv[4] = NULL;
+ strvec_push(&proc.args, hook_path);
+ strvec_push(&proc.args, cmd->ref_name);
+ strvec_push(&proc.args, oid_to_hex(&cmd->old_oid));
+ strvec_push(&proc.args, oid_to_hex(&cmd->new_oid));
proc.no_stdin = 1;
proc.stdout_to_stderr = 1;
proc.err = use_sideband ? -1 : 0;
- proc.argv = argv;
proc.trace2_hook_name = "update";
code = start_command(&proc);
@@ -1117,22 +1114,20 @@
struct child_process proc = CHILD_PROCESS_INIT;
struct async muxer;
struct command *cmd;
- const char *argv[2];
struct packet_reader reader;
struct strbuf cap = STRBUF_INIT;
struct strbuf errmsg = STRBUF_INIT;
int hook_use_push_options = 0;
int version = 0;
int code;
+ const char *hook_path = find_hook("proc-receive");
- argv[0] = find_hook("proc-receive");
- if (!argv[0]) {
+ if (!hook_path) {
rp_error("cannot find hook 'proc-receive'");
return -1;
}
- argv[1] = NULL;
- proc.argv = argv;
+ strvec_push(&proc.args, hook_path);
proc.in = -1;
proc.out = -1;
proc.trace2_hook_name = "proc-receive";
@@ -1370,23 +1365,11 @@
struct strvec *env,
const char *work_tree)
{
- const char *update_refresh[] = {
- "update-index", "-q", "--ignore-submodules", "--refresh", NULL
- };
- const char *diff_files[] = {
- "diff-files", "--quiet", "--ignore-submodules", "--", NULL
- };
- const char *diff_index[] = {
- "diff-index", "--quiet", "--cached", "--ignore-submodules",
- NULL, "--", NULL
- };
- const char *read_tree[] = {
- "read-tree", "-u", "-m", NULL, NULL
- };
struct child_process child = CHILD_PROCESS_INIT;
- child.argv = update_refresh;
- child.env = env->v;
+ strvec_pushl(&child.args, "update-index", "-q", "--ignore-submodules",
+ "--refresh", NULL);
+ strvec_pushv(&child.env_array, env->v);
child.dir = work_tree;
child.no_stdin = 1;
child.stdout_to_stderr = 1;
@@ -1396,8 +1379,9 @@
/* run_command() does not clean up completely; reinitialize */
child_process_init(&child);
- child.argv = diff_files;
- child.env = env->v;
+ strvec_pushl(&child.args, "diff-files", "--quiet",
+ "--ignore-submodules", "--", NULL);
+ strvec_pushv(&child.env_array, env->v);
child.dir = work_tree;
child.no_stdin = 1;
child.stdout_to_stderr = 1;
@@ -1405,12 +1389,13 @@
if (run_command(&child))
return "Working directory has unstaged changes";
- /* diff-index with either HEAD or an empty tree */
- diff_index[4] = head_has_history() ? "HEAD" : empty_tree_oid_hex();
-
child_process_init(&child);
- child.argv = diff_index;
- child.env = env->v;
+ strvec_pushl(&child.args, "diff-index", "--quiet", "--cached",
+ "--ignore-submodules",
+ /* diff-index with either HEAD or an empty tree */
+ head_has_history() ? "HEAD" : empty_tree_oid_hex(),
+ "--", NULL);
+ strvec_pushv(&child.env_array, env->v);
child.no_stdin = 1;
child.no_stdout = 1;
child.stdout_to_stderr = 0;
@@ -1418,10 +1403,10 @@
if (run_command(&child))
return "Working directory has staged changes";
- read_tree[3] = hash_to_hex(sha1);
child_process_init(&child);
- child.argv = read_tree;
- child.env = env->v;
+ strvec_pushl(&child.args, "read-tree", "-u", "-m", hash_to_hex(sha1),
+ NULL);
+ strvec_pushv(&child.env_array, env->v);
child.dir = work_tree;
child.no_stdin = 1;
child.no_stdout = 1;
@@ -1449,29 +1434,22 @@
static const char *update_worktree(unsigned char *sha1, const struct worktree *worktree)
{
- const char *retval, *work_tree, *git_dir = NULL;
+ const char *retval, *git_dir;
struct strvec env = STRVEC_INIT;
- if (worktree && worktree->path)
- work_tree = worktree->path;
- else if (git_work_tree_cfg)
- work_tree = git_work_tree_cfg;
- else
- work_tree = "..";
+ if (!worktree || !worktree->path)
+ BUG("worktree->path must be non-NULL");
- if (is_bare_repository())
+ if (worktree->is_bare)
return "denyCurrentBranch = updateInstead needs a worktree";
- if (worktree)
- git_dir = get_worktree_git_dir(worktree);
- if (!git_dir)
- git_dir = get_git_dir();
+ git_dir = get_worktree_git_dir(worktree);
strvec_pushf(&env, "GIT_DIR=%s", absolute_path(git_dir));
if (!hook_exists(push_to_checkout_hook))
- retval = push_to_deploy(sha1, &env, work_tree);
+ retval = push_to_deploy(sha1, &env, worktree->path);
else
- retval = push_to_checkout(sha1, &env, work_tree);
+ retval = push_to_checkout(sha1, &env, worktree->path);
strvec_clear(&env);
return retval;
@@ -1486,19 +1464,22 @@
struct object_id *old_oid = &cmd->old_oid;
struct object_id *new_oid = &cmd->new_oid;
int do_update_worktree = 0;
- const struct worktree *worktree = is_bare_repository() ? NULL : find_shared_symref("HEAD", name);
+ struct worktree **worktrees = get_worktrees();
+ const struct worktree *worktree =
+ find_shared_symref(worktrees, "HEAD", name);
/* only refs/... are allowed */
if (!starts_with(name, "refs/") || check_refname_format(name + 5, 0)) {
rp_error("refusing to create funny ref '%s' remotely", name);
- return "funny refname";
+ ret = "funny refname";
+ goto out;
}
strbuf_addf(&namespaced_name_buf, "%s%s", get_git_namespace(), name);
free(namespaced_name);
namespaced_name = strbuf_detach(&namespaced_name_buf, NULL);
- if (worktree) {
+ if (worktree && !worktree->is_bare) {
switch (deny_current_branch) {
case DENY_IGNORE:
break;
@@ -1510,7 +1491,8 @@
rp_error("refusing to update checked out branch: %s", name);
if (deny_current_branch == DENY_UNCONFIGURED)
refuse_unconfigured_deny();
- return "branch is currently checked out";
+ ret = "branch is currently checked out";
+ goto out;
case DENY_UPDATE_INSTEAD:
/* pass -- let other checks intervene first */
do_update_worktree = 1;
@@ -1521,13 +1503,15 @@
if (!is_null_oid(new_oid) && !has_object_file(new_oid)) {
error("unpack should have generated %s, "
"but I can't find it!", oid_to_hex(new_oid));
- return "bad pack";
+ ret = "bad pack";
+ goto out;
}
if (!is_null_oid(old_oid) && is_null_oid(new_oid)) {
if (deny_deletes && starts_with(name, "refs/heads/")) {
rp_error("denying ref deletion for %s", name);
- return "deletion prohibited";
+ ret = "deletion prohibited";
+ goto out;
}
if (worktree || (head_name && !strcmp(namespaced_name, head_name))) {
@@ -1543,9 +1527,11 @@
if (deny_delete_current == DENY_UNCONFIGURED)
refuse_unconfigured_deny_delete_current();
rp_error("refusing to delete the current branch: %s", name);
- return "deletion of the current branch prohibited";
+ ret = "deletion of the current branch prohibited";
+ goto out;
default:
- return "Invalid denyDeleteCurrent setting";
+ ret = "Invalid denyDeleteCurrent setting";
+ goto out;
}
}
}
@@ -1563,25 +1549,28 @@
old_object->type != OBJ_COMMIT ||
new_object->type != OBJ_COMMIT) {
error("bad sha1 objects for %s", name);
- return "bad ref";
+ ret = "bad ref";
+ goto out;
}
old_commit = (struct commit *)old_object;
new_commit = (struct commit *)new_object;
if (!in_merge_bases(old_commit, new_commit)) {
rp_error("denying non-fast-forward %s"
" (you should pull first)", name);
- return "non-fast-forward";
+ ret = "non-fast-forward";
+ goto out;
}
}
if (run_update_hook(cmd)) {
rp_error("hook declined to update %s", name);
- return "hook declined";
+ ret = "hook declined";
+ goto out;
}
if (do_update_worktree) {
- ret = update_worktree(new_oid->hash, find_shared_symref("HEAD", name));
+ ret = update_worktree(new_oid->hash, worktree);
if (ret)
- return ret;
+ goto out;
}
if (is_null_oid(new_oid)) {
@@ -1589,9 +1578,9 @@
if (!parse_object(the_repository, old_oid)) {
old_oid = NULL;
if (ref_exists(name)) {
- rp_warning("Allowing deletion of corrupt ref.");
+ rp_warning("allowing deletion of corrupt ref");
} else {
- rp_warning("Deleting a non-existent ref.");
+ rp_warning("deleting a non-existent ref");
cmd->did_not_exist = 1;
}
}
@@ -1600,17 +1589,19 @@
old_oid,
0, "push", &err)) {
rp_error("%s", err.buf);
- strbuf_release(&err);
- return "failed to delete";
+ ret = "failed to delete";
+ } else {
+ ret = NULL; /* good */
}
strbuf_release(&err);
- return NULL; /* good */
}
else {
struct strbuf err = STRBUF_INIT;
if (shallow_update && si->shallow_ref[cmd->index] &&
- update_shallow_ref(cmd, si))
- return "shallow error";
+ update_shallow_ref(cmd, si)) {
+ ret = "shallow error";
+ goto out;
+ }
if (ref_transaction_update(transaction,
namespaced_name,
@@ -1618,14 +1609,16 @@
0, "push",
&err)) {
rp_error("%s", err.buf);
- strbuf_release(&err);
-
- return "failed to update ref";
+ ret = "failed to update ref";
+ } else {
+ ret = NULL; /* good */
}
strbuf_release(&err);
-
- return NULL; /* good */
}
+
+out:
+ free_worktrees(worktrees);
+ return ret;
}
static void run_update_post_hook(struct command *commands)
@@ -2213,13 +2206,14 @@
strvec_push(&child.args, alt_shallow_file);
}
- tmp_objdir = tmp_objdir_create();
+ tmp_objdir = tmp_objdir_create("incoming");
if (!tmp_objdir) {
if (err_fd > 0)
close(err_fd);
return "unable to create temporary object directory";
}
- child.env = tmp_objdir_env(tmp_objdir);
+ if (tmp_objdir)
+ strvec_pushv(&child.env_array, tmp_objdir_env(tmp_objdir));
/*
* Normally we just pass the tmp_objdir environment to the child
@@ -2490,9 +2484,9 @@
argc = parse_options(argc, argv, prefix, options, receive_pack_usage, 0);
if (argc > 1)
- usage_msg_opt(_("Too many arguments."), receive_pack_usage, options);
+ usage_msg_opt(_("too many arguments"), receive_pack_usage, options);
if (argc == 0)
- usage_msg_opt(_("You must specify a directory."), receive_pack_usage, options);
+ usage_msg_opt(_("you must specify a directory"), receive_pack_usage, options);
service_dir = argv[0];
@@ -2566,25 +2560,25 @@
&push_options);
if (pack_lockfile)
unlink_or_warn(pack_lockfile);
+ sigchain_push(SIGPIPE, SIG_IGN);
if (report_status_v2)
report_v2(commands, unpack_status);
else if (report_status)
report(commands, unpack_status);
+ sigchain_pop(SIGPIPE);
run_receive_hook(commands, "post-receive", 1,
&push_options);
run_update_post_hook(commands);
string_list_clear(&push_options, 0);
if (auto_gc) {
- const char *argv_gc_auto[] = {
- "gc", "--auto", "--quiet", NULL,
- };
struct child_process proc = CHILD_PROCESS_INIT;
proc.no_stdin = 1;
proc.stdout_to_stderr = 1;
proc.err = use_sideband ? -1 : 0;
proc.git_cmd = proc.close_object_store = 1;
- proc.argv = argv_gc_auto;
+ strvec_pushl(&proc.args, "gc", "--auto", "--quiet",
+ NULL);
if (!start_command(&proc)) {
if (use_sideband)
diff --git a/builtin/reflog.c b/builtin/reflog.c
index 175c83e..a4b1dd2 100644
--- a/builtin/reflog.c
+++ b/builtin/reflog.c
@@ -28,7 +28,6 @@
static timestamp_t default_reflog_expire_unreachable;
struct cmd_reflog_expire_cb {
- struct rev_info revs;
int stalefix;
timestamp_t expire_total;
timestamp_t expire_unreachable;
@@ -46,18 +45,12 @@
struct cmd_reflog_expire_cb cmd;
struct commit *tip_commit;
struct commit_list *tips;
+ unsigned int dry_run:1;
};
-struct collected_reflog {
- struct object_id oid;
- char reflog[FLEX_ARRAY];
-};
-
-struct collect_reflog_cb {
- struct collected_reflog **e;
- int alloc;
- int nr;
- struct worktree *wt;
+struct worktree_reflogs {
+ struct worktree *worktree;
+ struct string_list reflogs;
};
/* Remember to update object flag allocation in object.h */
@@ -310,10 +303,15 @@
return 1;
if (timestamp < cb->cmd.expire_unreachable) {
- if (cb->unreachable_expire_kind == UE_ALWAYS)
+ switch (cb->unreachable_expire_kind) {
+ case UE_ALWAYS:
return 1;
- if (unreachable(cb, old_commit, ooid) || unreachable(cb, new_commit, noid))
- return 1;
+ case UE_NORMAL:
+ case UE_HEAD:
+ if (unreachable(cb, old_commit, ooid) || unreachable(cb, new_commit, noid))
+ return 1;
+ break;
+ }
}
if (cb->cmd.recno && --(cb->cmd.recno) == 0)
@@ -322,6 +320,28 @@
return 0;
}
+static int should_expire_reflog_ent_verbose(struct object_id *ooid,
+ struct object_id *noid,
+ const char *email,
+ timestamp_t timestamp, int tz,
+ const char *message, void *cb_data)
+{
+ struct expire_reflog_policy_cb *cb = cb_data;
+ int expire;
+
+ expire = should_expire_reflog_ent(ooid, noid, email, timestamp, tz,
+ message, cb);
+
+ if (!expire)
+ printf("keep %s", message);
+ else if (cb->dry_run)
+ printf("would prune %s", message);
+ else
+ printf("prune %s", message);
+
+ return expire;
+}
+
static int push_tip_to_list(const char *refname, const struct object_id *oid,
int flags, void *cb_data)
{
@@ -355,75 +375,71 @@
void *cb_data)
{
struct expire_reflog_policy_cb *cb = cb_data;
+ struct commit_list *elem;
+ struct commit *commit = NULL;
if (!cb->cmd.expire_unreachable || is_head(refname)) {
- cb->tip_commit = NULL;
cb->unreachable_expire_kind = UE_HEAD;
} else {
- cb->tip_commit = lookup_commit_reference_gently(the_repository,
- oid, 1);
- if (!cb->tip_commit)
- cb->unreachable_expire_kind = UE_ALWAYS;
- else
- cb->unreachable_expire_kind = UE_NORMAL;
+ commit = lookup_commit(the_repository, oid);
+ cb->unreachable_expire_kind = commit ? UE_NORMAL : UE_ALWAYS;
}
if (cb->cmd.expire_unreachable <= cb->cmd.expire_total)
cb->unreachable_expire_kind = UE_ALWAYS;
- cb->mark_list = NULL;
- cb->tips = NULL;
- if (cb->unreachable_expire_kind != UE_ALWAYS) {
- if (cb->unreachable_expire_kind == UE_HEAD) {
- struct commit_list *elem;
-
- for_each_ref(push_tip_to_list, &cb->tips);
- for (elem = cb->tips; elem; elem = elem->next)
- commit_list_insert(elem->item, &cb->mark_list);
- } else {
- commit_list_insert(cb->tip_commit, &cb->mark_list);
- }
- cb->mark_limit = cb->cmd.expire_total;
- mark_reachable(cb);
+ switch (cb->unreachable_expire_kind) {
+ case UE_ALWAYS:
+ return;
+ case UE_HEAD:
+ for_each_ref(push_tip_to_list, &cb->tips);
+ for (elem = cb->tips; elem; elem = elem->next)
+ commit_list_insert(elem->item, &cb->mark_list);
+ break;
+ case UE_NORMAL:
+ commit_list_insert(commit, &cb->mark_list);
+ /* For reflog_expiry_cleanup() below */
+ cb->tip_commit = commit;
}
+ cb->mark_limit = cb->cmd.expire_total;
+ mark_reachable(cb);
}
static void reflog_expiry_cleanup(void *cb_data)
{
struct expire_reflog_policy_cb *cb = cb_data;
+ struct commit_list *elem;
- if (cb->unreachable_expire_kind != UE_ALWAYS) {
- if (cb->unreachable_expire_kind == UE_HEAD) {
- struct commit_list *elem;
- for (elem = cb->tips; elem; elem = elem->next)
- clear_commit_marks(elem->item, REACHABLE);
- free_commit_list(cb->tips);
- } else {
- clear_commit_marks(cb->tip_commit, REACHABLE);
- }
+ switch (cb->unreachable_expire_kind) {
+ case UE_ALWAYS:
+ return;
+ case UE_HEAD:
+ for (elem = cb->tips; elem; elem = elem->next)
+ clear_commit_marks(elem->item, REACHABLE);
+ free_commit_list(cb->tips);
+ break;
+ case UE_NORMAL:
+ clear_commit_marks(cb->tip_commit, REACHABLE);
+ break;
}
}
static int collect_reflog(const char *ref, const struct object_id *oid, int unused, void *cb_data)
{
- struct collected_reflog *e;
- struct collect_reflog_cb *cb = cb_data;
+ struct worktree_reflogs *cb = cb_data;
+ struct worktree *worktree = cb->worktree;
struct strbuf newref = STRBUF_INIT;
/*
* Avoid collecting the same shared ref multiple times because
* they are available via all worktrees.
*/
- if (!cb->wt->is_current && ref_type(ref) == REF_TYPE_NORMAL)
+ if (!worktree->is_current && ref_type(ref) == REF_TYPE_NORMAL)
return 0;
- strbuf_worktree_ref(cb->wt, &newref, ref);
- FLEX_ALLOC_STR(e, reflog, newref.buf);
- strbuf_release(&newref);
+ strbuf_worktree_ref(worktree, &newref, ref);
+ string_list_append_nodup(&cb->reflogs, strbuf_detach(&newref, NULL));
- oidcpy(&e->oid, oid);
- ALLOC_GROW(cb->e, cb->nr + 1, cb->alloc);
- cb->e[cb->nr++] = e;
return 0;
}
@@ -541,11 +557,13 @@
static int cmd_reflog_expire(int argc, const char **argv, const char *prefix)
{
- struct expire_reflog_policy_cb cb;
+ struct cmd_reflog_expire_cb cmd = { 0 };
timestamp_t now = time(NULL);
int i, status, do_all, all_worktrees = 1;
int explicit_expiry = 0;
unsigned int flags = 0;
+ int verbose = 0;
+ reflog_expiry_should_prune_fn *should_prune_fn = should_expire_reflog_ent;
default_reflog_expire_unreachable = now - 30 * 24 * 3600;
default_reflog_expire = now - 90 * 24 * 3600;
@@ -553,10 +571,9 @@
save_commit_buffer = 0;
do_all = status = 0;
- memset(&cb, 0, sizeof(cb));
- cb.cmd.expire_total = default_reflog_expire;
- cb.cmd.expire_unreachable = default_reflog_expire_unreachable;
+ cmd.expire_total = default_reflog_expire;
+ cmd.expire_unreachable = default_reflog_expire_unreachable;
for (i = 1; i < argc; i++) {
const char *arg = argv[i];
@@ -564,17 +581,17 @@
if (!strcmp(arg, "--dry-run") || !strcmp(arg, "-n"))
flags |= EXPIRE_REFLOGS_DRY_RUN;
else if (skip_prefix(arg, "--expire=", &arg)) {
- if (parse_expiry_date(arg, &cb.cmd.expire_total))
+ if (parse_expiry_date(arg, &cmd.expire_total))
die(_("'%s' is not a valid timestamp"), arg);
explicit_expiry |= EXPIRE_TOTAL;
}
else if (skip_prefix(arg, "--expire-unreachable=", &arg)) {
- if (parse_expiry_date(arg, &cb.cmd.expire_unreachable))
+ if (parse_expiry_date(arg, &cmd.expire_unreachable))
die(_("'%s' is not a valid timestamp"), arg);
explicit_expiry |= EXPIRE_UNREACH;
}
else if (!strcmp(arg, "--stale-fix"))
- cb.cmd.stalefix = 1;
+ cmd.stalefix = 1;
else if (!strcmp(arg, "--rewrite"))
flags |= EXPIRE_REFLOGS_REWRITE;
else if (!strcmp(arg, "--updateref"))
@@ -584,7 +601,7 @@
else if (!strcmp(arg, "--single-worktree"))
all_worktrees = 0;
else if (!strcmp(arg, "--verbose"))
- flags |= EXPIRE_REFLOGS_VERBOSE;
+ verbose = 1;
else if (!strcmp(arg, "--")) {
i++;
break;
@@ -595,54 +612,65 @@
break;
}
+ if (verbose)
+ should_prune_fn = should_expire_reflog_ent_verbose;
+
/*
* We can trust the commits and objects reachable from refs
* even in older repository. We cannot trust what's reachable
* from reflog if the repository was pruned with older git.
*/
- if (cb.cmd.stalefix) {
- repo_init_revisions(the_repository, &cb.cmd.revs, prefix);
- cb.cmd.revs.do_not_die_on_missing_tree = 1;
- cb.cmd.revs.ignore_missing = 1;
- cb.cmd.revs.ignore_missing_links = 1;
- if (flags & EXPIRE_REFLOGS_VERBOSE)
+ if (cmd.stalefix) {
+ struct rev_info revs;
+
+ repo_init_revisions(the_repository, &revs, prefix);
+ revs.do_not_die_on_missing_tree = 1;
+ revs.ignore_missing = 1;
+ revs.ignore_missing_links = 1;
+ if (verbose)
printf(_("Marking reachable objects..."));
- mark_reachable_objects(&cb.cmd.revs, 0, 0, NULL);
- if (flags & EXPIRE_REFLOGS_VERBOSE)
+ mark_reachable_objects(&revs, 0, 0, NULL);
+ if (verbose)
putchar('\n');
}
if (do_all) {
- struct collect_reflog_cb collected;
+ struct worktree_reflogs collected = {
+ .reflogs = STRING_LIST_INIT_DUP,
+ };
+ struct string_list_item *item;
struct worktree **worktrees, **p;
- int i;
- memset(&collected, 0, sizeof(collected));
worktrees = get_worktrees();
for (p = worktrees; *p; p++) {
if (!all_worktrees && !(*p)->is_current)
continue;
- collected.wt = *p;
+ collected.worktree = *p;
refs_for_each_reflog(get_worktree_ref_store(*p),
collect_reflog, &collected);
}
free_worktrees(worktrees);
- for (i = 0; i < collected.nr; i++) {
- struct collected_reflog *e = collected.e[i];
- set_reflog_expiry_param(&cb.cmd, explicit_expiry, e->reflog);
- status |= reflog_expire(e->reflog, flags,
+ for_each_string_list_item(item, &collected.reflogs) {
+ struct expire_reflog_policy_cb cb = {
+ .cmd = cmd,
+ .dry_run = !!(flags & EXPIRE_REFLOGS_DRY_RUN),
+ };
+
+ set_reflog_expiry_param(&cb.cmd, explicit_expiry, item->string);
+ status |= reflog_expire(item->string, flags,
reflog_expiry_prepare,
- should_expire_reflog_ent,
+ should_prune_fn,
reflog_expiry_cleanup,
&cb);
- free(e);
}
- free(collected.e);
+ string_list_clear(&collected.reflogs, 0);
}
for (; i < argc; i++) {
char *ref;
+ struct expire_reflog_policy_cb cb = { .cmd = cmd };
+
if (!dwim_log(argv[i], strlen(argv[i]), NULL, &ref)) {
status |= error(_("%s points nowhere!"), argv[i]);
continue;
@@ -650,7 +678,7 @@
set_reflog_expiry_param(&cb.cmd, explicit_expiry, ref);
status |= reflog_expire(ref, flags,
reflog_expiry_prepare,
- should_expire_reflog_ent,
+ should_prune_fn,
reflog_expiry_cleanup,
&cb);
free(ref);
@@ -662,19 +690,19 @@
const char *email, timestamp_t timestamp, int tz,
const char *message, void *cb_data)
{
- struct expire_reflog_policy_cb *cb = cb_data;
- if (!cb->cmd.expire_total || timestamp < cb->cmd.expire_total)
- cb->cmd.recno++;
+ struct cmd_reflog_expire_cb *cb = cb_data;
+ if (!cb->expire_total || timestamp < cb->expire_total)
+ cb->recno++;
return 0;
}
static int cmd_reflog_delete(int argc, const char **argv, const char *prefix)
{
- struct expire_reflog_policy_cb cb;
+ struct cmd_reflog_expire_cb cmd = { 0 };
int i, status = 0;
unsigned int flags = 0;
-
- memset(&cb, 0, sizeof(cb));
+ int verbose = 0;
+ reflog_expiry_should_prune_fn *should_prune_fn = should_expire_reflog_ent;
for (i = 1; i < argc; i++) {
const char *arg = argv[i];
@@ -685,7 +713,7 @@
else if (!strcmp(arg, "--updateref"))
flags |= EXPIRE_REFLOGS_UPDATE_REF;
else if (!strcmp(arg, "--verbose"))
- flags |= EXPIRE_REFLOGS_VERBOSE;
+ verbose = 1;
else if (!strcmp(arg, "--")) {
i++;
break;
@@ -696,6 +724,9 @@
break;
}
+ if (verbose)
+ should_prune_fn = should_expire_reflog_ent_verbose;
+
if (argc - i < 1)
return error(_("no reflog specified to delete"));
@@ -703,6 +734,9 @@
const char *spec = strstr(argv[i], "@{");
char *ep, *ref;
int recno;
+ struct expire_reflog_policy_cb cb = {
+ .dry_run = !!(flags & EXPIRE_REFLOGS_DRY_RUN),
+ };
if (!spec) {
status |= error(_("not a reflog: %s"), argv[i]);
@@ -716,17 +750,18 @@
recno = strtoul(spec + 2, &ep, 10);
if (*ep == '}') {
- cb.cmd.recno = -recno;
- for_each_reflog_ent(ref, count_reflog_ent, &cb);
+ cmd.recno = -recno;
+ for_each_reflog_ent(ref, count_reflog_ent, &cmd);
} else {
- cb.cmd.expire_total = approxidate(spec + 2);
- for_each_reflog_ent(ref, count_reflog_ent, &cb);
- cb.cmd.expire_total = 0;
+ cmd.expire_total = approxidate(spec + 2);
+ for_each_reflog_ent(ref, count_reflog_ent, &cmd);
+ cmd.expire_total = 0;
}
+ cb.cmd = cmd;
status |= reflog_expire(ref, flags,
reflog_expiry_prepare,
- should_expire_reflog_ent,
+ should_prune_fn,
reflog_expiry_cleanup,
&cb);
free(ref);
diff --git a/builtin/repack.c b/builtin/repack.c
index 0b2d1e5..da1e364 100644
--- a/builtin/repack.c
+++ b/builtin/repack.c
@@ -258,9 +258,11 @@
for_each_packed_object(write_oid, &cmd,
FOR_EACH_OBJECT_PROMISOR_ONLY);
- if (cmd.in == -1)
+ if (cmd.in == -1) {
/* No packed objects; cmd was never started */
+ child_process_clear(&cmd);
return;
+ }
close(cmd.in);
@@ -610,7 +612,7 @@
struct tempfile *refs_snapshot = NULL;
int i, ext, ret;
FILE *out;
- int show_progress = isatty(2);
+ int show_progress;
/* variables to be filled by option parsing */
int pack_everything = 0;
@@ -679,7 +681,7 @@
if (keep_unreachable &&
(unpack_unreachable || (pack_everything & LOOSEN_UNREACHABLE)))
- die(_("--keep-unreachable and -A are incompatible"));
+ die(_("options '%s' and '%s' cannot be used together"), "--keep-unreachable", "-A");
if (write_bitmaps < 0) {
if (!write_midx &&
@@ -691,7 +693,7 @@
write_bitmaps = 0;
}
if (pack_kept_objects < 0)
- pack_kept_objects = write_bitmaps > 0;
+ pack_kept_objects = write_bitmaps > 0 && !write_midx;
if (write_bitmaps && !(pack_everything & ALL_INTO_ONE) && !write_midx)
die(_(incremental_bitmap_conflict_error));
@@ -710,7 +712,7 @@
if (geometric_factor) {
if (pack_everything)
- die(_("--geometric is incompatible with -A, -a"));
+ die(_("options '%s' and '%s' cannot be used together"), "--geometric", "-A/-a");
init_pack_geometry(&geometry);
split_pack_geometry(geometry, geometric_factor);
}
@@ -723,6 +725,8 @@
prepare_pack_objects(&cmd, &po_args);
+ show_progress = !po_args.quiet && isatty(2);
+
strvec_push(&cmd.args, "--keep-true-parents");
if (!pack_kept_objects)
strvec_push(&cmd.args, "--honor-pack-keep");
@@ -842,7 +846,7 @@
fname_old = mkpathdup("%s-%s%s",
packtmp, item->string, exts[ext].name);
- if (((uintptr_t)item->util) & (1 << ext)) {
+ if (((uintptr_t)item->util) & ((uintptr_t)1 << ext)) {
struct stat statbuffer;
if (!stat(fname_old, &statbuffer)) {
statbuffer.st_mode &= ~(S_IWUSR | S_IWGRP | S_IWOTH);
@@ -924,7 +928,7 @@
}
strbuf_release(&buf);
}
- if (!po_args.quiet && show_progress)
+ if (show_progress)
opts |= PRUNE_PACKED_VERBOSE;
prune_packed_objects(opts);
diff --git a/builtin/replace.c b/builtin/replace.c
index 946938d..6ff1734 100644
--- a/builtin/replace.c
+++ b/builtin/replace.c
@@ -258,11 +258,10 @@
return error_errno(_("unable to open %s for reading"), filename);
if (!raw && type == OBJ_TREE) {
- const char *argv[] = { "mktree", NULL };
struct child_process cmd = CHILD_PROCESS_INIT;
struct strbuf result = STRBUF_INIT;
- cmd.argv = argv;
+ strvec_push(&cmd.args, "mktree");
cmd.git_cmd = 1;
cmd.in = fd;
cmd.out = -1;
diff --git a/builtin/reset.c b/builtin/reset.c
index 7393595..b97745e 100644
--- a/builtin/reset.c
+++ b/builtin/reset.c
@@ -25,6 +25,7 @@
#include "cache-tree.h"
#include "submodule.h"
#include "submodule-config.h"
+#include "dir.h"
#define REFRESH_INDEX_DELAY_WARNING_IN_MS (2 * 1000)
@@ -136,21 +137,36 @@
int intent_to_add = *(int *)data;
for (i = 0; i < q->nr; i++) {
+ int pos;
struct diff_filespec *one = q->queue[i]->one;
- int is_missing = !(one->mode && !is_null_oid(&one->oid));
+ int is_in_reset_tree = one->mode && !is_null_oid(&one->oid);
struct cache_entry *ce;
- if (is_missing && !intent_to_add) {
+ if (!is_in_reset_tree && !intent_to_add) {
remove_file_from_cache(one->path);
continue;
}
ce = make_cache_entry(&the_index, one->mode, &one->oid, one->path,
0, 0);
+
+ /*
+ * If the file 1) corresponds to an existing index entry with
+ * skip-worktree set, or 2) does not exist in the index but is
+ * outside the sparse checkout definition, add a skip-worktree bit
+ * to the new index entry. Note that a sparse index will be expanded
+ * if this entry is outside the sparse cone - this is necessary
+ * to properly construct the reset sparse directory.
+ */
+ pos = cache_name_pos(one->path, strlen(one->path));
+ if ((pos >= 0 && ce_skip_worktree(active_cache[pos])) ||
+ (pos < 0 && !path_in_sparse_checkout(one->path, &the_index)))
+ ce->ce_flags |= CE_SKIP_WORKTREE;
+
if (!ce)
die(_("make_cache_entry failed for path '%s'"),
one->path);
- if (is_missing) {
+ if (!is_in_reset_tree) {
ce->ce_flags |= CE_INTENT_TO_ADD;
set_object_name_for_intent_to_add_entry(ce);
}
@@ -158,6 +174,82 @@
}
}
+static int pathspec_needs_expanded_index(const struct pathspec *pathspec)
+{
+ unsigned int i, pos;
+ int res = 0;
+ char *skip_worktree_seen = NULL;
+
+ /*
+ * When using a magic pathspec, assume for the sake of simplicity that
+ * the index needs to be expanded to match all matchable files.
+ */
+ if (pathspec->magic)
+ return 1;
+
+ for (i = 0; i < pathspec->nr; i++) {
+ struct pathspec_item item = pathspec->items[i];
+
+ /*
+ * If the pathspec item has a wildcard, the index should be expanded
+ * if the pathspec has the possibility of matching a subset of entries inside
+ * of a sparse directory (but not the entire directory).
+ *
+ * If the pathspec item is a literal path, the index only needs to be expanded
+ * if a) the pathspec isn't in the sparse checkout cone (to make sure we don't
+ * expand for in-cone files) and b) it doesn't match any sparse directories
+ * (since we can reset whole sparse directories without expanding them).
+ */
+ if (item.nowildcard_len < item.len) {
+ /*
+ * Special case: if the pattern is a path inside the cone
+ * followed by only wildcards, the pattern cannot match
+ * partial sparse directories, so we don't expand the index.
+ */
+ if (path_in_cone_mode_sparse_checkout(item.original, &the_index) &&
+ strspn(item.original + item.nowildcard_len, "*") == item.len - item.nowildcard_len)
+ continue;
+
+ for (pos = 0; pos < active_nr; pos++) {
+ struct cache_entry *ce = active_cache[pos];
+
+ if (!S_ISSPARSEDIR(ce->ce_mode))
+ continue;
+
+ /*
+ * If the pre-wildcard length is longer than the sparse
+ * directory name and the sparse directory is the first
+ * component of the pathspec, need to expand the index.
+ */
+ if (item.nowildcard_len > ce_namelen(ce) &&
+ !strncmp(item.original, ce->name, ce_namelen(ce))) {
+ res = 1;
+ break;
+ }
+
+ /*
+ * If the pre-wildcard length is shorter than the sparse
+ * directory and the pathspec does not match the whole
+ * directory, need to expand the index.
+ */
+ if (!strncmp(item.original, ce->name, item.nowildcard_len) &&
+ wildmatch(item.original, ce->name, 0)) {
+ res = 1;
+ break;
+ }
+ }
+ } else if (!path_in_cone_mode_sparse_checkout(item.original, &the_index) &&
+ !matches_skip_worktree(pathspec, i, &skip_worktree_seen))
+ res = 1;
+
+ if (res > 0)
+ break;
+ }
+
+ free(skip_worktree_seen);
+ return res;
+}
+
static int read_from_tree(const struct pathspec *pathspec,
struct object_id *tree_oid,
int intent_to_add)
@@ -170,7 +262,13 @@
opt.format_callback = update_index_from_diff;
opt.format_callback_data = &intent_to_add;
opt.flags.override_submodule_config = 1;
+ opt.flags.recursive = 1;
opt.repo = the_repository;
+ opt.change = diff_change;
+ opt.add_remove = diff_addremove;
+
+ if (pathspec->nr && the_index.sparse_index && pathspec_needs_expanded_index(pathspec))
+ ensure_full_index(&the_index);
if (do_diff_cache(tree_oid, &opt))
return 1;
@@ -249,9 +347,6 @@
}
*rev_ret = rev;
- if (read_cache() < 0)
- die(_("index file corrupt"));
-
parse_pathspec(pathspec, 0,
PATHSPEC_PREFER_FULL |
(patch_mode ? PATHSPEC_PREFIX_ORIGIN : 0),
@@ -328,16 +423,16 @@
if (pathspec_from_file) {
if (patch_mode)
- die(_("--pathspec-from-file is incompatible with --patch"));
+ die(_("options '%s' and '%s' cannot be used together"), "--pathspec-from-file", "--patch");
if (pathspec.nr)
- die(_("--pathspec-from-file is incompatible with pathspec arguments"));
+ die(_("'%s' and pathspec arguments cannot be used together"), "--pathspec-from-file");
parse_pathspec_file(&pathspec, 0,
PATHSPEC_PREFER_FULL,
prefix, pathspec_from_file, pathspec_file_nul);
} else if (pathspec_file_nul) {
- die(_("--pathspec-file-nul requires --pathspec-from-file"));
+ die(_("the option '%s' requires '%s'"), "--pathspec-file-nul", "--pathspec-from-file");
}
unborn = !strcmp(rev, "HEAD") && get_oid("HEAD", &oid);
@@ -364,7 +459,7 @@
if (patch_mode) {
if (reset_type != NONE)
- die(_("--patch is incompatible with --{hard,mixed,soft}"));
+ die(_("options '%s' and '%s' cannot be used together"), "--patch", "--{hard,mixed,soft}");
trace2_cmd_mode("patch-interactive");
return run_add_interactive(rev, "--patch=reset", &pathspec);
}
@@ -395,7 +490,13 @@
_(reset_type_names[reset_type]));
if (intent_to_add && reset_type != MIXED)
- die(_("-N can only be used with --mixed"));
+ die(_("the option '%s' requires '%s'"), "-N", "--mixed");
+
+ prepare_repo_settings(the_repository);
+ the_repository->settings.command_requires_full_index = 0;
+
+ if (read_cache() < 0)
+ die(_("index file corrupt"));
/* Soft reset does not touch the index file nor the working tree
* at all, but requires them in a good order. Other resets reset
diff --git a/builtin/rev-list.c b/builtin/rev-list.c
index 36cb909..777558e 100644
--- a/builtin/rev-list.c
+++ b/builtin/rev-list.c
@@ -538,7 +538,7 @@
const char *arg = argv[i];
if (skip_prefix(arg, "--missing=", &arg)) {
if (revs.exclude_promisor_objects)
- die(_("cannot combine --exclude-promisor-objects and --missing"));
+ die(_("options '%s' and '%s' cannot be used together"), "--exclude-promisor-objects", "--missing");
if (parse_missing_action_value(arg))
break;
}
@@ -676,7 +676,7 @@
if (revs.count &&
(revs.tag_objects || revs.tree_objects || revs.blob_objects) &&
(revs.left_right || revs.cherry_mark))
- die(_("marked counting is incompatible with --objects"));
+ die(_("marked counting and '%s' cannot be used together"), "--objects");
save_commit_buffer = (revs.verbose_header ||
revs.grep_filter.pattern_list ||
diff --git a/builtin/rm.c b/builtin/rm.c
index 3d0967c..84a935a 100644
--- a/builtin/rm.c
+++ b/builtin/rm.c
@@ -272,13 +272,13 @@
if (pathspec_from_file) {
if (pathspec.nr)
- die(_("--pathspec-from-file is incompatible with pathspec arguments"));
+ die(_("'%s' and pathspec arguments cannot be used together"), "--pathspec-from-file");
parse_pathspec_file(&pathspec, 0,
PATHSPEC_PREFER_CWD,
prefix, pathspec_from_file, pathspec_file_nul);
} else if (pathspec_file_nul) {
- die(_("--pathspec-file-nul requires --pathspec-from-file"));
+ die(_("the option '%s' requires '%s'"), "--pathspec-file-nul", "--pathspec-from-file");
}
if (!pathspec.nr)
@@ -399,12 +399,13 @@
if (!index_only) {
int removed = 0, gitmodules_modified = 0;
struct strbuf buf = STRBUF_INIT;
+ int flag = force ? REMOVE_DIR_PURGE_ORIGINAL_CWD : 0;
for (i = 0; i < list.nr; i++) {
const char *path = list.entry[i].name;
if (list.entry[i].is_submodule) {
strbuf_reset(&buf);
strbuf_addstr(&buf, path);
- if (remove_dir_recursively(&buf, 0))
+ if (remove_dir_recursively(&buf, flag))
die(_("could not remove '%s'"), path);
removed = 1;
diff --git a/builtin/show-branch.c b/builtin/show-branch.c
index 0824492..e12c5e8 100644
--- a/builtin/show-branch.c
+++ b/builtin/show-branch.c
@@ -707,8 +707,8 @@
*
* Also --all and --remotes do not make sense either.
*/
- die(_("--reflog is incompatible with --all, --remotes, "
- "--independent or --merge-base"));
+ die(_("options '%s' and '%s' cannot be used together"), "--reflog",
+ "--all/--remotes/--independent/--merge-base");
}
/* If nothing is specified, show all branches by default */
@@ -761,6 +761,7 @@
char *logmsg;
char *nth_desc;
const char *msg;
+ char *end;
timestamp_t timestamp;
int tz;
@@ -770,11 +771,12 @@
reflog = i;
break;
}
- msg = strchr(logmsg, '\t');
- if (!msg)
- msg = "(none)";
- else
- msg++;
+
+ end = strchr(logmsg, '\n');
+ if (end)
+ *end = '\0';
+
+ msg = (*logmsg == '\0') ? "(none)" : logmsg;
reflog_msg[i] = xstrfmt("(%s) %s",
show_date(timestamp, tz,
DATE_MODE(RELATIVE)),
diff --git a/builtin/sparse-checkout.c b/builtin/sparse-checkout.c
index d0f5c47..679c107 100644
--- a/builtin/sparse-checkout.c
+++ b/builtin/sparse-checkout.c
@@ -56,6 +56,9 @@
char *sparse_filename;
int res;
+ if (!core_apply_sparse_checkout)
+ die(_("this worktree is not sparse"));
+
argc = parse_options(argc, argv, NULL,
builtin_sparse_checkout_list_options,
builtin_sparse_checkout_list_usage, 0);
@@ -380,6 +383,41 @@
return 0;
}
+static int update_modes(int *cone_mode, int *sparse_index)
+{
+ int mode, record_mode;
+
+ /* Determine if we need to record the mode; ensure sparse checkout on */
+ record_mode = (*cone_mode != -1) || !core_apply_sparse_checkout;
+
+ /* If not specified, use previous definition of cone mode */
+ if (*cone_mode == -1 && core_apply_sparse_checkout)
+ *cone_mode = core_sparse_checkout_cone;
+
+ /* Set cone/non-cone mode appropriately */
+ core_apply_sparse_checkout = 1;
+ if (*cone_mode == 1) {
+ mode = MODE_CONE_PATTERNS;
+ core_sparse_checkout_cone = 1;
+ } else {
+ mode = MODE_ALL_PATTERNS;
+ }
+ if (record_mode && set_config(mode))
+ return 1;
+
+ /* Set sparse-index/non-sparse-index mode if specified */
+ if (*sparse_index >= 0) {
+ if (set_sparse_index_config(the_repository, *sparse_index) < 0)
+ die(_("failed to modify sparse-index config"));
+
+ /* force an index rewrite */
+ repo_read_index(the_repository);
+ the_repository->index->updated_workdir = 1;
+ }
+
+ return 0;
+}
+
static char const * const builtin_sparse_checkout_init_usage[] = {
N_("git sparse-checkout init [--cone] [--[no-]sparse-index]"),
NULL
@@ -396,7 +434,6 @@
char *sparse_filename;
int res;
struct object_id oid;
- int mode;
struct strbuf pattern = STRBUF_INIT;
static struct option builtin_sparse_checkout_init_options[] = {
@@ -409,19 +446,14 @@
repo_read_index(the_repository);
+ init_opts.cone_mode = -1;
init_opts.sparse_index = -1;
argc = parse_options(argc, argv, NULL,
builtin_sparse_checkout_init_options,
builtin_sparse_checkout_init_usage, 0);
- if (init_opts.cone_mode) {
- mode = MODE_CONE_PATTERNS;
- core_sparse_checkout_cone = 1;
- } else
- mode = MODE_ALL_PATTERNS;
-
- if (set_config(mode))
+ if (update_modes(&init_opts.cone_mode, &init_opts.sparse_index))
return 1;
memset(&pl, 0, sizeof(pl));
@@ -429,17 +461,6 @@
sparse_filename = get_sparse_checkout_filename();
res = add_patterns_from_file_to_list(sparse_filename, "", 0, &pl, NULL, 0);
- if (init_opts.sparse_index >= 0) {
- if (set_sparse_index_config(the_repository, init_opts.sparse_index) < 0)
- die(_("failed to modify sparse-index config"));
-
- /* force an index rewrite */
- repo_read_index(the_repository);
- the_repository->index->updated_workdir = 1;
- }
-
- core_apply_sparse_checkout = 1;
-
/* If we already have a sparse-checkout file, use it. */
if (res >= 0) {
free(sparse_filename);
@@ -483,7 +504,7 @@
char *oldpattern = e->pattern;
size_t newlen;
- if (slash == e->pattern)
+ if (!slash || slash == e->pattern)
break;
newlen = slash - e->pattern;
@@ -515,17 +536,9 @@
insert_recursive_pattern(pl, line);
}
-static char const * const builtin_sparse_checkout_set_usage[] = {
- N_("git sparse-checkout (set|add) (--stdin | <patterns>)"),
- NULL
-};
-
-static struct sparse_checkout_set_opts {
- int use_stdin;
-} set_opts;
-
static void add_patterns_from_input(struct pattern_list *pl,
- int argc, const char **argv)
+ int argc, const char **argv,
+ int use_stdin)
{
int i;
if (core_sparse_checkout_cone) {
@@ -535,7 +548,7 @@
hashmap_init(&pl->parent_hashmap, pl_hashmap_cmp, NULL, 0);
pl->use_cone_patterns = 1;
- if (set_opts.use_stdin) {
+ if (use_stdin) {
struct strbuf unquoted = STRBUF_INIT;
while (!strbuf_getline(&line, stdin)) {
if (line.buf[0] == '"') {
@@ -559,7 +572,7 @@
}
}
} else {
- if (set_opts.use_stdin) {
+ if (use_stdin) {
struct strbuf line = STRBUF_INIT;
while (!strbuf_getline(&line, stdin)) {
@@ -580,7 +593,8 @@
};
static void add_patterns_cone_mode(int argc, const char **argv,
- struct pattern_list *pl)
+ struct pattern_list *pl,
+ int use_stdin)
{
struct strbuf buffer = STRBUF_INIT;
struct pattern_entry *pe;
@@ -588,7 +602,7 @@
struct pattern_list existing;
char *sparse_filename = get_sparse_checkout_filename();
- add_patterns_from_input(pl, argc, argv);
+ add_patterns_from_input(pl, argc, argv, use_stdin);
memset(&existing, 0, sizeof(existing));
existing.use_cone_patterns = core_sparse_checkout_cone;
@@ -598,6 +612,9 @@
die(_("unable to load existing sparse-checkout patterns"));
free(sparse_filename);
+ if (!existing.use_cone_patterns)
+ die(_("existing sparse-checkout patterns do not use cone mode"));
+
hashmap_for_each_entry(&existing.recursive_hashmap, &iter, pe, ent) {
if (!hashmap_contains_parent(&pl->recursive_hashmap,
pe->pattern, &buffer) ||
@@ -614,17 +631,19 @@
}
static void add_patterns_literal(int argc, const char **argv,
- struct pattern_list *pl)
+ struct pattern_list *pl,
+ int use_stdin)
{
char *sparse_filename = get_sparse_checkout_filename();
if (add_patterns_from_file_to_list(sparse_filename, "", 0,
pl, NULL, 0))
die(_("unable to load existing sparse-checkout patterns"));
free(sparse_filename);
- add_patterns_from_input(pl, argc, argv);
+ add_patterns_from_input(pl, argc, argv, use_stdin);
}
-static int modify_pattern_list(int argc, const char **argv, enum modify_type m)
+static int modify_pattern_list(int argc, const char **argv, int use_stdin,
+ enum modify_type m)
{
int result;
int changed_config = 0;
@@ -633,13 +652,13 @@
switch (m) {
case ADD:
if (core_sparse_checkout_cone)
- add_patterns_cone_mode(argc, argv, pl);
+ add_patterns_cone_mode(argc, argv, pl, use_stdin);
else
- add_patterns_literal(argc, argv, pl);
+ add_patterns_literal(argc, argv, pl, use_stdin);
break;
case REPLACE:
- add_patterns_from_input(pl, argc, argv);
+ add_patterns_from_input(pl, argc, argv, use_stdin);
break;
}
@@ -659,41 +678,124 @@
return result;
}
-static int sparse_checkout_set(int argc, const char **argv, const char *prefix,
- enum modify_type m)
+static char const * const builtin_sparse_checkout_add_usage[] = {
+ N_("git sparse-checkout add (--stdin | <patterns>)"),
+ NULL
+};
+
+static struct sparse_checkout_add_opts {
+ int use_stdin;
+} add_opts;
+
+static int sparse_checkout_add(int argc, const char **argv, const char *prefix)
{
- static struct option builtin_sparse_checkout_set_options[] = {
- OPT_BOOL(0, "stdin", &set_opts.use_stdin,
+ static struct option builtin_sparse_checkout_add_options[] = {
+ OPT_BOOL(0, "stdin", &add_opts.use_stdin,
N_("read patterns from standard in")),
OPT_END(),
};
+ if (!core_apply_sparse_checkout)
+ die(_("no sparse-checkout to add to"));
+
repo_read_index(the_repository);
argc = parse_options(argc, argv, prefix,
+ builtin_sparse_checkout_add_options,
+ builtin_sparse_checkout_add_usage,
+ PARSE_OPT_KEEP_UNKNOWN);
+
+ return modify_pattern_list(argc, argv, add_opts.use_stdin, ADD);
+}
+
+static char const * const builtin_sparse_checkout_set_usage[] = {
+ N_("git sparse-checkout set [--[no-]cone] [--[no-]sparse-index] (--stdin | <patterns>)"),
+ NULL
+};
+
+static struct sparse_checkout_set_opts {
+ int cone_mode;
+ int sparse_index;
+ int use_stdin;
+} set_opts;
+
+static int sparse_checkout_set(int argc, const char **argv, const char *prefix)
+{
+ int default_patterns_nr = 2;
+ const char *default_patterns[] = {"/*", "!/*/", NULL};
+
+ static struct option builtin_sparse_checkout_set_options[] = {
+ OPT_BOOL(0, "cone", &set_opts.cone_mode,
+ N_("initialize the sparse-checkout in cone mode")),
+ OPT_BOOL(0, "sparse-index", &set_opts.sparse_index,
+ N_("toggle the use of a sparse index")),
+ OPT_BOOL_F(0, "stdin", &set_opts.use_stdin,
+ N_("read patterns from standard in"),
+ PARSE_OPT_NONEG),
+ OPT_END(),
+ };
+
+ repo_read_index(the_repository);
+
+ set_opts.cone_mode = -1;
+ set_opts.sparse_index = -1;
+
+ argc = parse_options(argc, argv, prefix,
builtin_sparse_checkout_set_options,
builtin_sparse_checkout_set_usage,
PARSE_OPT_KEEP_UNKNOWN);
- return modify_pattern_list(argc, argv, m);
+ if (update_modes(&set_opts.cone_mode, &set_opts.sparse_index))
+ return 1;
+
+ /*
+ * Cone mode automatically specifies the toplevel directory. For
+ * non-cone mode, if nothing is specified, manually select just the
+ * top-level directory (much as 'init' would do).
+ */
+ if (!core_sparse_checkout_cone && argc == 0) {
+ argv = default_patterns;
+ argc = default_patterns_nr;
+ }
+
+ return modify_pattern_list(argc, argv, set_opts.use_stdin, REPLACE);
}
static char const * const builtin_sparse_checkout_reapply_usage[] = {
- N_("git sparse-checkout reapply"),
+ N_("git sparse-checkout reapply [--[no-]cone] [--[no-]sparse-index]"),
NULL
};
+static struct sparse_checkout_reapply_opts {
+ int cone_mode;
+ int sparse_index;
+} reapply_opts;
+
static int sparse_checkout_reapply(int argc, const char **argv)
{
static struct option builtin_sparse_checkout_reapply_options[] = {
+ OPT_BOOL(0, "cone", &reapply_opts.cone_mode,
+ N_("initialize the sparse-checkout in cone mode")),
+ OPT_BOOL(0, "sparse-index", &reapply_opts.sparse_index,
+ N_("toggle the use of a sparse index")),
OPT_END(),
};
+ if (!core_apply_sparse_checkout)
+ die(_("must be in a sparse-checkout to reapply sparsity patterns"));
+
argc = parse_options(argc, argv, NULL,
builtin_sparse_checkout_reapply_options,
builtin_sparse_checkout_reapply_usage, 0);
repo_read_index(the_repository);
+
+ reapply_opts.cone_mode = -1;
+ reapply_opts.sparse_index = -1;
+
+ if (update_modes(&reapply_opts.cone_mode, &reapply_opts.sparse_index))
+ return 1;
+
return update_working_directory(NULL);
}
@@ -710,6 +812,17 @@
struct pattern_list pl;
struct strbuf match_all = STRBUF_INIT;
+ /*
+ * We do not exit early if !core_apply_sparse_checkout; due to the
+ * ability for users to manually muck things up between
+ * direct editing of .git/info/sparse-checkout
+ * running read-tree -m u HEAD or update-index --skip-worktree
+ * direct toggling of config options
+ * users might end up with an index with SKIP_WORKTREE bit set on
+ * some files and not know how to undo it. So, here we just
+ * forcibly return to a dense checkout regardless of initial state.
+ */
+
argc = parse_options(argc, argv, NULL,
builtin_sparse_checkout_disable_options,
builtin_sparse_checkout_disable_usage, 0);
@@ -758,9 +871,9 @@
if (!strcmp(argv[0], "init"))
return sparse_checkout_init(argc, argv);
if (!strcmp(argv[0], "set"))
- return sparse_checkout_set(argc, argv, prefix, REPLACE);
+ return sparse_checkout_set(argc, argv, prefix);
if (!strcmp(argv[0], "add"))
- return sparse_checkout_set(argc, argv, prefix, ADD);
+ return sparse_checkout_add(argc, argv, prefix);
if (!strcmp(argv[0], "reapply"))
return sparse_checkout_reapply(argc, argv);
if (!strcmp(argv[0], "disable"))
diff --git a/builtin/stash.c b/builtin/stash.c
index a0ccc86..86cd0b4 100644
--- a/builtin/stash.c
+++ b/builtin/stash.c
@@ -27,11 +27,11 @@
N_("git stash ( pop | apply ) [--index] [-q|--quiet] [<stash>]"),
N_("git stash branch <branchname> [<stash>]"),
"git stash clear",
- N_("git stash [push [-p|--patch] [-k|--[no-]keep-index] [-q|--quiet]\n"
+ N_("git stash [push [-p|--patch] [-S|--staged] [-k|--[no-]keep-index] [-q|--quiet]\n"
" [-u|--include-untracked] [-a|--all] [-m|--message <message>]\n"
" [--pathspec-from-file=<file> [--pathspec-file-nul]]\n"
" [--] [<pathspec>...]]"),
- N_("git stash save [-p|--patch] [-k|--[no-]keep-index] [-q|--quiet]\n"
+ N_("git stash save [-p|--patch] [-S|--staged] [-k|--[no-]keep-index] [-q|--quiet]\n"
" [-u|--include-untracked] [-a|--all] [<message>]"),
NULL
};
@@ -561,18 +561,19 @@
if (index)
fprintf_ln(stderr, _("Index was not unstashed."));
- return ret;
+ goto restore_untracked;
}
if (has_index) {
if (reset_tree(&index_tree, 0, 0))
- return -1;
+ ret = -1;
} else {
unstage_changes_unless_new(&c_tree);
}
+restore_untracked:
if (info->has_u && restore_untracked(&info->u_tree))
- return error(_("could not restore untracked files from stash"));
+ ret = error(_("could not restore untracked files from stash"));
if (!quiet) {
struct child_process cp = CHILD_PROCESS_INIT;
@@ -592,7 +593,7 @@
run_command(&cp);
}
- return 0;
+ return ret;
}
static int apply_stash(int argc, const char **argv, const char *prefix)
@@ -1132,6 +1133,38 @@
return ret;
}
+static int stash_staged(struct stash_info *info, struct strbuf *out_patch,
+ int quiet)
+{
+ int ret = 0;
+ struct child_process cp_diff_tree = CHILD_PROCESS_INIT;
+ struct index_state istate = { NULL };
+
+ if (write_index_as_tree(&info->w_tree, &istate, the_repository->index_file,
+ 0, NULL)) {
+ ret = -1;
+ goto done;
+ }
+
+ cp_diff_tree.git_cmd = 1;
+ strvec_pushl(&cp_diff_tree.args, "diff-tree", "-p", "-U1", "HEAD",
+ oid_to_hex(&info->w_tree), "--", NULL);
+ if (pipe_command(&cp_diff_tree, NULL, 0, out_patch, 0, NULL, 0)) {
+ ret = -1;
+ goto done;
+ }
+
+ if (!out_patch->len) {
+ if (!quiet)
+ fprintf_ln(stderr, _("No staged changes"));
+ ret = 1;
+ }
+
+done:
+ discard_index(&istate);
+ return ret;
+}
+
static int stash_patch(struct stash_info *info, const struct pathspec *ps,
struct strbuf *out_patch, int quiet)
{
@@ -1258,7 +1291,7 @@
}
static int do_create_stash(const struct pathspec *ps, struct strbuf *stash_msg_buf,
- int include_untracked, int patch_mode,
+ int include_untracked, int patch_mode, int only_staged,
struct stash_info *info, struct strbuf *patch,
int quiet)
{
@@ -1337,6 +1370,16 @@
} else if (ret > 0) {
goto done;
}
+ } else if (only_staged) {
+ ret = stash_staged(info, patch, quiet);
+ if (ret < 0) {
+ if (!quiet)
+ fprintf_ln(stderr, _("Cannot save the current "
+ "staged state"));
+ goto done;
+ } else if (ret > 0) {
+ goto done;
+ }
} else {
if (stash_working_tree(info, ps)) {
if (!quiet)
@@ -1395,7 +1438,7 @@
if (!check_changes_tracked_files(&ps))
return 0;
- ret = do_create_stash(&ps, &stash_msg_buf, 0, 0, &info,
+ ret = do_create_stash(&ps, &stash_msg_buf, 0, 0, 0, &info,
NULL, 0);
if (!ret)
printf_ln("%s", oid_to_hex(&info.w_commit));
@@ -1405,7 +1448,7 @@
}
static int do_push_stash(const struct pathspec *ps, const char *stash_msg, int quiet,
- int keep_index, int patch_mode, int include_untracked)
+ int keep_index, int patch_mode, int include_untracked, int only_staged)
{
int ret = 0;
struct stash_info info;
@@ -1423,6 +1466,17 @@
goto done;
}
+ /* --patch overrides --staged */
+ if (patch_mode)
+ only_staged = 0;
+
+ if (only_staged && include_untracked) {
+ fprintf_ln(stderr, _("Can't use --staged and --include-untracked"
+ " or --all at the same time"));
+ ret = -1;
+ goto done;
+ }
+
read_cache_preload(NULL);
if (!include_untracked && ps->nr) {
int i;
@@ -1463,7 +1517,7 @@
if (stash_msg)
strbuf_addstr(&stash_msg_buf, stash_msg);
- if (do_create_stash(ps, &stash_msg_buf, include_untracked, patch_mode,
+ if (do_create_stash(ps, &stash_msg_buf, include_untracked, patch_mode, only_staged,
&info, &patch, quiet)) {
ret = -1;
goto done;
@@ -1480,13 +1534,19 @@
printf_ln(_("Saved working directory and index state %s"),
stash_msg_buf.buf);
- if (!patch_mode) {
+ if (!(patch_mode || only_staged)) {
if (include_untracked && !ps->nr) {
struct child_process cp = CHILD_PROCESS_INIT;
cp.git_cmd = 1;
+ if (startup_info->original_cwd) {
+ cp.dir = startup_info->original_cwd;
+ strvec_pushf(&cp.env_array, "%s=%s",
+ GIT_WORK_TREE_ENVIRONMENT,
+ the_repository->worktree);
+ }
strvec_pushl(&cp.args, "clean", "--force",
- "--quiet", "-d", NULL);
+ "--quiet", "-d", ":/", NULL);
if (include_untracked == INCLUDE_ALL_FILES)
strvec_push(&cp.args, "-x");
if (run_command(&cp)) {
@@ -1598,6 +1658,7 @@
{
int force_assume = 0;
int keep_index = -1;
+ int only_staged = 0;
int patch_mode = 0;
int include_untracked = 0;
int quiet = 0;
@@ -1608,6 +1669,8 @@
struct option options[] = {
OPT_BOOL('k', "keep-index", &keep_index,
N_("keep index")),
+ OPT_BOOL('S', "staged", &only_staged,
+ N_("stash staged changes only")),
OPT_BOOL('p', "patch", &patch_mode,
N_("stash in patch mode")),
OPT__QUIET(&quiet, N_("quiet mode")),
@@ -1625,6 +1688,7 @@
if (argc) {
force_assume = !strcmp(argv[0], "-p");
argc = parse_options(argc, argv, prefix, options,
+ push_assumed ? git_stash_usage :
git_stash_push_usage,
PARSE_OPT_KEEP_DASHDASH);
}
@@ -1644,25 +1708,29 @@
if (pathspec_from_file) {
if (patch_mode)
- die(_("--pathspec-from-file is incompatible with --patch"));
+ die(_("options '%s' and '%s' cannot be used together"), "--pathspec-from-file", "--patch");
+
+ if (only_staged)
+ die(_("options '%s' and '%s' cannot be used together"), "--pathspec-from-file", "--staged");
if (ps.nr)
- die(_("--pathspec-from-file is incompatible with pathspec arguments"));
+ die(_("'%s' and pathspec arguments cannot be used together"), "--pathspec-from-file");
parse_pathspec_file(&ps, 0,
PATHSPEC_PREFER_FULL | PATHSPEC_PREFIX_ORIGIN,
prefix, pathspec_from_file, pathspec_file_nul);
} else if (pathspec_file_nul) {
- die(_("--pathspec-file-nul requires --pathspec-from-file"));
+ die(_("the option '%s' requires '%s'"), "--pathspec-file-nul", "--pathspec-from-file");
}
return do_push_stash(&ps, stash_msg, quiet, keep_index, patch_mode,
- include_untracked);
+ include_untracked, only_staged);
}
static int save_stash(int argc, const char **argv, const char *prefix)
{
int keep_index = -1;
+ int only_staged = 0;
int patch_mode = 0;
int include_untracked = 0;
int quiet = 0;
@@ -1673,6 +1741,8 @@
struct option options[] = {
OPT_BOOL('k', "keep-index", &keep_index,
N_("keep index")),
+ OPT_BOOL('S', "staged", &only_staged,
+ N_("stash staged changes only")),
OPT_BOOL('p', "patch", &patch_mode,
N_("stash in patch mode")),
OPT__QUIET(&quiet, N_("quiet mode")),
@@ -1694,7 +1764,7 @@
memset(&ps, 0, sizeof(ps));
ret = do_push_stash(&ps, stash_msg, quiet, keep_index,
- patch_mode, include_untracked);
+ patch_mode, include_untracked, only_staged);
strbuf_release(&stash_msg_buf);
return ret;
diff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c
index e630f0c..c5d3fc3 100644
--- a/builtin/submodule--helper.c
+++ b/builtin/submodule--helper.c
@@ -1313,7 +1313,7 @@
if (files) {
if (cached)
- die(_("--cached and --files are mutually exclusive"));
+ die(_("options '%s' and '%s' cannot be used together"), "--cached", "--files");
diff_cmd = DIFF_FILES;
}
@@ -1503,16 +1503,17 @@
struct strbuf sb_rm = STRBUF_INIT;
const char *format;
- /*
- * protect submodules containing a .git directory
- * NEEDSWORK: instead of dying, automatically call
- * absorbgitdirs and (possibly) warn.
- */
- if (is_directory(sub_git_dir))
- die(_("Submodule work tree '%s' contains a .git "
- "directory (use 'rm -rf' if you really want "
- "to remove it including all of its history)"),
- displaypath);
+ if (is_directory(sub_git_dir)) {
+ if (!(flags & OPT_QUIET))
+ warning(_("Submodule work tree '%s' contains a .git "
+ "directory. This will be replaced with a "
+ ".git file by using absorbgitdirs."),
+ displaypath);
+
+ absorb_git_dir_into_superproject(path,
+ ABSORB_GITDIR_RECURSE_SUBMODULES);
+
+ }
if (!(flags & OPT_FORCE)) {
struct child_process cp_rm = CHILD_PROCESS_INIT;
@@ -2971,7 +2972,7 @@
die(_("--branch or --default required"));
if (opt_branch && opt_default)
- die(_("--branch and --default are mutually exclusive"));
+ die(_("options '%s' and '%s' cannot be used together"), "--branch", "--default");
if (argc != 1 || !(path = argv[0]))
usage_with_options(usage, options);
diff --git a/builtin/tag.c b/builtin/tag.c
index 6fe6467..134b3f1 100644
--- a/builtin/tag.c
+++ b/builtin/tag.c
@@ -178,7 +178,6 @@
static int git_tag_config(const char *var, const char *value, void *cb)
{
int status;
- struct ref_sorting **sorting_tail = (struct ref_sorting **)cb;
if (!strcmp(var, "tag.gpgsign")) {
config_sign_tag = git_config_bool(var, value);
@@ -188,7 +187,7 @@
if (!strcmp(var, "tag.sort")) {
if (!value)
return config_error_nonbool(var);
- parse_ref_sorting(sorting_tail, value);
+ string_list_append(cb, value);
return 0;
}
@@ -436,7 +435,8 @@
struct ref_transaction *transaction;
struct strbuf err = STRBUF_INIT;
struct ref_filter filter;
- static struct ref_sorting *sorting = NULL, **sorting_tail = &sorting;
+ struct ref_sorting *sorting;
+ struct string_list sorting_options = STRING_LIST_INIT_DUP;
struct ref_format format = REF_FORMAT_INIT;
int icase = 0;
int edit_flag = 0;
@@ -470,7 +470,7 @@
OPT_WITHOUT(&filter.no_commit, N_("print only tags that don't contain the commit")),
OPT_MERGED(&filter, N_("print only tags that are merged")),
OPT_NO_MERGED(&filter, N_("print only tags that are not merged")),
- OPT_REF_SORT(sorting_tail),
+ OPT_REF_SORT(&sorting_options),
{
OPTION_CALLBACK, 0, "points-at", &filter.points_at, N_("object"),
N_("print only tags of the object"), PARSE_OPT_LASTARG_DEFAULT,
@@ -483,10 +483,11 @@
OPT_END()
};
int ret = 0;
+ const char *only_in_list = NULL;
setup_ref_filter_porcelain_msg();
- git_config(git_tag_config, sorting_tail);
+ git_config(git_tag_config, &sorting_options);
memset(&opt, 0, sizeof(opt));
memset(&filter, 0, sizeof(filter));
@@ -522,11 +523,10 @@
finalize_colopts(&colopts, -1);
if (cmdmode == 'l' && filter.lines != -1) {
if (explicitly_enable_column(colopts))
- die(_("--column and -n are incompatible"));
+ die(_("options '%s' and '%s' cannot be used together"), "--column", "-n");
colopts = 0;
}
- if (!sorting)
- sorting = ref_default_sorting();
+ sorting = ref_sorting_options(&sorting_options);
ref_sorting_set_sort_flags_all(sorting, REF_SORTING_ICASE, icase);
filter.ignore_case = icase;
if (cmdmode == 'l') {
@@ -543,15 +543,19 @@
goto cleanup;
}
if (filter.lines != -1)
- die(_("-n option is only allowed in list mode"));
- if (filter.with_commit)
- die(_("--contains option is only allowed in list mode"));
- if (filter.no_commit)
- die(_("--no-contains option is only allowed in list mode"));
- if (filter.points_at.nr)
- die(_("--points-at option is only allowed in list mode"));
- if (filter.reachable_from || filter.unreachable_from)
- die(_("--merged and --no-merged options are only allowed in list mode"));
+ only_in_list = "-n";
+ else if (filter.with_commit)
+ only_in_list = "--contains";
+ else if (filter.no_commit)
+ only_in_list = "--no-contains";
+ else if (filter.points_at.nr)
+ only_in_list = "--points-at";
+ else if (filter.reachable_from)
+ only_in_list = "--merged";
+ else if (filter.unreachable_from)
+ only_in_list = "--no-merged";
+ if (only_in_list)
+ die(_("the '%s' option is only allowed in list mode"), only_in_list);
if (cmdmode == 'd') {
ret = delete_tags(argv);
goto cleanup;
@@ -565,7 +569,7 @@
if (msg.given || msgfile) {
if (msg.given && msgfile)
- die(_("only one -F or -m option is allowed."));
+ die(_("options '%s' and '%s' cannot be used together"), "-F", "-m");
if (msg.given)
strbuf_addbuf(&buf, &(msg.buf));
else {
diff --git a/builtin/upload-archive.c b/builtin/upload-archive.c
index 24654b4..98d028d 100644
--- a/builtin/upload-archive.c
+++ b/builtin/upload-archive.c
@@ -77,7 +77,7 @@
int cmd_upload_archive(int argc, const char **argv, const char *prefix)
{
- struct child_process writer = { argv };
+ struct child_process writer = CHILD_PROCESS_INIT;
if (argc == 2 && !strcmp(argv[1], "-h"))
usage(upload_archive_usage);
@@ -89,9 +89,10 @@
* multiplexed out to our fd#1. If the child dies, we tell the other
* end over channel #3.
*/
- argv[0] = "upload-archive--writer";
writer.out = writer.err = -1;
writer.git_cmd = 1;
+ strvec_push(&writer.args, "upload-archive--writer");
+ strvec_pushv(&writer.args, argv + 1);
if (start_command(&writer)) {
int err = errno;
packet_write_fmt(1, "NACK unable to spawn subprocess\n");
diff --git a/builtin/var.c b/builtin/var.c
index 6c6f46b..491db27 100644
--- a/builtin/var.c
+++ b/builtin/var.c
@@ -5,6 +5,7 @@
*/
#include "builtin.h"
#include "config.h"
+#include "refs.h"
static const char var_usage[] = "git var (-l | <variable>)";
@@ -27,6 +28,11 @@
return pgm;
}
+static const char *default_branch(int flag)
+{
+ return git_default_branch_name(1);
+}
+
struct git_var {
const char *name;
const char *(*read)(int);
@@ -36,6 +42,7 @@
{ "GIT_AUTHOR_IDENT", git_author_info },
{ "GIT_EDITOR", editor },
{ "GIT_PAGER", pager },
+ { "GIT_DEFAULT_BRANCH", default_branch },
{ "", NULL },
};
diff --git a/builtin/worktree.c b/builtin/worktree.c
index d22ece9..2838254 100644
--- a/builtin/worktree.c
+++ b/builtin/worktree.c
@@ -72,7 +72,7 @@
static void prune_worktree(const char *id, const char *reason)
{
if (show_only || verbose)
- printf_ln(_("Removing %s/%s: %s"), "worktrees", id, reason);
+ fprintf_ln(stderr, _("Removing %s/%s: %s"), "worktrees", id, reason);
if (!show_only)
delete_git_dir(id);
}
@@ -349,18 +349,18 @@
strvec_push(&cp.args, "--quiet");
}
- cp.env = child_env.v;
+ strvec_pushv(&cp.env_array, child_env.v);
ret = run_command(&cp);
if (ret)
goto done;
if (opts->checkout) {
- cp.argv = NULL;
- strvec_clear(&cp.args);
+ struct child_process cp = CHILD_PROCESS_INIT;
+ cp.git_cmd = 1;
strvec_pushl(&cp.args, "reset", "--hard", "--no-recurse-submodules", NULL);
if (opts->quiet)
strvec_push(&cp.args, "--quiet");
- cp.env = child_env.v;
+ strvec_pushv(&cp.env_array, child_env.v);
ret = run_command(&cp);
if (ret)
goto done;
@@ -385,12 +385,11 @@
const char *hook = find_hook("post-checkout");
if (hook) {
const char *env[] = { "GIT_DIR", "GIT_WORK_TREE", NULL };
- cp.git_cmd = 0;
+ struct child_process cp = CHILD_PROCESS_INIT;
cp.no_stdin = 1;
cp.stdout_to_stderr = 1;
cp.dir = path;
- cp.env = env;
- cp.argv = NULL;
+ strvec_pushv(&cp.env_array, env);
cp.trace2_hook_name = "post-checkout";
strvec_pushl(&cp.args, absolute_path(hook),
oid_to_hex(null_oid()),
@@ -418,24 +417,24 @@
if (force_new_branch) {
struct commit *commit = lookup_commit_reference_by_name(new_branch);
if (!commit)
- printf_ln(_("Preparing worktree (new branch '%s')"), new_branch);
+ fprintf_ln(stderr, _("Preparing worktree (new branch '%s')"), new_branch);
else
- printf_ln(_("Preparing worktree (resetting branch '%s'; was at %s)"),
+ fprintf_ln(stderr, _("Preparing worktree (resetting branch '%s'; was at %s)"),
new_branch,
find_unique_abbrev(&commit->object.oid, DEFAULT_ABBREV));
} else if (new_branch) {
- printf_ln(_("Preparing worktree (new branch '%s')"), new_branch);
+ fprintf_ln(stderr, _("Preparing worktree (new branch '%s')"), new_branch);
} else {
struct strbuf s = STRBUF_INIT;
if (!detach && !strbuf_check_branch_ref(&s, branch) &&
ref_exists(s.buf))
- printf_ln(_("Preparing worktree (checking out '%s')"),
+ fprintf_ln(stderr, _("Preparing worktree (checking out '%s')"),
branch);
else {
struct commit *commit = lookup_commit_reference_by_name(branch);
if (!commit)
die(_("invalid reference: %s"), branch);
- printf_ln(_("Preparing worktree (detached HEAD %s)"),
+ fprintf_ln(stderr, _("Preparing worktree (detached HEAD %s)"),
find_unique_abbrev(&commit->object.oid, DEFAULT_ABBREV));
}
strbuf_release(&s);
@@ -504,9 +503,9 @@
opts.checkout = 1;
ac = parse_options(ac, av, prefix, options, worktree_usage, 0);
if (!!opts.detach + !!new_branch + !!new_branch_force > 1)
- die(_("-b, -B, and --detach are mutually exclusive"));
+ die(_("options '%s', '%s', and '%s' cannot be used together"), "-b", "-B", "--detach");
if (lock_reason && !keep_locked)
- die(_("--reason requires --lock"));
+ die(_("the option '%s' requires '%s'"), "--reason", "--lock");
if (lock_reason)
opts.keep_locked = lock_reason;
else if (keep_locked)
@@ -700,7 +699,7 @@
if (ac)
usage_with_options(worktree_usage, options);
else if (verbose && porcelain)
- die(_("--verbose and --porcelain are mutually exclusive"));
+ die(_("options '%s' and '%s' cannot be used together"), "--verbose", "--porcelain");
else {
struct worktree **worktrees = get_worktrees();
int path_maxlen = 0, abbrev = DEFAULT_ABBREV, i;
@@ -1006,7 +1005,7 @@
static void report_repair(int iserr, const char *path, const char *msg, void *cb_data)
{
if (!iserr) {
- printf_ln(_("repair: %s: %s"), msg, path);
+ fprintf_ln(stderr, _("repair: %s: %s"), msg, path);
} else {
int *exit_status = (int *)cb_data;
fprintf_ln(stderr, _("error: %s: %s"), msg, path);
diff --git a/cache-tree.c b/cache-tree.c
index 79d1681..65ca993 100644
--- a/cache-tree.c
+++ b/cache-tree.c
@@ -741,15 +741,26 @@
return ret;
}
+static void prime_cache_tree_sparse_dir(struct cache_tree *it,
+ struct tree *tree)
+{
+
+ oidcpy(&it->oid, &tree->object.oid);
+ it->entry_count = 1;
+}
+
static void prime_cache_tree_rec(struct repository *r,
struct cache_tree *it,
- struct tree *tree)
+ struct tree *tree,
+ struct strbuf *tree_path)
{
struct tree_desc desc;
struct name_entry entry;
int cnt;
+ int base_path_len = tree_path->len;
oidcpy(&it->oid, &tree->object.oid);
+
init_tree_desc(&desc, tree->buffer, tree->size);
cnt = 0;
while (tree_entry(&desc, &entry)) {
@@ -758,14 +769,40 @@
else {
struct cache_tree_sub *sub;
struct tree *subtree = lookup_tree(r, &entry.oid);
+
if (!subtree->object.parsed)
parse_tree(subtree);
sub = cache_tree_sub(it, entry.path);
sub->cache_tree = cache_tree();
- prime_cache_tree_rec(r, sub->cache_tree, subtree);
+
+ /*
+ * Recursively-constructed subtree path is only needed when working
+ * in a sparse index (where it's used to determine whether the
+ * subtree is a sparse directory in the index).
+ */
+ if (r->index->sparse_index) {
+ strbuf_setlen(tree_path, base_path_len);
+ strbuf_grow(tree_path, base_path_len + entry.pathlen + 1);
+ strbuf_add(tree_path, entry.path, entry.pathlen);
+ strbuf_addch(tree_path, '/');
+ }
+
+ /*
+ * If a sparse index is in use, the directory being processed may be
+ * sparse. To confirm that, we can check whether an entry with that
+ * exact name exists in the index. If it does, the created subtree
+ * should be sparse. Otherwise, cache tree expansion should continue
+ * as normal.
+ */
+ if (r->index->sparse_index &&
+ index_entry_exists(r->index, tree_path->buf, tree_path->len))
+ prime_cache_tree_sparse_dir(sub->cache_tree, subtree);
+ else
+ prime_cache_tree_rec(r, sub->cache_tree, subtree, tree_path);
cnt += sub->cache_tree->entry_count;
}
}
+
it->entry_count = cnt;
}
@@ -773,11 +810,14 @@
struct index_state *istate,
struct tree *tree)
{
+ struct strbuf tree_path = STRBUF_INIT;
+
trace2_region_enter("cache-tree", "prime_cache_tree", the_repository);
cache_tree_free(&istate->cache_tree);
istate->cache_tree = cache_tree();
- prime_cache_tree_rec(r, istate->cache_tree, tree);
+ prime_cache_tree_rec(r, istate->cache_tree, tree, &tree_path);
+ strbuf_release(&tree_path);
istate->cache_changed |= CACHE_TREE_CHANGED;
trace2_region_leave("cache-tree", "prime_cache_tree", the_repository);
}
diff --git a/cache.h b/cache.h
index eba1248..281f00a 100644
--- a/cache.h
+++ b/cache.h
@@ -350,8 +350,6 @@
void remove_name_hash(struct index_state *istate, struct cache_entry *ce);
void free_name_hash(struct index_state *istate);
-void ensure_full_index(struct index_state *istate);
-
/* Cache entry creation and cleanup */
/*
@@ -817,6 +815,16 @@
int index_name_pos(struct index_state *, const char *name, int namelen);
/*
+ * Determines whether an entry with the given name exists within the
+ * given index. The return value is 1 if an exact match is found, otherwise
+ * it is 0. Note that, unlike index_name_pos, this function does not expand
+ * the index if it is sparse. If an item exists within the full index but it
+ * is contained within a sparse directory (and not in the sparse index), 0 is
+ * returned.
+ */
+int index_entry_exists(struct index_state *, const char *name, int namelen);
+
+/*
* Some functions return the negative complement of an insert position when a
* precise match was not found but a position was found where the entry would
* need to be inserted. This helper protects that logic from any integer
@@ -986,6 +994,7 @@
extern char *git_replace_ref_base;
extern int fsync_object_files;
+extern int use_fsync;
extern int core_preload_index;
extern int precomposed_unicode;
extern int protect_hfs;
@@ -1588,6 +1597,7 @@
timestamp_t approxidate_relative(const char *date);
void parse_date_format(const char *format, struct date_mode *mode);
int date_overflows(timestamp_t date);
+time_t tm_to_time_t(const struct tm *tm);
#define IDENT_STRICT 1
#define IDENT_NO_DATE 2
@@ -1834,8 +1844,10 @@
struct startup_info {
int have_repository;
const char *prefix;
+ const char *original_cwd;
};
extern struct startup_info *startup_info;
+extern const char *tmp_original_cwd;
/* merge.c */
struct commit_list;
diff --git a/cbtree.c b/cbtree.c
index b0c65d8..336e46d 100644
--- a/cbtree.c
+++ b/cbtree.c
@@ -95,38 +95,6 @@
return p && !memcmp(p->k, k, klen) ? p : NULL;
}
-struct cb_node *cb_unlink(struct cb_tree *t, const uint8_t *k, size_t klen)
-{
- struct cb_node **wherep = &t->root;
- struct cb_node **whereq = NULL;
- struct cb_node *q = NULL;
- size_t direction = 0;
- uint8_t c;
- struct cb_node *p = t->root;
-
- if (!p) return NULL; /* empty tree, nothing to delete */
-
- /* traverse to find best match, keeping link to parent */
- while (1 & (uintptr_t)p) {
- whereq = wherep;
- q = cb_node_of(p);
- c = q->byte < klen ? k[q->byte] : 0;
- direction = (1 + (q->otherbits | c)) >> 8;
- wherep = q->child + direction;
- p = *wherep;
- }
-
- if (memcmp(p->k, k, klen))
- return NULL; /* no match, nothing unlinked */
-
- /* found an exact match */
- if (whereq) /* update parent */
- *whereq = q->child[1 - direction];
- else
- t->root = NULL;
- return p;
-}
-
static enum cb_next cb_descend(struct cb_node *p, cb_iter fn, void *arg)
{
if (1 & (uintptr_t)p) {
diff --git a/cbtree.h b/cbtree.h
index dedbb8e..0be14fb 100644
--- a/cbtree.h
+++ b/cbtree.h
@@ -47,7 +47,6 @@
struct cb_node *cb_lookup(struct cb_tree *, const uint8_t *k, size_t klen);
struct cb_node *cb_insert(struct cb_tree *, struct cb_node *, size_t klen);
-struct cb_node *cb_unlink(struct cb_tree *t, const uint8_t *k, size_t klen);
typedef enum cb_next (*cb_iter)(struct cb_node *, void *arg);
diff --git a/ci/check-directional-formatting.bash b/ci/check-directional-formatting.bash
new file mode 100755
index 0000000..e6211b1
--- /dev/null
+++ b/ci/check-directional-formatting.bash
@@ -0,0 +1,27 @@
+#!/bin/bash
+
+# This script verifies that the non-binary files tracked in the Git index do
+# not contain any Unicode directional formatting: such formatting could be used
+# to deceive reviewers into interpreting code differently from the compiler.
+# This is intended to run on an Ubuntu agent in a GitHub workflow.
+#
+# To allow translated messages to introduce such directional formatting in the
+# future, we exclude the `.po` files from this validation.
+#
+# Neither GNU grep nor `git grep` (not even with `-P`) handle `\u` as a way to
+# specify UTF-8.
+#
+# To work around that, we use `printf` to produce the pattern as a byte
+# sequence, and then feed that to `git grep` as a byte sequence (setting
+# `LC_CTYPE` to make sure that the arguments are interpreted as intended).
+#
+# Note: we need to use Bash here because its `printf` interprets `\uNNNN` as
+# UTF-8 code points, as desired. Running this script through Ubuntu's `dash`,
+# for example, would use a `printf` that does not understand that syntax.
+
+# U+202a..U+2a2e: LRE, RLE, PDF, LRO and RLO
+# U+2066..U+2069: LRI, RLI, FSI and PDI
+regex='(\u202a|\u202b|\u202c|\u202d|\u202e|\u2066|\u2067|\u2068|\u2069)'
+
+! LC_CTYPE=C git grep -El "$(LC_CTYPE=C.UTF-8 printf "$regex")" \
+ -- ':(exclude,attr:binary)' ':(exclude)*.po'
diff --git a/ci/install-dependencies.sh b/ci/install-dependencies.sh
index 1d0e48f..dbcebad 100755
--- a/ci/install-dependencies.sh
+++ b/ci/install-dependencies.sh
@@ -11,18 +11,11 @@
tcl tk gettext zlib1g-dev perl-modules liberror-perl libauthen-sasl-perl
libemail-valid-perl libio-socket-ssl-perl libnet-smtp-ssl-perl"
-case "$jobname" in
-linux-clang|linux-gcc|linux-leaks)
- sudo apt-add-repository -y "ppa:ubuntu-toolchain-r/test"
+case "$runs_on_pool" in
+ubuntu-latest)
sudo apt-get -q update
sudo apt-get -q -y install language-pack-is libsvn-perl apache2 \
- $UBUNTU_COMMON_PKGS
- case "$jobname" in
- linux-gcc)
- sudo apt-get -q -y install gcc-8
- ;;
- esac
-
+ $UBUNTU_COMMON_PKGS $CC_PACKAGE
mkdir --parents "$P4_PATH"
pushd "$P4_PATH"
wget --quiet "$P4WHENCE/bin.linux26x86_64/p4d"
@@ -37,7 +30,7 @@
cp git-lfs-$LINUX_GIT_LFS_VERSION/git-lfs .
popd
;;
-osx-clang|osx-gcc)
+macos-latest)
export HOMEBREW_NO_AUTO_UPDATE=1 HOMEBREW_NO_INSTALL_CLEANUP=1
# Uncomment this if you want to run perf tests:
# brew install gnu-time
@@ -51,15 +44,17 @@
brew install --cask --no-quarantine perforce
} ||
brew install homebrew/cask/perforce
- case "$jobname" in
- osx-gcc)
- brew install gcc@9
- # Just in case the image is updated to contain gcc@9
- # pre-installed but not linked.
- brew link gcc@9
- ;;
- esac
+
+ if test -n "$CC_PACKAGE"
+ then
+ BREW_PACKAGE=${CC_PACKAGE/-/@}
+ brew install "$BREW_PACKAGE"
+ brew link "$BREW_PACKAGE"
+ fi
;;
+esac
+
+case "$jobname" in
StaticAnalysis)
sudo apt-get -q update
sudo apt-get -q -y install coccinelle libcurl4-openssl-dev libssl-dev \
@@ -77,7 +72,7 @@
test -n "$ALREADY_HAVE_ASCIIDOCTOR" ||
sudo gem install --version 1.5.8 asciidoctor
;;
-linux-gcc-default|linux-gcc-4.8)
+linux-gcc-default)
sudo apt-get -q update
sudo apt-get -q -y install $UBUNTU_COMMON_PKGS
;;
diff --git a/ci/install-docker-dependencies.sh b/ci/install-docker-dependencies.sh
index 07a8c6b..78b7e32 100755
--- a/ci/install-docker-dependencies.sh
+++ b/ci/install-docker-dependencies.sh
@@ -4,7 +4,7 @@
#
case "$jobname" in
-Linux32)
+linux32)
linux32 --32bit i386 sh -c '
apt update >/dev/null &&
apt install -y build-essential libcurl4-openssl-dev \
diff --git a/ci/lib.sh b/ci/lib.sh
index 82cb17f..9d28ab5 100755
--- a/ci/lib.sh
+++ b/ci/lib.sh
@@ -34,7 +34,7 @@
# successfully before (e.g. because the branch got rebased, changing only
# the commit messages).
skip_good_tree () {
- if test "$TRAVIS_DEBUG_MODE" = true || test true = "$GITHUB_ACTIONS"
+ if test true = "$GITHUB_ACTIONS"
then
return
fi
@@ -60,7 +60,7 @@
cat <<-EOF
$(tput setaf 2)Skipping build job for commit $CI_COMMIT.$(tput sgr0)
This commit's tree has already been built and tested successfully in build job $prev_good_job_number for commit $prev_good_commit.
- The log of that build job is available at $(url_for_job_id $prev_good_job_id)
+ The log of that build job is available at $SYSTEM_TASKDEFINITIONSURI$SYSTEM_TEAMPROJECT/_build/results?buildId=$prev_good_job_id
To force a re-build delete the branch's cache and then hit 'Restart job'.
EOF
fi
@@ -91,29 +91,7 @@
# and installing dependencies.
set -ex
-if test true = "$TRAVIS"
-then
- CI_TYPE=travis
- # When building a PR, TRAVIS_BRANCH refers to the *target* branch. Not
- # what we want here. We want the source branch instead.
- CI_BRANCH="${TRAVIS_PULL_REQUEST_BRANCH:-$TRAVIS_BRANCH}"
- CI_COMMIT="$TRAVIS_COMMIT"
- CI_JOB_ID="$TRAVIS_JOB_ID"
- CI_JOB_NUMBER="$TRAVIS_JOB_NUMBER"
- CI_OS_NAME="$TRAVIS_OS_NAME"
- CI_REPO_SLUG="$TRAVIS_REPO_SLUG"
-
- cache_dir="$HOME/travis-cache"
-
- url_for_job_id () {
- echo "https://travis-ci.org/$CI_REPO_SLUG/jobs/$1"
- }
-
- BREW_INSTALL_PACKAGES="git-lfs gettext"
- export GIT_PROVE_OPTS="--timer --jobs 3 --state=failed,slow,save"
- export GIT_TEST_OPTS="--verbose-log -x --immediate"
- MAKEFLAGS="$MAKEFLAGS --jobs=2"
-elif test -n "$SYSTEM_COLLECTIONURI" || test -n "$SYSTEM_TASKDEFINITIONSURI"
+if test -n "$SYSTEM_COLLECTIONURI" || test -n "$SYSTEM_TASKDEFINITIONSURI"
then
CI_TYPE=azure-pipelines
# We are running in Azure Pipelines
@@ -130,10 +108,6 @@
# among *all* phases)
cache_dir="$HOME/test-cache/$SYSTEM_PHASENAME"
- url_for_job_id () {
- echo "$SYSTEM_TASKDEFINITIONSURI$SYSTEM_TEAMPROJECT/_build/results?buildId=$1"
- }
-
export GIT_PROVE_OPTS="--timer --jobs 10 --state=failed,slow,save"
export GIT_TEST_OPTS="--verbose-log -x --write-junit-xml"
MAKEFLAGS="$MAKEFLAGS --jobs=10"
@@ -182,11 +156,15 @@
export GIT_TEST_CLONE_2GB=true
export SKIP_DASHED_BUILT_INS=YesPlease
-case "$jobname" in
-linux-clang|linux-gcc|linux-leaks)
+case "$runs_on_pool" in
+ubuntu-latest)
+ if test "$jobname" = "linux-gcc-default"
+ then
+ break
+ fi
+
if [ "$jobname" = linux-gcc ]
then
- export CC=gcc-8
MAKEFLAGS="$MAKEFLAGS PYTHON_PATH=/usr/bin/python3"
else
MAKEFLAGS="$MAKEFLAGS PYTHON_PATH=/usr/bin/python2"
@@ -206,24 +184,20 @@
GIT_LFS_PATH="$HOME/custom/git-lfs"
export PATH="$GIT_LFS_PATH:$P4_PATH:$PATH"
;;
-osx-clang|osx-gcc)
+macos-latest)
if [ "$jobname" = osx-gcc ]
then
- export CC=gcc-9
MAKEFLAGS="$MAKEFLAGS PYTHON_PATH=$(which python3)"
else
MAKEFLAGS="$MAKEFLAGS PYTHON_PATH=$(which python2)"
fi
+ ;;
+esac
- # t9810 occasionally fails on Travis CI OS X
- # t9816 occasionally fails with "TAP out of sequence errors" on
- # Travis CI OS X
- export GIT_SKIP_TESTS="t9810 t9816"
- ;;
-linux-gcc-default)
- ;;
-Linux32)
+case "$jobname" in
+linux32)
CC=gcc
+ MAKEFLAGS="$MAKEFLAGS NO_UNCOMPRESS2=1"
;;
linux-musl)
CC=gcc
@@ -231,9 +205,6 @@
MAKEFLAGS="$MAKEFLAGS NO_REGEX=Yes ICONV_OMITS_BOM=Yes"
MAKEFLAGS="$MAKEFLAGS GIT_TEST_UTF8_LOCALE=C.UTF-8"
;;
-esac
-
-case "$jobname" in
linux-leaks)
export SANITIZE=leak
export GIT_TEST_PASSING_SANITIZE_LEAK=true
diff --git a/ci/print-test-failures.sh b/ci/print-test-failures.sh
index c70d6cd..57277ee 100755
--- a/ci/print-test-failures.sh
+++ b/ci/print-test-failures.sh
@@ -39,8 +39,6 @@
test_name="${test_name##*/}"
trash_dir="trash directory.$test_name"
case "$CI_TYPE" in
- travis)
- ;;
azure-pipelines)
mkdir -p failed-test-artifacts
mv "$trash_dir" failed-test-artifacts
@@ -88,11 +86,3 @@
fi
fi
done
-
-if [ $combined_trash_size -gt 0 ]
-then
- echo "------------------------------------------------------------------------"
- echo "Trash directories embedded in this log can be extracted by running:"
- echo
- echo " curl https://api.travis-ci.org/v3/job/$TRAVIS_JOB_ID/log.txt |./ci/util/extract-trash-dirs.sh"
-fi
diff --git a/ci/run-build-and-tests.sh b/ci/run-build-and-tests.sh
index cc62616..280dda7 100755
--- a/ci/run-build-and-tests.sh
+++ b/ci/run-build-and-tests.sh
@@ -10,16 +10,13 @@
*) ln -s "$cache_dir/.prove" t/.prove;;
esac
-if test "$jobname" = "pedantic"
-then
- export DEVOPTS=pedantic
-fi
+export MAKE_TARGETS="all test"
-make
case "$jobname" in
linux-gcc)
export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
- make test
+ ;;
+linux-TEST-vars)
export GIT_TEST_SPLIT_INDEX=yes
export GIT_TEST_MERGE_ALGORITHM=recursive
export GIT_TEST_FULL_IN_PACK_ARRAY=true
@@ -33,23 +30,25 @@
export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=master
export GIT_TEST_WRITE_REV_INDEX=1
export GIT_TEST_CHECKOUT_WORKERS=2
- make test
;;
linux-clang)
export GIT_TEST_DEFAULT_HASH=sha1
- make test
+ ;;
+linux-sha256)
export GIT_TEST_DEFAULT_HASH=sha256
- make test
;;
-linux-gcc-4.8|pedantic)
+pedantic)
# Don't run the tests; we only care about whether Git can be
- # built with GCC 4.8 or with pedantic
- ;;
-*)
- make test
+ # built.
+ export DEVOPTS=pedantic
+ export MAKE_TARGETS=all
;;
esac
+# Any new "test" targets should not go after this "make", but should
+# adjust $MAKE_TARGETS. Otherwise compilation-only targets above will
+# start running tests.
+make $MAKE_TARGETS
check_unignored_build_artifacts
save_good_tree
diff --git a/ci/run-docker-build.sh b/ci/run-docker-build.sh
index 8d47a5f..6cd832e 100755
--- a/ci/run-docker-build.sh
+++ b/ci/run-docker-build.sh
@@ -15,7 +15,7 @@
fi
case "$jobname" in
-Linux32)
+linux32)
switch_cmd="linux32 --32bit i386"
;;
linux-musl)
@@ -47,15 +47,6 @@
else
useradd -u $HOST_UID $CI_USER
fi
-
- # Due to a bug the test suite was run as root in the past, so
- # a prove state file created back then is only accessible by
- # root. Now that bug is fixed, the test suite is run as a
- # regular user, but the prove state file coming from Travis
- # CI's cache might still be owned by root.
- # Make sure that this user has rights to any cached files,
- # including an existing prove state file.
- test -n "$cache_dir" && chown -R $HOST_UID:$HOST_UID "$cache_dir"
fi
# Build and test
diff --git a/ci/run-docker.sh b/ci/run-docker.sh
index 37fa372..af89d16 100755
--- a/ci/run-docker.sh
+++ b/ci/run-docker.sh
@@ -6,7 +6,7 @@
. ${0%/*}/lib.sh
case "$jobname" in
-Linux32)
+linux32)
CI_CONTAINER="daald/ubuntu32:xenial"
;;
linux-musl)
@@ -25,7 +25,7 @@
# root@container:/# export jobname=<jobname>
# root@container:/# /usr/src/git/ci/run-docker-build.sh <host-user-id>
-container_cache_dir=/tmp/travis-cache
+container_cache_dir=/tmp/container-cache
docker run \
--interactive \
diff --git a/color.c b/color.c
index 64f52a4..4f884c6 100644
--- a/color.c
+++ b/color.c
@@ -40,7 +40,7 @@
enum {
COLOR_UNSPECIFIED = 0,
COLOR_NORMAL,
- COLOR_ANSI, /* basic 0-7 ANSI colors */
+ COLOR_ANSI, /* basic 0-7 ANSI colors + "default" (value = 9) */
COLOR_256,
COLOR_RGB
} type;
@@ -83,6 +83,27 @@
int i;
int color_offset = COLOR_FOREGROUND_ANSI;
+ if (match_word(name, len, "default")) {
+ /*
+ * Restores to the terminal's default color, which may not be
+ * the same as explicitly setting "white" or "black".
+ *
+ * ECMA-48 - Control Functions \
+ * for Coded Character Sets, 5th edition (June 1991):
+ * > 39 default display colour (implementation-defined)
+ * > 49 default background colour (implementation-defined)
+ *
+ * Although not supported /everywhere/--according to terminfo,
+ * some terminals define "op" (original pair) as a blunt
+ * "set to white on black", or even "send full SGR reset"--
+ * it's standard and well-supported enough that if a user
+ * asks for it in their config this will do the right thing.
+ */
+ out->type = COLOR_ANSI;
+ out->value = 9 + color_offset;
+ return 0;
+ }
+
if (strncasecmp(name, "bright", 6) == 0) {
color_offset = COLOR_FOREGROUND_BRIGHT_ANSI;
name += 6;
@@ -234,6 +255,7 @@
const char *ptr = value;
int len = value_len;
char *end = dst + COLOR_MAXLEN;
+ unsigned int has_reset = 0;
unsigned int attr = 0;
struct color fg = { COLOR_UNSPECIFIED };
struct color bg = { COLOR_UNSPECIFIED };
@@ -248,12 +270,7 @@
return 0;
}
- if (!strncasecmp(ptr, "reset", len)) {
- xsnprintf(dst, end - dst, GIT_COLOR_RESET);
- return 0;
- }
-
- /* [fg [bg]] [attr]... */
+ /* [reset] [fg [bg]] [attr]... */
while (len > 0) {
const char *word = ptr;
struct color c = { COLOR_UNSPECIFIED };
@@ -270,6 +287,11 @@
len--;
}
+ if (match_word(word, wordlen, "reset")) {
+ has_reset = 1;
+ continue;
+ }
+
if (!parse_color(&c, word, wordlen)) {
if (fg.type == COLOR_UNSPECIFIED) {
fg = c;
@@ -295,13 +317,16 @@
*dst++ = (x); \
} while(0)
- if (attr || !color_empty(&fg) || !color_empty(&bg)) {
+ if (has_reset || attr || !color_empty(&fg) || !color_empty(&bg)) {
int sep = 0;
int i;
OUT('\033');
OUT('[');
+ if (has_reset)
+ sep++;
+
for (i = 0; attr; i++) {
unsigned bit = (1 << i);
if (!(attr & bit))
diff --git a/color.h b/color.h
index 98894d6..cfc8f84 100644
--- a/color.h
+++ b/color.h
@@ -6,6 +6,7 @@
/*
* The maximum length of ANSI color sequence we would generate:
* - leading ESC '[' 2
+ * - reset ';' .................1
* - attr + ';' 2 * num_attr (e.g. "1;")
* - no-attr + ';' 3 * num_attr (e.g. "22;")
* - fg color + ';' 17 (e.g. "38;2;255;255;255;")
@@ -24,30 +25,42 @@
#define GIT_COLOR_NORMAL ""
#define GIT_COLOR_RESET "\033[m"
#define GIT_COLOR_BOLD "\033[1m"
+#define GIT_COLOR_BLACK "\033[30m"
#define GIT_COLOR_RED "\033[31m"
#define GIT_COLOR_GREEN "\033[32m"
#define GIT_COLOR_YELLOW "\033[33m"
#define GIT_COLOR_BLUE "\033[34m"
#define GIT_COLOR_MAGENTA "\033[35m"
#define GIT_COLOR_CYAN "\033[36m"
+#define GIT_COLOR_WHITE "\033[37m"
+#define GIT_COLOR_DEFAULT "\033[39m"
+#define GIT_COLOR_BOLD_BLACK "\033[1;30m"
#define GIT_COLOR_BOLD_RED "\033[1;31m"
#define GIT_COLOR_BOLD_GREEN "\033[1;32m"
#define GIT_COLOR_BOLD_YELLOW "\033[1;33m"
#define GIT_COLOR_BOLD_BLUE "\033[1;34m"
#define GIT_COLOR_BOLD_MAGENTA "\033[1;35m"
#define GIT_COLOR_BOLD_CYAN "\033[1;36m"
+#define GIT_COLOR_BOLD_WHITE "\033[1;37m"
+#define GIT_COLOR_BOLD_DEFAULT "\033[1;39m"
+#define GIT_COLOR_FAINT_BLACK "\033[2;30m"
#define GIT_COLOR_FAINT_RED "\033[2;31m"
#define GIT_COLOR_FAINT_GREEN "\033[2;32m"
#define GIT_COLOR_FAINT_YELLOW "\033[2;33m"
#define GIT_COLOR_FAINT_BLUE "\033[2;34m"
#define GIT_COLOR_FAINT_MAGENTA "\033[2;35m"
#define GIT_COLOR_FAINT_CYAN "\033[2;36m"
+#define GIT_COLOR_FAINT_WHITE "\033[2;37m"
+#define GIT_COLOR_FAINT_DEFAULT "\033[2;39m"
+#define GIT_COLOR_BG_BLACK "\033[40m"
#define GIT_COLOR_BG_RED "\033[41m"
#define GIT_COLOR_BG_GREEN "\033[42m"
#define GIT_COLOR_BG_YELLOW "\033[43m"
#define GIT_COLOR_BG_BLUE "\033[44m"
#define GIT_COLOR_BG_MAGENTA "\033[45m"
#define GIT_COLOR_BG_CYAN "\033[46m"
+#define GIT_COLOR_BG_WHITE "\033[47m"
+#define GIT_COLOR_BG_DEFAULT "\033[49m"
#define GIT_COLOR_FAINT "\033[2m"
#define GIT_COLOR_FAINT_ITALIC "\033[2;3m"
#define GIT_COLOR_REVERSE "\033[7m"
diff --git a/command-list.txt b/command-list.txt
index eb9cee8..675c28f 100644
--- a/command-list.txt
+++ b/command-list.txt
@@ -43,7 +43,7 @@
# specified here, which can only have "guide" attribute and nothing
# else.
#
-### command list (do not change this line, also do not change alignment)
+### command list (do not change this line)
# command name category [category] [category]
git-add mainporcelain worktree
git-am mainporcelain
@@ -60,9 +60,9 @@
git-check-attr purehelpers
git-check-ignore purehelpers
git-check-mailmap purehelpers
+git-check-ref-format purehelpers
git-checkout mainporcelain
git-checkout-index plumbingmanipulators
-git-check-ref-format purehelpers
git-cherry plumbinginterrogators complete
git-cherry-pick mainporcelain
git-citool mainporcelain
@@ -111,7 +111,6 @@
git-init mainporcelain init
git-instaweb ancillaryinterrogators complete
git-interpret-trailers purehelpers
-gitk mainporcelain
git-log mainporcelain info
git-ls-files plumbinginterrogators
git-ls-remote plumbinginterrogators
@@ -124,11 +123,11 @@
git-merge-file plumbingmanipulators
git-merge-index plumbingmanipulators
git-merge-one-file purehelpers
-git-mergetool ancillarymanipulators complete
git-merge-tree ancillaryinterrogators
-git-multi-pack-index plumbingmanipulators
+git-mergetool ancillarymanipulators complete
git-mktag plumbingmanipulators
git-mktree plumbingmanipulators
+git-multi-pack-index plumbingmanipulators
git-mv mainporcelain worktree
git-name-rev plumbinginterrogators
git-notes mainporcelain
@@ -154,23 +153,23 @@
git-rerere ancillaryinterrogators
git-reset mainporcelain history
git-restore mainporcelain worktree
-git-revert mainporcelain
git-rev-list plumbinginterrogators
git-rev-parse plumbinginterrogators
+git-revert mainporcelain
git-rm mainporcelain worktree
git-send-email foreignscminterface complete
git-send-pack synchingrepositories
+git-sh-i18n purehelpers
+git-sh-setup purehelpers
git-shell synchelpers
git-shortlog mainporcelain
git-show mainporcelain info
git-show-branch ancillaryinterrogators complete
git-show-index plumbinginterrogators
git-show-ref plumbinginterrogators
-git-sh-i18n purehelpers
-git-sh-setup purehelpers
git-sparse-checkout mainporcelain
-git-stash mainporcelain
git-stage complete
+git-stash mainporcelain
git-status mainporcelain info
git-stripspace purehelpers
git-submodule mainporcelain
@@ -189,7 +188,6 @@
git-verify-commit ancillaryinterrogators
git-verify-pack plumbinginterrogators
git-verify-tag ancillaryinterrogators
-gitweb ancillaryinterrogators
git-whatchanged ancillaryinterrogators complete
git-worktree mainporcelain
git-write-tree plumbingmanipulators
@@ -204,6 +202,7 @@
gitglossary guide
githooks guide
gitignore guide
+gitk mainporcelain
gitmailmap guide
gitmodules guide
gitnamespaces guide
@@ -211,6 +210,7 @@
gitrepository-layout guide
gitrevisions guide
gitsubmodules guide
-gittutorial-2 guide
gittutorial guide
+gittutorial-2 guide
+gitweb ancillaryinterrogators
gitworkflows guide
diff --git a/commit-graph.c b/commit-graph.c
index 2706683..265c010 100644
--- a/commit-graph.c
+++ b/commit-graph.c
@@ -632,10 +632,13 @@
struct object_directory *odb;
/*
+ * Early return if there is no git dir or if the commit graph is
+ * disabled.
+ *
* This must come before the "already attempted?" check below, because
* we want to disable even an already-loaded graph file.
*/
- if (r->commit_graph_disabled)
+ if (!r->gitdir || r->commit_graph_disabled)
return 0;
if (r->objects->commit_graph_attempted)
diff --git a/commit.c b/commit.c
index 551de49..a348f08 100644
--- a/commit.c
+++ b/commit.c
@@ -1212,8 +1212,10 @@
if (parse_signed_commit(commit, &payload, &signature, the_hash_algo) <= 0)
goto out;
- ret = check_signature(payload.buf, payload.len, signature.buf,
- signature.len, sigc);
+
+ sigc->payload_type = SIGNATURE_PAYLOAD_COMMIT;
+ sigc->payload = strbuf_detach(&payload, &sigc->payload_len);
+ ret = check_signature(sigc, signature.buf, signature.len);
out:
strbuf_release(&payload);
diff --git a/common-main.c b/common-main.c
index 71e21dd..29fb745 100644
--- a/common-main.c
+++ b/common-main.c
@@ -26,6 +26,7 @@
int main(int argc, const char **argv)
{
int result;
+ struct strbuf tmp = STRBUF_INIT;
trace2_initialize_clock();
@@ -49,9 +50,15 @@
trace2_cmd_start(argv);
trace2_collect_process_info(TRACE2_PROCESS_INFO_STARTUP);
+ if (!strbuf_getcwd(&tmp))
+ tmp_original_cwd = strbuf_detach(&tmp, NULL);
+
result = cmd_main(argc, argv);
- trace2_cmd_exit(result);
-
- return result;
+ /*
+ * We define exit() to call trace2_cmd_exit_fl() in
+ * git-compat-util.h. Whether we reach this or exit()
+ * elsewhere we'll always run our trace2 exit handler.
+ */
+ exit(result);
}
diff --git a/compat/.gitattributes b/compat/.gitattributes
new file mode 100644
index 0000000..40dbfb1
--- /dev/null
+++ b/compat/.gitattributes
@@ -0,0 +1 @@
+/zlib-uncompress2.c whitespace=-indent-with-non-tab,-trailing-space
diff --git a/compat/mingw.c b/compat/mingw.c
index 5e42191..41fc163 100644
--- a/compat/mingw.c
+++ b/compat/mingw.c
@@ -9,6 +9,8 @@
#include "win32/lazyload.h"
#include "../config.h"
#include "dir.h"
+#define SECURITY_WIN32
+#include <sspi.h>
#define HCAST(type, handle) ((type)(intptr_t)handle)
@@ -1009,7 +1011,7 @@
/* a pointer to the original strftime in case we can't find the UCRT version */
static size_t (*fallback)(char *, size_t, const char *, const struct tm *) = strftime;
size_t ret;
- DECLARE_PROC_ADDR(ucrtbase.dll, size_t, strftime, char *, size_t,
+ DECLARE_PROC_ADDR(ucrtbase.dll, size_t, __cdecl, strftime, char *, size_t,
const char *, const struct tm *);
if (INIT_PROC_ADDR(strftime))
@@ -1126,6 +1128,10 @@
}
if (!ret || ret >= ARRAY_SIZE(wpointer))
return NULL;
+ if (GetFileAttributesW(wpointer) == INVALID_FILE_ATTRIBUTES) {
+ errno = ENOENT;
+ return NULL;
+ }
if (xwcstoutf(pointer, wpointer, len) < 0)
return NULL;
convert_slashes(pointer);
@@ -2186,7 +2192,7 @@
static char *get_extended_user_info(enum EXTENDED_NAME_FORMAT type)
{
- DECLARE_PROC_ADDR(secur32.dll, BOOL, GetUserNameExW,
+ DECLARE_PROC_ADDR(secur32.dll, BOOL, SEC_ENTRY, GetUserNameExW,
enum EXTENDED_NAME_FORMAT, LPCWSTR, PULONG);
static wchar_t wbuffer[1024];
DWORD len;
diff --git a/compat/unsetenv.c b/compat/unsetenv.c
index bf5fd70..b9d34af 100644
--- a/compat/unsetenv.c
+++ b/compat/unsetenv.c
@@ -1,6 +1,6 @@
#include "../git-compat-util.h"
-void gitunsetenv (const char *name)
+int gitunsetenv(const char *name)
{
#if !defined(__MINGW32__)
extern char **environ;
@@ -24,4 +24,6 @@
++dst;
}
environ[dst] = NULL;
+
+ return 0;
}
diff --git a/compat/win32/lazyload.h b/compat/win32/lazyload.h
index 2b36371..f2bb96c 100644
--- a/compat/win32/lazyload.h
+++ b/compat/win32/lazyload.h
@@ -4,7 +4,7 @@
/*
* A pair of macros to simplify loading of DLL functions. Example:
*
- * DECLARE_PROC_ADDR(kernel32.dll, BOOL, CreateHardLinkW,
+ * DECLARE_PROC_ADDR(kernel32.dll, BOOL, WINAPI, CreateHardLinkW,
* LPCWSTR, LPCWSTR, LPSECURITY_ATTRIBUTES);
*
* if (!INIT_PROC_ADDR(CreateHardLinkW))
@@ -25,10 +25,10 @@
};
/* Declares a function to be loaded dynamically from a DLL. */
-#define DECLARE_PROC_ADDR(dll, rettype, function, ...) \
+#define DECLARE_PROC_ADDR(dll, rettype, convention, function, ...) \
static struct proc_addr proc_addr_##function = \
{ #dll, #function, NULL, 0 }; \
- typedef rettype (WINAPI *proc_type_##function)(__VA_ARGS__); \
+ typedef rettype (convention *proc_type_##function)(__VA_ARGS__); \
static proc_type_##function function
/*
diff --git a/compat/win32/trace2_win32_process_info.c b/compat/win32/trace2_win32_process_info.c
index 8ccbd1c..a53fd92 100644
--- a/compat/win32/trace2_win32_process_info.c
+++ b/compat/win32/trace2_win32_process_info.c
@@ -143,8 +143,8 @@
*/
static void get_peak_memory_info(void)
{
- DECLARE_PROC_ADDR(psapi.dll, BOOL, GetProcessMemoryInfo, HANDLE,
- PPROCESS_MEMORY_COUNTERS, DWORD);
+ DECLARE_PROC_ADDR(psapi.dll, BOOL, WINAPI, GetProcessMemoryInfo,
+ HANDLE, PPROCESS_MEMORY_COUNTERS, DWORD);
if (INIT_PROC_ADDR(GetProcessMemoryInfo)) {
PROCESS_MEMORY_COUNTERS pmc;
diff --git a/compat/winansi.c b/compat/winansi.c
index c27b20a..4fceecf 100644
--- a/compat/winansi.c
+++ b/compat/winansi.c
@@ -45,8 +45,9 @@
static void warn_if_raster_font(void)
{
DWORD fontFamily = 0;
- DECLARE_PROC_ADDR(kernel32.dll, BOOL, GetCurrentConsoleFontEx,
- HANDLE, BOOL, PCONSOLE_FONT_INFOEX);
+ DECLARE_PROC_ADDR(kernel32.dll, BOOL, WINAPI,
+ GetCurrentConsoleFontEx, HANDLE, BOOL,
+ PCONSOLE_FONT_INFOEX);
/* don't bother if output was ascii only */
if (!non_ascii_used)
diff --git a/compat/zlib-uncompress2.c b/compat/zlib-uncompress2.c
new file mode 100644
index 0000000..722610b
--- /dev/null
+++ b/compat/zlib-uncompress2.c
@@ -0,0 +1,95 @@
+/* taken from zlib's uncompr.c
+
+ commit cacf7f1d4e3d44d871b605da3b647f07d718623f
+ Author: Mark Adler <madler@alumni.caltech.edu>
+ Date: Sun Jan 15 09:18:46 2017 -0800
+
+ zlib 1.2.11
+
+*/
+
+#include "../reftable/system.h"
+#define z_const
+
+/*
+ * Copyright (C) 1995-2003, 2010, 2014, 2016 Jean-loup Gailly, Mark Adler
+ * For conditions of distribution and use, see copyright notice in zlib.h
+ */
+
+#include <zlib.h>
+
+/* clang-format off */
+
+/* ===========================================================================
+ Decompresses the source buffer into the destination buffer. *sourceLen is
+ the byte length of the source buffer. Upon entry, *destLen is the total size
+ of the destination buffer, which must be large enough to hold the entire
+ uncompressed data. (The size of the uncompressed data must have been saved
+ previously by the compressor and transmitted to the decompressor by some
+ mechanism outside the scope of this compression library.) Upon exit,
+ *destLen is the size of the decompressed data and *sourceLen is the number
+ of source bytes consumed. Upon return, source + *sourceLen points to the
+ first unused input byte.
+
+ uncompress returns Z_OK if success, Z_MEM_ERROR if there was not enough
+ memory, Z_BUF_ERROR if there was not enough room in the output buffer, or
+ Z_DATA_ERROR if the input data was corrupted, including if the input data is
+ an incomplete zlib stream.
+*/
+int ZEXPORT uncompress2 (
+ Bytef *dest,
+ uLongf *destLen,
+ const Bytef *source,
+ uLong *sourceLen) {
+ z_stream stream;
+ int err;
+ const uInt max = (uInt)-1;
+ uLong len, left;
+ Byte buf[1]; /* for detection of incomplete stream when *destLen == 0 */
+
+ len = *sourceLen;
+ if (*destLen) {
+ left = *destLen;
+ *destLen = 0;
+ }
+ else {
+ left = 1;
+ dest = buf;
+ }
+
+ stream.next_in = (z_const Bytef *)source;
+ stream.avail_in = 0;
+ stream.zalloc = (alloc_func)0;
+ stream.zfree = (free_func)0;
+ stream.opaque = (voidpf)0;
+
+ err = inflateInit(&stream);
+ if (err != Z_OK) return err;
+
+ stream.next_out = dest;
+ stream.avail_out = 0;
+
+ do {
+ if (stream.avail_out == 0) {
+ stream.avail_out = left > (uLong)max ? max : (uInt)left;
+ left -= stream.avail_out;
+ }
+ if (stream.avail_in == 0) {
+ stream.avail_in = len > (uLong)max ? max : (uInt)len;
+ len -= stream.avail_in;
+ }
+ err = inflate(&stream, Z_NO_FLUSH);
+ } while (err == Z_OK);
+
+ *sourceLen -= len + stream.avail_in;
+ if (dest != buf)
+ *destLen = stream.total_out;
+ else if (stream.total_out && err == Z_BUF_ERROR)
+ left = 1;
+
+ inflateEnd(&stream);
+ return err == Z_STREAM_END ? Z_OK :
+ err == Z_NEED_DICT ? Z_DATA_ERROR :
+ err == Z_BUF_ERROR && left + stream.avail_out ? Z_DATA_ERROR :
+ err;
+}
diff --git a/config.c b/config.c
index c5873f3..2bffa8d 100644
--- a/config.c
+++ b/config.c
@@ -1559,9 +1559,12 @@
static int git_default_branch_config(const char *var, const char *value)
{
if (!strcmp(var, "branch.autosetupmerge")) {
- if (value && !strcasecmp(value, "always")) {
+ if (value && !strcmp(value, "always")) {
git_branch_track = BRANCH_TRACK_ALWAYS;
return 0;
+ } else if (value && !strcmp(value, "inherit")) {
+ git_branch_track = BRANCH_TRACK_INHERIT;
+ return 0;
}
git_branch_track = git_config_bool(var, value);
return 0;
@@ -2555,11 +2558,12 @@
{
const struct string_list *values;
struct key_value_info *kv_info;
+ report_fn error_fn = get_error_routine();
if (err) {
va_list params;
va_start(params, err);
- vreportf("error: ", err, params);
+ error_fn(err, params);
va_end(params);
}
values = git_config_get_value_multi(key);
diff --git a/config.mak.dev b/config.mak.dev
index 7673fed..3deb076 100644
--- a/config.mak.dev
+++ b/config.mak.dev
@@ -19,6 +19,16 @@
endif
endif
endif
+
+ifneq ($(uname_S),FreeBSD)
+ifneq ($(or $(filter gcc6,$(COMPILER_FEATURES)),$(filter clang7,$(COMPILER_FEATURES))),)
+DEVELOPER_CFLAGS += -std=gnu99
+endif
+else
+# FreeBSD cannot limit to C99 because its system headers unconditionally
+# rely on C11 features.
+endif
+
DEVELOPER_CFLAGS += -Wdeclaration-after-statement
DEVELOPER_CFLAGS += -Wformat-security
DEVELOPER_CFLAGS += -Wold-style-definition
diff --git a/config.mak.uname b/config.mak.uname
index 3236a49..c48db45 100644
--- a/config.mak.uname
+++ b/config.mak.uname
@@ -58,12 +58,16 @@
# -lrt is needed for clock_gettime on glibc <= 2.16
NEEDS_LIBRT = YesPlease
HAVE_GETDELIM = YesPlease
- SANE_TEXT_GREP=-a
FREAD_READS_DIRECTORIES = UnfortunatelyYes
BASIC_CFLAGS += -DHAVE_SYSINFO
PROCFS_EXECUTABLE_PATH = /proc/self/exe
HAVE_PLATFORM_PROCINFO = YesPlease
COMPAT_OBJS += compat/linux/procinfo.o
+ # centos7/rhel7 provides gcc 4.8.5 and zlib 1.2.7.
+ ifneq ($(findstring .el7.,$(uname_R)),)
+ BASIC_CFLAGS += -std=c99
+ NO_UNCOMPRESS2 = YesPlease
+ endif
endif
ifeq ($(uname_S),GNU/kFreeBSD)
HAVE_ALLOCA_H = YesPlease
@@ -262,6 +266,10 @@
FILENO_IS_A_MACRO = UnfortunatelyYes
endif
ifeq ($(uname_S),OpenBSD)
+ # Versions < 7.0 need compatibility layer
+ ifeq ($(shell expr "$(uname_R)" : "[1-6]\."),2)
+ NO_UNCOMPRESS2 = UnfortunatelyYes
+ endif
NO_STRCASESTR = YesPlease
NO_MEMMEM = YesPlease
USE_ST_TIMESPEC = YesPlease
@@ -517,6 +525,7 @@
endif
endif
ifeq ($(uname_S),Minix)
+ NO_UNCOMPRESS2 = YesPlease
NO_IPV6 = YesPlease
NO_ST_BLOCKS_IN_STRUCT_STAT = YesPlease
NO_NSEC = YesPlease
@@ -572,6 +581,7 @@
NO_SETENV = YesPlease
NO_UNSETENV = YesPlease
NO_MKDTEMP = YesPlease
+ NO_UNCOMPRESS2 = YesPlease
# Currently libiconv-1.9.1.
OLD_ICONV = UnfortunatelyYes
NO_REGEX = NeedsStartEnd
diff --git a/configure.ac b/configure.ac
index 031e8d3..d60d494 100644
--- a/configure.ac
+++ b/configure.ac
@@ -507,14 +507,6 @@
esac
fi
-if grep -a ascii configure.ac >/dev/null; then
- AC_MSG_RESULT([Using 'grep -a' for sane_grep])
- SANE_TEXT_GREP=-a
-else
- SANE_TEXT_GREP=
-fi
-GIT_CONF_SUBST([SANE_TEXT_GREP])
-
## Checks for libraries.
AC_MSG_NOTICE([CHECKS for libraries])
#
@@ -672,9 +664,22 @@
NO_DEFLATE_BOUND=yes])
LIBS="$old_LIBS"
+AC_DEFUN([ZLIBTEST_UNCOMPRESS2_SRC], [
+AC_LANG_PROGRAM([#include <zlib.h>],
+ [uncompress2(NULL,NULL,NULL,NULL);])])
+AC_MSG_CHECKING([for uncompress2 in -lz])
+old_LIBS="$LIBS"
+LIBS="$LIBS -lz"
+AC_LINK_IFELSE([ZLIBTEST_UNCOMPRESS2_SRC],
+ [AC_MSG_RESULT([yes])],
+ [AC_MSG_RESULT([no])
+ NO_UNCOMPRESS2=yes])
+LIBS="$old_LIBS"
+
GIT_UNSTASH_FLAGS($ZLIB_PATH)
GIT_CONF_SUBST([NO_DEFLATE_BOUND])
+GIT_CONF_SUBST([NO_UNCOMPRESS2])
#
# Define NEEDS_SOCKET if linking with libc is not enough (SunOS,
diff --git a/connected.c b/connected.c
index 35bd4a2..ed3025e 100644
--- a/connected.c
+++ b/connected.c
@@ -109,7 +109,8 @@
_("Checking connectivity"));
rev_list.git_cmd = 1;
- rev_list.env = opt->env;
+ if (opt->env)
+ strvec_pushv(&rev_list.env_array, opt->env);
rev_list.in = -1;
rev_list.no_stdout = 1;
if (opt->err_fd)
diff --git a/contrib/buildsystems/CMakeLists.txt b/contrib/buildsystems/CMakeLists.txt
index fd1399c..5100f56 100644
--- a/contrib/buildsystems/CMakeLists.txt
+++ b/contrib/buildsystems/CMakeLists.txt
@@ -208,7 +208,7 @@
if(CMAKE_C_COMPILER_ID STREQUAL "MSVC")
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY_DEBUG ${CMAKE_BINARY_DIR})
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY_RELEASE ${CMAKE_BINARY_DIR})
- add_compile_options(/MP)
+ add_compile_options(/MP /std:c11)
endif()
#default behaviour
@@ -647,6 +647,12 @@
list(TRANSFORM libxdiff_SOURCES PREPEND "${CMAKE_SOURCE_DIR}/")
add_library(xdiff STATIC ${libxdiff_SOURCES})
+#reftable
+parse_makefile_for_sources(reftable_SOURCES "REFTABLE_OBJS")
+
+list(TRANSFORM reftable_SOURCES PREPEND "${CMAKE_SOURCE_DIR}/")
+add_library(reftable STATIC ${reftable_SOURCES})
+
if(WIN32)
if(NOT MSVC)#use windres when compiling with gcc and clang
add_custom_command(OUTPUT ${CMAKE_BINARY_DIR}/git.res
@@ -669,7 +675,7 @@
#link all required libraries to common-main
add_library(common-main OBJECT ${CMAKE_SOURCE_DIR}/common-main.c)
-target_link_libraries(common-main libgit xdiff ${ZLIB_LIBRARIES})
+target_link_libraries(common-main libgit xdiff reftable ${ZLIB_LIBRARIES})
if(Intl_FOUND)
target_link_libraries(common-main ${Intl_LIBRARIES})
endif()
@@ -781,7 +787,6 @@
string(REPLACE "@@USE_GETTEXT_SCHEME@@" "" content "${content}")
string(REPLACE "# @@BROKEN_PATH_FIX@@" "" content "${content}")
string(REPLACE "@@PERL@@" "${PERL_PATH}" content "${content}")
- string(REPLACE "@@SANE_TEXT_GREP@@" "-a" content "${content}")
string(REPLACE "@@PAGER_ENV@@" "LESS=FRX LV=-c" content "${content}")
file(WRITE ${CMAKE_BINARY_DIR}/${script} ${content})
endforeach()
@@ -909,11 +914,15 @@
add_executable(test-fake-ssh ${CMAKE_SOURCE_DIR}/t/helper/test-fake-ssh.c)
target_link_libraries(test-fake-ssh common-main)
+#reftable-tests
+parse_makefile_for_sources(test-reftable_SOURCES "REFTABLE_TEST_OBJS")
+list(TRANSFORM test-reftable_SOURCES PREPEND "${CMAKE_SOURCE_DIR}/")
+
#test-tool
parse_makefile_for_sources(test-tool_SOURCES "TEST_BUILTINS_OBJS")
list(TRANSFORM test-tool_SOURCES PREPEND "${CMAKE_SOURCE_DIR}/t/helper/")
-add_executable(test-tool ${CMAKE_SOURCE_DIR}/t/helper/test-tool.c ${test-tool_SOURCES})
+add_executable(test-tool ${CMAKE_SOURCE_DIR}/t/helper/test-tool.c ${test-tool_SOURCES} ${test-reftable_SOURCES})
target_link_libraries(test-tool common-main)
set_target_properties(test-fake-ssh test-tool
diff --git a/contrib/buildsystems/Generators/Vcxproj.pm b/contrib/buildsystems/Generators/Vcxproj.pm
index d258445..1a25789 100644
--- a/contrib/buildsystems/Generators/Vcxproj.pm
+++ b/contrib/buildsystems/Generators/Vcxproj.pm
@@ -77,7 +77,7 @@
my $libs_release = "\n ";
my $libs_debug = "\n ";
if (!$static_library) {
- $libs_release = join(";", sort(grep /^(?!libgit\.lib|xdiff\/lib\.lib|vcs-svn\/lib\.lib)/, @{$$build_structure{"$prefix${name}_LIBS"}}));
+ $libs_release = join(";", sort(grep /^(?!libgit\.lib|xdiff\/lib\.lib|vcs-svn\/lib\.lib|reftable\/libreftable\.lib)/, @{$$build_structure{"$prefix${name}_LIBS"}}));
$libs_debug = $libs_release;
$libs_debug =~ s/zlib\.lib/zlibd\.lib/g;
$libs_debug =~ s/libexpat\.lib/libexpatd\.lib/g;
@@ -232,6 +232,7 @@
EOM
if (!$static_library || $target =~ 'vcs-svn' || $target =~ 'xdiff') {
my $uuid_libgit = $$build_structure{"LIBS_libgit_GUID"};
+ my $uuid_libreftable = $$build_structure{"LIBS_reftable/libreftable_GUID"};
my $uuid_xdiff_lib = $$build_structure{"LIBS_xdiff/lib_GUID"};
print F << "EOM";
@@ -241,6 +242,14 @@
<ReferenceOutputAssembly>false</ReferenceOutputAssembly>
</ProjectReference>
EOM
+ if (!($name =~ /xdiff|libreftable/)) {
+ print F << "EOM";
+ <ProjectReference Include="$cdup\\reftable\\libreftable\\libreftable.vcxproj">
+ <Project>$uuid_libreftable</Project>
+ <ReferenceOutputAssembly>false</ReferenceOutputAssembly>
+ </ProjectReference>
+EOM
+ }
if (!($name =~ 'xdiff')) {
print F << "EOM";
<ProjectReference Include="$cdup\\xdiff\\lib\\xdiff_lib.vcxproj">
diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash
index 7c3a753..377d6c5 100644
--- a/contrib/completion/git-completion.bash
+++ b/contrib/completion/git-completion.bash
@@ -1566,7 +1566,7 @@
case "$cur" in
--conflict=*)
- __gitcomp "diff3 merge" "" "${cur##--conflict=}"
+ __gitcomp "diff3 merge zdiff3" "" "${cur##--conflict=}"
;;
--*)
__gitcomp_builtin checkout
@@ -2001,7 +2001,7 @@
"
__git_log_pretty_formats="oneline short medium full fuller reference email raw format: tformat: mboxrd"
-__git_log_date_formats="relative iso8601 iso8601-strict rfc2822 short local default raw unix format:"
+__git_log_date_formats="relative iso8601 iso8601-strict rfc2822 short local default human raw unix auto: format:"
_git_log ()
{
@@ -2359,16 +2359,7 @@
return
;;
--*)
- __gitcomp_builtin send-email "--annotate --bcc --cc --cc-cmd --chain-reply-to
- --compose --confirm= --dry-run --envelope-sender
- --from --identity
- --in-reply-to --no-chain-reply-to --no-signed-off-by-cc
- --no-suppress-from --no-thread --quiet --reply-to
- --signed-off-by-cc --smtp-pass --smtp-server
- --smtp-server-port --smtp-encryption= --smtp-user
- --subject --suppress-cc= --suppress-from --thread --to
- --validate --no-validate
- $__git_format_patch_extra_options"
+ __gitcomp_builtin send-email "$__git_format_patch_extra_options"
return
;;
esac
@@ -2446,7 +2437,7 @@
case "$cur" in
--conflict=*)
- __gitcomp "diff3 merge" "" "${cur##--conflict=}"
+ __gitcomp "diff3 merge zdiff3" "" "${cur##--conflict=}"
;;
--*)
__gitcomp_builtin switch
@@ -2886,7 +2877,7 @@
case "$cur" in
--conflict=*)
- __gitcomp "diff3 merge" "" "${cur##--conflict=}"
+ __gitcomp "diff3 merge zdiff3" "" "${cur##--conflict=}"
;;
--source=*)
__git_complete_refs --cur="${cur##--source=}"
diff --git a/contrib/git-jump/README b/contrib/git-jump/README
index 2f618a7..8bcace2 100644
--- a/contrib/git-jump/README
+++ b/contrib/git-jump/README
@@ -65,6 +65,9 @@
# jump to merge conflicts
git jump merge
+# documentation conflicts are hard; skip past them for now
+git jump merge :^Documentation
+
# jump to all instances of foo_bar
git jump grep foo_bar
diff --git a/contrib/git-jump/git-jump b/contrib/git-jump/git-jump
index 931b0fe..92dbd4c 100755
--- a/contrib/git-jump/git-jump
+++ b/contrib/git-jump/git-jump
@@ -39,7 +39,7 @@
}
mode_merge() {
- git ls-files -u |
+ git ls-files -u "$@" |
perl -pe 's/^.*?\t//' |
sort -u |
while IFS= read fn; do
diff --git a/contrib/mw-to-git/t/t9365-continuing-queries.sh b/contrib/mw-to-git/t/t9365-continuing-queries.sh
index 0164547..d3e7312 100755
--- a/contrib/mw-to-git/t/t9365-continuing-queries.sh
+++ b/contrib/mw-to-git/t/t9365-continuing-queries.sh
@@ -12,7 +12,7 @@
for i in $(test_seq 501)
do
echo "creating revision $i" &&
- wiki_editpage foo "revision $i<br/>" true
+ wiki_editpage foo "revision $i<br/>" true || return 1
done
'
diff --git a/contrib/scalar/.gitignore b/contrib/scalar/.gitignore
new file mode 100644
index 0000000..ff3d47e
--- /dev/null
+++ b/contrib/scalar/.gitignore
@@ -0,0 +1,2 @@
+/*.exe
+/scalar
diff --git a/contrib/scalar/Makefile b/contrib/scalar/Makefile
new file mode 100644
index 0000000..231b1ee
--- /dev/null
+++ b/contrib/scalar/Makefile
@@ -0,0 +1,45 @@
+QUIET_SUBDIR0 = +$(MAKE) -C # space to separate -C and subdir
+QUIET_SUBDIR1 =
+
+ifneq ($(findstring s,$(MAKEFLAGS)),s)
+ifndef V
+ QUIET_GEN = @echo ' ' GEN $@;
+ QUIET_SUBDIR0 = +@subdir=
+ QUIET_SUBDIR1 = ;$(NO_SUBDIR) echo ' ' SUBDIR $$subdir; \
+ $(MAKE) $(PRINT_DIR) -C $$subdir
+else
+ export V
+endif
+endif
+
+all:
+
+include ../../config.mak.uname
+-include ../../config.mak.autogen
+-include ../../config.mak
+
+TARGETS = scalar$(X) scalar.o
+GITLIBS = ../../common-main.o ../../libgit.a ../../xdiff/lib.a
+
+all: scalar$(X) ../../bin-wrappers/scalar
+
+$(GITLIBS):
+ $(QUIET_SUBDIR0)../.. $(QUIET_SUBDIR1) $(subst ../../,,$@)
+
+$(TARGETS): $(GITLIBS) scalar.c
+ $(QUIET_SUBDIR0)../.. $(QUIET_SUBDIR1) $(patsubst %,contrib/scalar/%,$@)
+
+clean:
+ $(RM) $(TARGETS) ../../bin-wrappers/scalar
+
+../../bin-wrappers/scalar: ../../wrap-for-bin.sh Makefile
+ @mkdir -p ../../bin-wrappers
+ $(QUIET_GEN)sed -e '1s|#!.*/sh|#!$(SHELL_PATH_SQ)|' \
+ -e 's|@@BUILD_DIR@@|$(shell cd ../.. && pwd)|' \
+ -e 's|@@PROG@@|contrib/scalar/scalar$(X)|' < $< > $@ && \
+ chmod +x $@
+
+test: all
+ $(MAKE) -C t
+
+.PHONY: $(GITLIBS) all clean test FORCE
diff --git a/contrib/scalar/README.md b/contrib/scalar/README.md
new file mode 100644
index 0000000..634b577
--- /dev/null
+++ b/contrib/scalar/README.md
@@ -0,0 +1,82 @@
+# Scalar - an opinionated repository management tool
+
+Scalar is an add-on to Git that helps users take advantage of advanced
+performance features in Git. Originally implemented in C# using .NET Core,
+based on the learnings from the VFS for Git project, most of the techniques
+developed by the Scalar project have been integrated into core Git already:
+
+* partial clone,
+* commit graphs,
+* multi-pack index,
+* sparse checkout (cone mode),
+* scheduled background maintenance,
+* etc
+
+This directory contains the remaining parts of Scalar that are not (yet) in
+core Git.
+
+## Roadmap
+
+The idea is to populate this directory via incremental patch series and
+eventually move to a top-level directory next to `gitk-git/` and to `git-gui/`. The
+current plan involves the following patch series:
+
+- `scalar-the-beginning`: The initial patch series which sets up
+ `contrib/scalar/` and populates it with a minimal `scalar` command that
+ demonstrates the fundamental ideas.
+
+- `scalar-c-and-C`: The `scalar` command learns about two options that can be
+ specified before the command, `-c <key>=<value>` and `-C <directory>`.
+
+- `scalar-diagnose`: The `scalar` command is taught the `diagnose` subcommand.
+
+- `scalar-and-builtin-fsmonitor`: The built-in FSMonitor is enabled in `scalar
+ register` and in `scalar clone`, for an enormous performance boost when
+ working in large worktrees. This patch series necessarily depends on Jeff
+ Hostetler's FSMonitor patch series to be integrated into Git.
+
+- `scalar-gentler-config-locking`: Scalar enlistments are registered in the
+ user's Git config. This usually does not represent any problem because it is
+ rare for a user to register an enlistment. However, in Scalar's functional
+ tests, Scalar enlistments are created galore, and in parallel, which can lead
+ to lock contention. This patch series works around that problem by re-trying
+ to lock the config file in a gentle fashion.
+
+- `scalar-extra-docs`: Add some extensive documentation that has been written
+ in the original Scalar project (all subject to discussion, of course).
+
+- `optionally-install-scalar`: Now that Scalar is feature (and documentation)
+ complete and is verified in CI builds, let's offer to install it.
+
+- `move-scalar-to-toplevel`: Now that Scalar is complete, let's move it next to
+ `gitk-git/` and to `git-gui/`, making it a top-level command.
+
+The following two patch series exist in Microsoft's fork of Git and are
+publicly available. There is no current plan to upstream them, not because I
+want to withhold these patches, but because I don't think the Git community is
+interested in these patches.
+
+There are some interesting ideas there, but the implementation is too specific
+to Azure Repos and/or VFS for Git to be of much help in general (and also: my
+colleagues tried to upstream some patches already and the enthusiasm for
+integrating things related to Azure Repos and VFS for Git can be summarized in
+very, very few words).
+
+These still exist mainly because the GVFS protocol is what Azure Repos has
+instead of partial clone, while Git is focused on improving partial clone:
+
+- `scalar-with-gvfs`: The primary purpose of this patch series is to support
+ existing Scalar users whose repositories are hosted in Azure Repos (which
+ does not support Git's partial clones, but supports its predecessor, the GVFS
+ protocol, which is used by Scalar to emulate the partial clone).
+
+ Since the GVFS protocol will never be supported by core Git, this patch
+ series will remain in Microsoft's fork of Git.
+
+- `run-scalar-functional-tests`: The Scalar project developed a quite
+ comprehensive set of integration tests (or, "Functional Tests"). They are the
+ sole remaining part of the original C#-based Scalar project, and this patch
+ adds a GitHub workflow that runs them all.
+
+ Since the tests partially depend on features that are only provided in the
+ `scalar-with-gvfs` patch series, this patch cannot be upstreamed.
diff --git a/contrib/scalar/scalar.c b/contrib/scalar/scalar.c
new file mode 100644
index 0000000..1ce9c2b
--- /dev/null
+++ b/contrib/scalar/scalar.c
@@ -0,0 +1,826 @@
+/*
+ * The Scalar command-line interface.
+ */
+
+#include "cache.h"
+#include "gettext.h"
+#include "parse-options.h"
+#include "config.h"
+#include "run-command.h"
+#include "refs.h"
+#include "dir.h"
+#include "packfile.h"
+#include "help.h"
+
+/*
+ * Remove the deepest subdirectory in the provided path string. Path must not
+ * include a trailing path separator. Returns 1 if parent directory found,
+ * otherwise 0.
+ */
+static int strbuf_parent_directory(struct strbuf *buf)
+{
+ size_t len = buf->len;
+ size_t offset = offset_1st_component(buf->buf);
+ char *path_sep = find_last_dir_sep(buf->buf + offset);
+ strbuf_setlen(buf, path_sep ? path_sep - buf->buf : offset);
+
+ return buf->len < len;
+}
+
+static void setup_enlistment_directory(int argc, const char **argv,
+ const char * const *usagestr,
+ const struct option *options,
+ struct strbuf *enlistment_root)
+{
+ struct strbuf path = STRBUF_INIT;
+ char *root;
+ int enlistment_found = 0;
+
+ if (startup_info->have_repository)
+ BUG("gitdir already set up?!?");
+
+ if (argc > 1)
+ usage_with_options(usagestr, options);
+
+ /* find the worktree, determine its corresponding root */
+ if (argc == 1)
+ strbuf_add_absolute_path(&path, argv[0]);
+ else if (strbuf_getcwd(&path) < 0)
+ die(_("need a working directory"));
+
+ strbuf_trim_trailing_dir_sep(&path);
+ do {
+ const size_t len = path.len;
+
+ /* check if currently in enlistment root with src/ workdir */
+ strbuf_addstr(&path, "/src");
+ if (is_nonbare_repository_dir(&path)) {
+ if (enlistment_root)
+ strbuf_add(enlistment_root, path.buf, len);
+
+ enlistment_found = 1;
+ break;
+ }
+
+ /* reset to original path */
+ strbuf_setlen(&path, len);
+
+ /* check if currently in workdir */
+ if (is_nonbare_repository_dir(&path)) {
+ if (enlistment_root) {
+ /*
+ * If the worktree's directory's name is `src`, the enlistment is the
+ * parent directory, otherwise it is identical to the worktree.
+ */
+ root = strip_path_suffix(path.buf, "src");
+ strbuf_addstr(enlistment_root, root ? root : path.buf);
+ free(root);
+ }
+
+ enlistment_found = 1;
+ break;
+ }
+ } while (strbuf_parent_directory(&path));
+
+ if (!enlistment_found)
+ die(_("could not find enlistment root"));
+
+ if (chdir(path.buf) < 0)
+ die_errno(_("could not switch to '%s'"), path.buf);
+
+ strbuf_release(&path);
+ setup_git_directory();
+}
+
+static int run_git(const char *arg, ...)
+{
+ struct strvec argv = STRVEC_INIT;
+ va_list args;
+ const char *p;
+ int res;
+
+ va_start(args, arg);
+ strvec_push(&argv, arg);
+ while ((p = va_arg(args, const char *)))
+ strvec_push(&argv, p);
+ va_end(args);
+
+ res = run_command_v_opt(argv.v, RUN_GIT_CMD);
+
+ strvec_clear(&argv);
+ return res;
+}
+
+static int set_recommended_config(int reconfigure)
+{
+ struct {
+ const char *key;
+ const char *value;
+ int overwrite_on_reconfigure;
+ } config[] = {
+ /* Required */
+ { "am.keepCR", "true", 1 },
+ { "core.FSCache", "true", 1 },
+ { "core.multiPackIndex", "true", 1 },
+ { "core.preloadIndex", "true", 1 },
+#ifndef WIN32
+ { "core.untrackedCache", "true", 1 },
+#else
+ /*
+ * Unfortunately, Scalar's Functional Tests demonstrated
+ * that the untracked cache feature is unreliable on Windows
+ * (which is a bummer because that platform would benefit the
+ * most from it). For some reason, freshly created files seem
+ * not to update the directory's `lastModified` time
+ * immediately, but the untracked cache would need to rely on
+ * that.
+ *
+ * Therefore, with a sad heart, we disable this very useful
+ * feature on Windows.
+ */
+ { "core.untrackedCache", "false", 1 },
+#endif
+ { "core.logAllRefUpdates", "true", 1 },
+ { "credential.https://dev.azure.com.useHttpPath", "true", 1 },
+ { "credential.validate", "false", 1 }, /* GCM4W-only */
+ { "gc.auto", "0", 1 },
+ { "gui.GCWarning", "false", 1 },
+ { "index.threads", "true", 1 },
+ { "index.version", "4", 1 },
+ { "merge.stat", "false", 1 },
+ { "merge.renames", "true", 1 },
+ { "pack.useBitmaps", "false", 1 },
+ { "pack.useSparse", "true", 1 },
+ { "receive.autoGC", "false", 1 },
+ { "reset.quiet", "true", 1 },
+ { "feature.manyFiles", "false", 1 },
+ { "feature.experimental", "false", 1 },
+ { "fetch.unpackLimit", "1", 1 },
+ { "fetch.writeCommitGraph", "false", 1 },
+#ifdef WIN32
+ { "http.sslBackend", "schannel", 1 },
+#endif
+ /* Optional */
+ { "status.aheadBehind", "false" },
+ { "commitGraph.generationVersion", "1" },
+ { "core.autoCRLF", "false" },
+ { "core.safeCRLF", "false" },
+ { "fetch.showForcedUpdates", "false" },
+ { NULL, NULL },
+ };
+ int i;
+ char *value;
+
+ for (i = 0; config[i].key; i++) {
+ if ((reconfigure && config[i].overwrite_on_reconfigure) ||
+ git_config_get_string(config[i].key, &value)) {
+ trace2_data_string("scalar", the_repository, config[i].key, "created");
+ if (git_config_set_gently(config[i].key,
+ config[i].value) < 0)
+ return error(_("could not configure %s=%s"),
+ config[i].key, config[i].value);
+ } else {
+ trace2_data_string("scalar", the_repository, config[i].key, "exists");
+ free(value);
+ }
+ }
+
+ /*
+ * The `log.excludeDecoration` setting is special because it allows
+ * for multiple values.
+ */
+ if (git_config_get_string("log.excludeDecoration", &value)) {
+ trace2_data_string("scalar", the_repository,
+ "log.excludeDecoration", "created");
+ if (git_config_set_multivar_gently("log.excludeDecoration",
+ "refs/prefetch/*",
+ CONFIG_REGEX_NONE, 0))
+ return error(_("could not configure "
+ "log.excludeDecoration"));
+ } else {
+ trace2_data_string("scalar", the_repository,
+ "log.excludeDecoration", "exists");
+ free(value);
+ }
+
+ return 0;
+}
+
+static int toggle_maintenance(int enable)
+{
+ return run_git("maintenance", enable ? "start" : "unregister", NULL);
+}
+
+static int add_or_remove_enlistment(int add)
+{
+ int res;
+
+ if (!the_repository->worktree)
+ die(_("Scalar enlistments require a worktree"));
+
+ res = run_git("config", "--global", "--get", "--fixed-value",
+ "scalar.repo", the_repository->worktree, NULL);
+
+ /*
+ * If we want to add and the setting is already there, then do nothing.
+ * If we want to remove and the setting is not there, then do nothing.
+ */
+ if ((add && !res) || (!add && res))
+ return 0;
+
+ return run_git("config", "--global", add ? "--add" : "--unset",
+ add ? "--no-fixed-value" : "--fixed-value",
+ "scalar.repo", the_repository->worktree, NULL);
+}
+
+static int register_dir(void)
+{
+ int res = add_or_remove_enlistment(1);
+
+ if (!res)
+ res = set_recommended_config(0);
+
+ if (!res)
+ res = toggle_maintenance(1);
+
+ return res;
+}
+
+static int unregister_dir(void)
+{
+ int res = 0;
+
+ if (toggle_maintenance(0) < 0)
+ res = -1;
+
+ if (add_or_remove_enlistment(0) < 0)
+ res = -1;
+
+ return res;
+}
+
+/* printf-style interface, expects `<key>=<value>` argument */
+static int set_config(const char *fmt, ...)
+{
+ struct strbuf buf = STRBUF_INIT;
+ char *value;
+ int res;
+ va_list args;
+
+ va_start(args, fmt);
+ strbuf_vaddf(&buf, fmt, args);
+ va_end(args);
+
+ value = strchr(buf.buf, '=');
+ if (value)
+ *(value++) = '\0';
+ res = git_config_set_gently(buf.buf, value);
+ strbuf_release(&buf);
+
+ return res;
+}
+
+static char *remote_default_branch(const char *url)
+{
+ struct child_process cp = CHILD_PROCESS_INIT;
+ struct strbuf out = STRBUF_INIT;
+
+ cp.git_cmd = 1;
+ strvec_pushl(&cp.args, "ls-remote", "--symref", url, "HEAD", NULL);
+ if (!pipe_command(&cp, NULL, 0, &out, 0, NULL, 0)) {
+ const char *line = out.buf;
+
+ while (*line) {
+ const char *eol = strchrnul(line, '\n'), *p;
+ size_t len = eol - line;
+ char *branch;
+
+ if (!skip_prefix(line, "ref: ", &p) ||
+ !strip_suffix_mem(line, &len, "\tHEAD")) {
+ line = eol + (*eol == '\n');
+ continue;
+ }
+
+ eol = line + len;
+ if (skip_prefix(p, "refs/heads/", &p)) {
+ branch = xstrndup(p, eol - p);
+ strbuf_release(&out);
+ return branch;
+ }
+
+ error(_("remote HEAD is not a branch: '%.*s'"),
+ (int)(eol - p), p);
+ strbuf_release(&out);
+ return NULL;
+ }
+ }
+ warning(_("failed to get default branch name from remote; "
+ "using local default"));
+ strbuf_reset(&out);
+
+ child_process_init(&cp);
+ cp.git_cmd = 1;
+ strvec_pushl(&cp.args, "symbolic-ref", "--short", "HEAD", NULL);
+ if (!pipe_command(&cp, NULL, 0, &out, 0, NULL, 0)) {
+ strbuf_trim(&out);
+ return strbuf_detach(&out, NULL);
+ }
+
+ strbuf_release(&out);
+ error(_("failed to get default branch name"));
+ return NULL;
+}
+
+static int delete_enlistment(struct strbuf *enlistment)
+{
+#ifdef WIN32
+ struct strbuf parent = STRBUF_INIT;
+#endif
+
+ if (unregister_dir())
+ die(_("failed to unregister repository"));
+
+#ifdef WIN32
+ /*
+ * Change the current directory to one outside of the enlistment so
+ * that we may delete everything underneath it.
+ */
+ strbuf_addbuf(&parent, enlistment);
+ strbuf_parent_directory(&parent);
+ if (chdir(parent.buf) < 0)
+ die_errno(_("could not switch to '%s'"), parent.buf);
+ strbuf_release(&parent);
+#endif
+
+ if (remove_dir_recursively(enlistment, 0))
+ die(_("failed to delete enlistment directory"));
+
+ return 0;
+}
+
+/*
+ * Dummy implementation; Using `get_version_info()` would cause a link error
+ * without this.
+ */
+void load_builtin_commands(const char *prefix, struct cmdnames *cmds)
+{
+ die("not implemented");
+}
+
+static int cmd_clone(int argc, const char **argv)
+{
+ const char *branch = NULL;
+ int full_clone = 0, single_branch = 0;
+ struct option clone_options[] = {
+ OPT_STRING('b', "branch", &branch, N_("<branch>"),
+ N_("branch to checkout after clone")),
+ OPT_BOOL(0, "full-clone", &full_clone,
+ N_("when cloning, create full working directory")),
+ OPT_BOOL(0, "single-branch", &single_branch,
+ N_("only download metadata for the branch that will "
+ "be checked out")),
+ OPT_END(),
+ };
+ const char * const clone_usage[] = {
+ N_("scalar clone [<options>] [--] <repo> [<dir>]"),
+ NULL
+ };
+ const char *url;
+ char *enlistment = NULL, *dir = NULL;
+ struct strbuf buf = STRBUF_INIT;
+ int res;
+
+ argc = parse_options(argc, argv, NULL, clone_options, clone_usage, 0);
+
+ if (argc == 2) {
+ url = argv[0];
+ enlistment = xstrdup(argv[1]);
+ } else if (argc == 1) {
+ url = argv[0];
+
+ strbuf_addstr(&buf, url);
+ /* Strip trailing slashes, if any */
+ while (buf.len > 0 && is_dir_sep(buf.buf[buf.len - 1]))
+ strbuf_setlen(&buf, buf.len - 1);
+ /* Strip suffix `.git`, if any */
+ strbuf_strip_suffix(&buf, ".git");
+
+ enlistment = find_last_dir_sep(buf.buf);
+ if (!enlistment) {
+ die(_("cannot deduce worktree name from '%s'"), url);
+ }
+ enlistment = xstrdup(enlistment + 1);
+ } else {
+ usage_msg_opt(_("You must specify a repository to clone."),
+ clone_usage, clone_options);
+ }
+
+ if (is_directory(enlistment))
+ die(_("directory '%s' exists already"), enlistment);
+
+ dir = xstrfmt("%s/src", enlistment);
+
+ strbuf_reset(&buf);
+ if (branch)
+ strbuf_addf(&buf, "init.defaultBranch=%s", branch);
+ else {
+ char *b = repo_default_branch_name(the_repository, 1);
+ strbuf_addf(&buf, "init.defaultBranch=%s", b);
+ free(b);
+ }
+
+ if ((res = run_git("-c", buf.buf, "init", "--", dir, NULL)))
+ goto cleanup;
+
+ if (chdir(dir) < 0) {
+ res = error_errno(_("could not switch to '%s'"), dir);
+ goto cleanup;
+ }
+
+ setup_git_directory();
+
+ /* common-main already logs `argv` */
+ trace2_def_repo(the_repository);
+
+ if (!branch && !(branch = remote_default_branch(url))) {
+ res = error(_("failed to get default branch for '%s'"), url);
+ goto cleanup;
+ }
+
+ if (set_config("remote.origin.url=%s", url) ||
+ set_config("remote.origin.fetch="
+ "+refs/heads/%s:refs/remotes/origin/%s",
+ single_branch ? branch : "*",
+ single_branch ? branch : "*") ||
+ set_config("remote.origin.promisor=true") ||
+ set_config("remote.origin.partialCloneFilter=blob:none")) {
+ res = error(_("could not configure remote in '%s'"), dir);
+ goto cleanup;
+ }
+
+ if (!full_clone &&
+ (res = run_git("sparse-checkout", "init", "--cone", NULL)))
+ goto cleanup;
+
+ if (set_recommended_config(0))
+ return error(_("could not configure '%s'"), dir);
+
+ if ((res = run_git("fetch", "--quiet", "origin", NULL))) {
+ warning(_("partial clone failed; attempting full clone"));
+
+ if (set_config("remote.origin.promisor") ||
+ set_config("remote.origin.partialCloneFilter")) {
+ res = error(_("could not configure for full clone"));
+ goto cleanup;
+ }
+
+ if ((res = run_git("fetch", "--quiet", "origin", NULL)))
+ goto cleanup;
+ }
+
+ if ((res = set_config("branch.%s.remote=origin", branch)))
+ goto cleanup;
+ if ((res = set_config("branch.%s.merge=refs/heads/%s",
+ branch, branch)))
+ goto cleanup;
+
+ strbuf_reset(&buf);
+ strbuf_addf(&buf, "origin/%s", branch);
+ res = run_git("checkout", "-f", "-t", buf.buf, NULL);
+ if (res)
+ goto cleanup;
+
+ res = register_dir();
+
+cleanup:
+ free(enlistment);
+ free(dir);
+ strbuf_release(&buf);
+ return res;
+}
+
+static int cmd_list(int argc, const char **argv)
+{
+ if (argc != 1)
+ die(_("`scalar list` does not take arguments"));
+
+ if (run_git("config", "--global", "--get-all", "scalar.repo", NULL) < 0)
+ return -1;
+ return 0;
+}
+
+static int cmd_register(int argc, const char **argv)
+{
+ struct option options[] = {
+ OPT_END(),
+ };
+ const char * const usage[] = {
+ N_("scalar register [<enlistment>]"),
+ NULL
+ };
+
+ argc = parse_options(argc, argv, NULL, options,
+ usage, 0);
+
+ setup_enlistment_directory(argc, argv, usage, options, NULL);
+
+ return register_dir();
+}
+
+static int get_scalar_repos(const char *key, const char *value, void *data)
+{
+ struct string_list *list = data;
+
+ if (!strcmp(key, "scalar.repo"))
+ string_list_append(list, value);
+
+ return 0;
+}
+
+static int cmd_reconfigure(int argc, const char **argv)
+{
+ int all = 0;
+ struct option options[] = {
+ OPT_BOOL('a', "all", &all,
+ N_("reconfigure all registered enlistments")),
+ OPT_END(),
+ };
+ const char * const usage[] = {
+ N_("scalar reconfigure [--all | <enlistment>]"),
+ NULL
+ };
+ struct string_list scalar_repos = STRING_LIST_INIT_DUP;
+ int i, res = 0;
+ struct repository r = { NULL };
+ struct strbuf commondir = STRBUF_INIT, gitdir = STRBUF_INIT;
+
+ argc = parse_options(argc, argv, NULL, options,
+ usage, 0);
+
+ if (!all) {
+ setup_enlistment_directory(argc, argv, usage, options, NULL);
+
+ return set_recommended_config(1);
+ }
+
+ if (argc > 0)
+ usage_msg_opt(_("--all or <enlistment>, but not both"),
+ usage, options);
+
+ git_config(get_scalar_repos, &scalar_repos);
+
+ for (i = 0; i < scalar_repos.nr; i++) {
+ const char *dir = scalar_repos.items[i].string;
+
+ strbuf_reset(&commondir);
+ strbuf_reset(&gitdir);
+
+ if (chdir(dir) < 0) {
+ warning_errno(_("could not switch to '%s'"), dir);
+ res = -1;
+ } else if (discover_git_directory(&commondir, &gitdir) < 0) {
+ warning_errno(_("git repository gone in '%s'"), dir);
+ res = -1;
+ } else {
+ git_config_clear();
+
+ the_repository = &r;
+ r.commondir = commondir.buf;
+ r.gitdir = gitdir.buf;
+
+ if (set_recommended_config(1) < 0)
+ res = -1;
+ }
+ }
+
+ string_list_clear(&scalar_repos, 1);
+ strbuf_release(&commondir);
+ strbuf_release(&gitdir);
+
+ return res;
+}
+
+static int cmd_run(int argc, const char **argv)
+{
+ struct option options[] = {
+ OPT_END(),
+ };
+ struct {
+ const char *arg, *task;
+ } tasks[] = {
+ { "config", NULL },
+ { "commit-graph", "commit-graph" },
+ { "fetch", "prefetch" },
+ { "loose-objects", "loose-objects" },
+ { "pack-files", "incremental-repack" },
+ { NULL, NULL }
+ };
+ struct strbuf buf = STRBUF_INIT;
+ const char *usagestr[] = { NULL, NULL };
+ int i;
+
+ strbuf_addstr(&buf, N_("scalar run <task> [<enlistment>]\nTasks:\n"));
+ for (i = 0; tasks[i].arg; i++)
+ strbuf_addf(&buf, "\t%s\n", tasks[i].arg);
+ usagestr[0] = buf.buf;
+
+ argc = parse_options(argc, argv, NULL, options,
+ usagestr, 0);
+
+ if (!argc)
+ usage_with_options(usagestr, options);
+
+ if (!strcmp("all", argv[0])) {
+ i = -1;
+ } else {
+ for (i = 0; tasks[i].arg && strcmp(tasks[i].arg, argv[0]); i++)
+ ; /* keep looking for the task */
+
+ if (i > 0 && !tasks[i].arg) {
+ error(_("no such task: '%s'"), argv[0]);
+ usage_with_options(usagestr, options);
+ }
+ }
+
+ argc--;
+ argv++;
+ setup_enlistment_directory(argc, argv, usagestr, options, NULL);
+ strbuf_release(&buf);
+
+ if (i == 0)
+ return register_dir();
+
+ if (i > 0)
+ return run_git("maintenance", "run",
+ "--task", tasks[i].task, NULL);
+
+ if (register_dir())
+ return -1;
+ for (i = 1; tasks[i].arg; i++)
+ if (run_git("maintenance", "run",
+ "--task", tasks[i].task, NULL))
+ return -1;
+ return 0;
+}
+
+static int remove_deleted_enlistment(struct strbuf *path)
+{
+ int res = 0;
+ strbuf_realpath_forgiving(path, path->buf, 1);
+
+ if (run_git("config", "--global",
+ "--unset", "--fixed-value",
+ "scalar.repo", path->buf, NULL) < 0)
+ res = -1;
+
+ if (run_git("config", "--global",
+ "--unset", "--fixed-value",
+ "maintenance.repo", path->buf, NULL) < 0)
+ res = -1;
+
+ return res;
+}
+
+static int cmd_unregister(int argc, const char **argv)
+{
+ struct option options[] = {
+ OPT_END(),
+ };
+ const char * const usage[] = {
+ N_("scalar unregister [<enlistment>]"),
+ NULL
+ };
+
+ argc = parse_options(argc, argv, NULL, options,
+ usage, 0);
+
+ /*
+ * Be forgiving when the enlistment or worktree does not even exist any
+ * longer; This can be the case if a user deleted the worktree by
+ * mistake and _still_ wants to unregister the thing.
+ */
+ if (argc == 1) {
+ struct strbuf src_path = STRBUF_INIT, workdir_path = STRBUF_INIT;
+
+ strbuf_addf(&src_path, "%s/src/.git", argv[0]);
+ strbuf_addf(&workdir_path, "%s/.git", argv[0]);
+ if (!is_directory(src_path.buf) && !is_directory(workdir_path.buf)) {
+ /* remove possible matching registrations */
+ int res = -1;
+
+ strbuf_strip_suffix(&src_path, "/.git");
+ res = remove_deleted_enlistment(&src_path) && res;
+
+ strbuf_strip_suffix(&workdir_path, "/.git");
+ res = remove_deleted_enlistment(&workdir_path) && res;
+
+ strbuf_release(&src_path);
+ strbuf_release(&workdir_path);
+ return res;
+ }
+ strbuf_release(&src_path);
+ strbuf_release(&workdir_path);
+ }
+
+ setup_enlistment_directory(argc, argv, usage, options, NULL);
+
+ return unregister_dir();
+}
+
+static int cmd_delete(int argc, const char **argv)
+{
+ char *cwd = xgetcwd();
+ struct option options[] = {
+ OPT_END(),
+ };
+ const char * const usage[] = {
+ N_("scalar delete <enlistment>"),
+ NULL
+ };
+ struct strbuf enlistment = STRBUF_INIT;
+ int res = 0;
+
+ argc = parse_options(argc, argv, NULL, options,
+ usage, 0);
+
+ if (argc != 1)
+ usage_with_options(usage, options);
+
+ setup_enlistment_directory(argc, argv, usage, options, &enlistment);
+
+ if (dir_inside_of(cwd, enlistment.buf) >= 0)
+ res = error(_("refusing to delete current working directory"));
+ else {
+ close_object_store(the_repository->objects);
+ res = delete_enlistment(&enlistment);
+ }
+ strbuf_release(&enlistment);
+ free(cwd);
+
+ return res;
+}
+
+static int cmd_version(int argc, const char **argv)
+{
+ int verbose = 0, build_options = 0;
+ struct option options[] = {
+ OPT__VERBOSE(&verbose, N_("include Git version")),
+ OPT_BOOL(0, "build-options", &build_options,
+ N_("include Git's build options")),
+ OPT_END(),
+ };
+ const char * const usage[] = {
+ N_("scalar verbose [-v | --verbose] [--build-options]"),
+ NULL
+ };
+ struct strbuf buf = STRBUF_INIT;
+
+ argc = parse_options(argc, argv, NULL, options,
+ usage, 0);
+
+ if (argc != 0)
+ usage_with_options(usage, options);
+
+ get_version_info(&buf, build_options);
+ fprintf(stderr, "%s\n", buf.buf);
+ strbuf_release(&buf);
+
+ return 0;
+}
+
+static struct {
+ const char *name;
+ int (*fn)(int, const char **);
+} builtins[] = {
+ { "clone", cmd_clone },
+ { "list", cmd_list },
+ { "register", cmd_register },
+ { "unregister", cmd_unregister },
+ { "run", cmd_run },
+ { "reconfigure", cmd_reconfigure },
+ { "delete", cmd_delete },
+ { "version", cmd_version },
+ { NULL, NULL},
+};
+
+int cmd_main(int argc, const char **argv)
+{
+ struct strbuf scalar_usage = STRBUF_INIT;
+ int i;
+
+ if (argc > 1) {
+ argv++;
+ argc--;
+
+ for (i = 0; builtins[i].name; i++)
+ if (!strcmp(builtins[i].name, argv[0]))
+ return !!builtins[i].fn(argc, argv);
+ }
+
+ strbuf_addstr(&scalar_usage,
+ N_("scalar <command> [<options>]\n\nCommands:\n"));
+ for (i = 0; builtins[i].name; i++)
+ strbuf_addf(&scalar_usage, "\t%s\n", builtins[i].name);
+
+ usage(scalar_usage.buf);
+}
diff --git a/contrib/scalar/scalar.txt b/contrib/scalar/scalar.txt
new file mode 100644
index 0000000..f416d63
--- /dev/null
+++ b/contrib/scalar/scalar.txt
@@ -0,0 +1,145 @@
+scalar(1)
+=========
+
+NAME
+----
+scalar - an opinionated repository management tool
+
+SYNOPSIS
+--------
+[verse]
+scalar clone [--single-branch] [--branch <main-branch>] [--full-clone] <url> [<enlistment>]
+scalar list
+scalar register [<enlistment>]
+scalar unregister [<enlistment>]
+scalar run ( all | config | commit-graph | fetch | loose-objects | pack-files ) [<enlistment>]
+scalar reconfigure [ --all | <enlistment> ]
+scalar delete <enlistment>
+
+DESCRIPTION
+-----------
+
+Scalar is an opinionated repository management tool. By creating new
+repositories or registering existing repositories with Scalar, your Git
+experience will speed up. Scalar sets advanced Git config settings,
+maintains your repositories in the background, and helps reduce data sent
+across the network.
+
+An important Scalar concept is the enlistment: this is the top-level directory
+of the project. It usually contains the subdirectory `src/` which is a Git
+worktree. This encourages the separation between tracked files (inside `src/`)
+and untracked files, such as build artifacts (outside `src/`). When registering
+an existing Git worktree with Scalar whose name is not `src`, the enlistment
+will be identical to the worktree.
+
+The `scalar` command implements various subcommands, and different options
+depending on the subcommand. With the exception of `clone`, `list` and
+`reconfigure --all`, all subcommands expect to be run in an enlistment.
+
+COMMANDS
+--------
+
+Clone
+~~~~~
+
+clone [<options>] <url> [<enlistment>]::
+ Clones the specified repository, similar to linkgit:git-clone[1]. By
+ default, only commit and tree objects are cloned. Once finished, the
+ worktree is located at `<enlistment>/src`.
++
+The sparse-checkout feature is enabled (except when run with `--full-clone`)
+and the only files present are those in the top-level directory. Use
+`git sparse-checkout set` to expand the set of directories you want to see,
+or `git sparse-checkout disable` to expand to all files (see
+linkgit:git-sparse-checkout[1] for more details). You can explore the
+subdirectories outside your sparse-checkout by using `git ls-tree
+HEAD[:<directory>]`.
+
+-b <name>::
+--branch <name>::
+ Instead of checking out the branch pointed to by the cloned
+ repository's HEAD, check out the `<name>` branch instead.
+
+--[no-]single-branch::
+ Clone only the history leading to the tip of a single branch, either
+ specified by the `--branch` option or the primary branch remote's
+ `HEAD` points at.
++
+Further fetches into the resulting repository will only update the
+remote-tracking branch for the branch this option was used for the initial
+cloning. If the HEAD at the remote did not point at any branch when
+`--single-branch` clone was made, no remote-tracking branch is created.
+
+--[no-]full-clone::
+ A sparse-checkout is initialized by default. This behavior can be
+ turned off via `--full-clone`.
+
+List
+~~~~
+
+list::
+ List enlistments that are currently registered by Scalar. This
+ subcommand does not need to be run inside an enlistment.
+
+Register
+~~~~~~~~
+
+register [<enlistment>]::
+ Adds the enlistment's repository to the list of registered repositories
+ and starts background maintenance. If `<enlistment>` is not provided,
+ then the enlistment associated with the current working directory is
+ registered.
++
+Note: when this subcommand is called in a worktree that is called `src/`, its
+parent directory is considered to be the Scalar enlistment. If the worktree is
+_not_ called `src/`, it itself will be considered to be the Scalar enlistment.
+
+Unregister
+~~~~~~~~~~
+
+unregister [<enlistment>]::
+ Remove the specified repository from the list of repositories
+ registered with Scalar and stop the scheduled background maintenance.
+
+Run
+~~~
+
+scalar run ( all | config | commit-graph | fetch | loose-objects | pack-files ) [<enlistment>]::
+ Run the given maintenance task (or all tasks, if `all` was specified).
+ Except for `all` and `config`, this subcommand simply hands off to
+ linkgit:git-maintenance[1] (mapping `fetch` to `prefetch` and
+ `pack-files` to `incremental-repack`).
++
+These tasks are run automatically as part of the scheduled maintenance,
+as soon as the repository is registered with Scalar. It should therefore
+not be necessary to run this subcommand manually.
++
+The `config` task is specific to Scalar and configures all those
+opinionated default settings that make Git work more efficiently with
+large repositories. As this task is run as part of `scalar clone`
+automatically, explicit invocations of this task are rarely needed.
+
+Reconfigure
+~~~~~~~~~~~
+
+After a Scalar upgrade, or when the configuration of a Scalar enlistment
+was somehow corrupted or changed by mistake, this subcommand allows to
+reconfigure the enlistment.
+
+With the `--all` option, all enlistments currently registered with Scalar
+will be reconfigured. Use this option after each Scalar upgrade.
+
+Delete
+~~~~~~
+
+delete <enlistment>::
+ This subcommand lets you delete an existing Scalar enlistment from your
+ local file system, unregistering the repository.
+
+SEE ALSO
+--------
+linkgit:git-clone[1], linkgit:git-maintenance[1].
+
+Scalar
+---
+Associated with the linkgit:git[1] suite
diff --git a/contrib/scalar/t/Makefile b/contrib/scalar/t/Makefile
new file mode 100644
index 0000000..6170672
--- /dev/null
+++ b/contrib/scalar/t/Makefile
@@ -0,0 +1,78 @@
+# Run scalar tests
+#
+# Copyright (c) 2005,2021 Junio C Hamano, Johannes Schindelin
+#
+
+-include ../../../config.mak.autogen
+-include ../../../config.mak
+
+SHELL_PATH ?= $(SHELL)
+PERL_PATH ?= /usr/bin/perl
+RM ?= rm -f
+PROVE ?= prove
+DEFAULT_TEST_TARGET ?= test
+TEST_LINT ?= test-lint
+
+ifdef TEST_OUTPUT_DIRECTORY
+TEST_RESULTS_DIRECTORY = $(TEST_OUTPUT_DIRECTORY)/test-results
+else
+TEST_RESULTS_DIRECTORY = ../../../t/test-results
+endif
+
+# Shell quote;
+SHELL_PATH_SQ = $(subst ','\'',$(SHELL_PATH))
+PERL_PATH_SQ = $(subst ','\'',$(PERL_PATH))
+TEST_RESULTS_DIRECTORY_SQ = $(subst ','\'',$(TEST_RESULTS_DIRECTORY))
+
+T = $(sort $(wildcard t[0-9][0-9][0-9][0-9]-*.sh))
+
+all: $(DEFAULT_TEST_TARGET)
+
+test: $(TEST_LINT)
+ $(MAKE) aggregate-results-and-cleanup
+
+prove: $(TEST_LINT)
+ @echo "*** prove ***"; GIT_CONFIG=.git/config $(PROVE) --exec '$(SHELL_PATH_SQ)' $(GIT_PROVE_OPTS) $(T) :: $(GIT_TEST_OPTS)
+ $(MAKE) clean-except-prove-cache
+
+$(T):
+ @echo "*** $@ ***"; GIT_CONFIG=.git/config '$(SHELL_PATH_SQ)' $@ $(GIT_TEST_OPTS)
+
+clean-except-prove-cache:
+ $(RM) -r 'trash directory'.* '$(TEST_RESULTS_DIRECTORY_SQ)'
+ $(RM) -r valgrind/bin
+
+clean: clean-except-prove-cache
+ $(RM) .prove
+
+test-lint: test-lint-duplicates test-lint-executable test-lint-shell-syntax
+
+test-lint-duplicates:
+ @dups=`echo $(T) | tr ' ' '\n' | sed 's/-.*//' | sort | uniq -d` && \
+ test -z "$$dups" || { \
+ echo >&2 "duplicate test numbers:" $$dups; exit 1; }
+
+test-lint-executable:
+ @bad=`for i in $(T); do test -x "$$i" || echo $$i; done` && \
+ test -z "$$bad" || { \
+ echo >&2 "non-executable tests:" $$bad; exit 1; }
+
+test-lint-shell-syntax:
+ @'$(PERL_PATH_SQ)' ../../../t/check-non-portable-shell.pl $(T)
+
+aggregate-results-and-cleanup: $(T)
+ $(MAKE) aggregate-results
+ $(MAKE) clean
+
+aggregate-results:
+ for f in '$(TEST_RESULTS_DIRECTORY_SQ)'/t*-*.counts; do \
+ echo "$$f"; \
+ done | '$(SHELL_PATH_SQ)' ../../../t/aggregate-results.sh
+
+valgrind:
+ $(MAKE) GIT_TEST_OPTS="$(GIT_TEST_OPTS) --valgrind"
+
+test-results:
+ mkdir -p test-results
+
+.PHONY: $(T) aggregate-results clean valgrind
diff --git a/contrib/scalar/t/t9099-scalar.sh b/contrib/scalar/t/t9099-scalar.sh
new file mode 100755
index 0000000..2e1502a
--- /dev/null
+++ b/contrib/scalar/t/t9099-scalar.sh
@@ -0,0 +1,88 @@
+#!/bin/sh
+
+test_description='test the `scalar` command'
+
+TEST_DIRECTORY=$PWD/../../../t
+export TEST_DIRECTORY
+
+# Make it work with --no-bin-wrappers
+PATH=$PWD/..:$PATH
+
+. ../../../t/test-lib.sh
+
+GIT_TEST_MAINT_SCHEDULER="crontab:test-tool crontab ../cron.txt,launchctl:true,schtasks:true"
+export GIT_TEST_MAINT_SCHEDULER
+
+test_expect_success 'scalar shows a usage' '
+ test_expect_code 129 scalar -h
+'
+
+test_expect_success 'scalar unregister' '
+ git init vanish/src &&
+ scalar register vanish/src &&
+ git config --get --global --fixed-value \
+ maintenance.repo "$(pwd)/vanish/src" &&
+ scalar list >scalar.repos &&
+ grep -F "$(pwd)/vanish/src" scalar.repos &&
+ rm -rf vanish/src/.git &&
+ scalar unregister vanish &&
+ test_must_fail git config --get --global --fixed-value \
+ maintenance.repo "$(pwd)/vanish/src" &&
+ scalar list >scalar.repos &&
+ ! grep -F "$(pwd)/vanish/src" scalar.repos
+'
+
+test_expect_success 'set up repository to clone' '
+ test_commit first &&
+ test_commit second &&
+ test_commit third &&
+ git switch -c parallel first &&
+ mkdir -p 1/2 &&
+ test_commit 1/2/3 &&
+ git config uploadPack.allowFilter true &&
+ git config uploadPack.allowAnySHA1InWant true
+'
+
+test_expect_success 'scalar clone' '
+ second=$(git rev-parse --verify second:second.t) &&
+ scalar clone "file://$(pwd)" cloned --single-branch &&
+ (
+ cd cloned/src &&
+
+ git config --get --global --fixed-value maintenance.repo \
+ "$(pwd)" &&
+
+ git for-each-ref --format="%(refname)" refs/remotes/origin/ >actual &&
+ echo "refs/remotes/origin/parallel" >expect &&
+ test_cmp expect actual &&
+
+ test_path_is_missing 1/2 &&
+ test_must_fail git rev-list --missing=print $second &&
+ git rev-list $second &&
+ git cat-file blob $second >actual &&
+ echo "second" >expect &&
+ test_cmp expect actual
+ )
+'
+
+test_expect_success 'scalar reconfigure' '
+ git init one/src &&
+ scalar register one &&
+ git -C one/src config core.preloadIndex false &&
+ scalar reconfigure one &&
+ test true = "$(git -C one/src config core.preloadIndex)" &&
+ git -C one/src config core.preloadIndex false &&
+ scalar reconfigure -a &&
+ test true = "$(git -C one/src config core.preloadIndex)"
+'
+
+test_expect_success 'scalar delete without enlistment shows a usage' '
+ test_expect_code 129 scalar delete
+'
+
+test_expect_success 'scalar delete with enlistment' '
+ scalar delete cloned &&
+ test_path_is_missing cloned
+'
+
+test_done
diff --git a/contrib/subtree/git-subtree.sh b/contrib/subtree/git-subtree.sh
index 7f767b5..71f1fd9 100755
--- a/contrib/subtree/git-subtree.sh
+++ b/contrib/subtree/git-subtree.sh
@@ -296,10 +296,9 @@
done
}
-# Usage: check_parents PARENTS_EXPR
+# Usage: check_parents [REVS...]
check_parents () {
- assert test $# = 1
- missed=$(cache_miss "$1") || exit $?
+ missed=$(cache_miss "$@") || exit $?
local indent=$(($indent + 1))
for miss in $missed
do
@@ -753,7 +752,7 @@
fi
createcount=$(($createcount + 1))
debug "parents: $parents"
- check_parents "$parents"
+ check_parents $parents
newparents=$(cache_get $parents) || exit $?
debug "newparents: $newparents"
diff --git a/contrib/subtree/t/t7900-subtree.sh b/contrib/subtree/t/t7900-subtree.sh
index 4153b65..1c1f76f 100755
--- a/contrib/subtree/t/t7900-subtree.sh
+++ b/contrib/subtree/t/t7900-subtree.sh
@@ -1445,7 +1445,7 @@
) &&
test_create_commit "$test_count" folder_subtree/0 &&
test_create_commit "$test_count" folder_subtree/b &&
- cherry=$(cd "$test_count"; git rev-parse HEAD) &&
+ cherry=$(cd "$test_count" && git rev-parse HEAD) &&
(
cd "$test_count" &&
git checkout branch
diff --git a/convert.c b/convert.c
index 0d6fb34..df7186b 100644
--- a/convert.c
+++ b/convert.c
@@ -613,7 +613,7 @@
struct filter_params {
const char *src;
- unsigned long size;
+ size_t size;
int fd;
const char *cmd;
const char *path;
diff --git a/daemon.c b/daemon.c
index b1fcbe0..94a5b8a 100644
--- a/daemon.c
+++ b/daemon.c
@@ -232,13 +232,13 @@
rlen = strlcpy(interp_path, expanded_path.buf,
sizeof(interp_path));
+ strbuf_release(&expanded_path);
if (rlen >= sizeof(interp_path)) {
logerror("interpolated path too large: %s",
interp_path);
return NULL;
}
- strbuf_release(&expanded_path);
loginfo("Interpolated dir '%s'", interp_path);
dir = interp_path;
@@ -326,22 +326,18 @@
{
struct child_process child = CHILD_PROCESS_INIT;
struct strbuf buf = STRBUF_INIT;
- const char *argv[8];
- const char **arg = argv;
char *eol;
int seen_errors = 0;
- *arg++ = access_hook;
- *arg++ = service->name;
- *arg++ = path;
- *arg++ = hi->hostname.buf;
- *arg++ = get_canon_hostname(hi);
- *arg++ = get_ip_address(hi);
- *arg++ = hi->tcp_port.buf;
- *arg = NULL;
+ strvec_push(&child.args, access_hook);
+ strvec_push(&child.args, service->name);
+ strvec_push(&child.args, path);
+ strvec_push(&child.args, hi->hostname.buf);
+ strvec_push(&child.args, get_canon_hostname(hi));
+ strvec_push(&child.args, get_ip_address(hi));
+ strvec_push(&child.args, hi->tcp_port.buf);
child.use_shell = 1;
- child.argv = argv;
child.no_stdin = 1;
child.no_stderr = 1;
child.out = -1;
@@ -922,7 +918,7 @@
#endif
}
- cld.argv = cld_argv.v;
+ strvec_pushv(&cld.args, cld_argv.v);
cld.in = incoming;
cld.out = dup(incoming);
diff --git a/date.c b/date.c
index c55ea47..84bb445 100644
--- a/date.c
+++ b/date.c
@@ -9,7 +9,7 @@
/*
* This is like mktime, but without normalization of tm_wday and tm_yday.
*/
-static time_t tm_to_time_t(const struct tm *tm)
+time_t tm_to_time_t(const struct tm *tm)
{
static const int mdays[] = {
0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334
diff --git a/delta.h b/delta.h
index 2df5fe1..8a56ec0 100644
--- a/delta.h
+++ b/delta.h
@@ -90,15 +90,15 @@
const unsigned char *top)
{
const unsigned char *data = *datap;
- unsigned long cmd, size = 0;
+ size_t cmd, size = 0;
int i = 0;
do {
cmd = *data++;
- size |= (cmd & 0x7f) << i;
+ size |= st_left_shift(cmd & 0x7f, i);
i += 7;
} while (cmd & 0x80 && data < top);
*datap = data;
- return size;
+ return cast_size_t_to_ulong(size);
}
#endif
diff --git a/diff.c b/diff.c
index 861282d..c862771 100644
--- a/diff.c
+++ b/diff.c
@@ -18,6 +18,7 @@
#include "submodule-config.h"
#include "submodule.h"
#include "hashmap.h"
+#include "mem-pool.h"
#include "ll-merge.h"
#include "string-list.h"
#include "strvec.h"
@@ -773,6 +774,7 @@
int flags;
int indent_off; /* Offset to first non-whitespace character */
int indent_width; /* The visual width of the indentation */
+ unsigned id;
enum diff_symbol s;
};
#define EMITTED_DIFF_SYMBOL_INIT { 0 }
@@ -798,9 +800,9 @@
}
struct moved_entry {
- struct hashmap_entry ent;
const struct emitted_diff_symbol *es;
struct moved_entry *next_line;
+ struct moved_entry *next_match;
};
struct moved_block {
@@ -808,11 +810,6 @@
int wsd; /* The whitespace delta of this block */
};
-static void moved_block_clear(struct moved_block *b)
-{
- memset(b, 0, sizeof(*b));
-}
-
#define INDENT_BLANKLINE INT_MIN
static void fill_es_indent_data(struct emitted_diff_symbol *es)
@@ -856,79 +853,41 @@
}
static int compute_ws_delta(const struct emitted_diff_symbol *a,
- const struct emitted_diff_symbol *b,
- int *out)
+ const struct emitted_diff_symbol *b)
{
- int a_len = a->len,
- b_len = b->len,
- a_off = a->indent_off,
- a_width = a->indent_width,
- b_off = b->indent_off,
+ int a_width = a->indent_width,
b_width = b->indent_width;
- int delta;
- if (a_width == INDENT_BLANKLINE && b_width == INDENT_BLANKLINE) {
- *out = INDENT_BLANKLINE;
- return 1;
- }
+ if (a_width == INDENT_BLANKLINE && b_width == INDENT_BLANKLINE)
+ return INDENT_BLANKLINE;
- if (a->s == DIFF_SYMBOL_PLUS)
- delta = a_width - b_width;
- else
- delta = b_width - a_width;
-
- if (a_len - a_off != b_len - b_off ||
- memcmp(a->line + a_off, b->line + b_off, a_len - a_off))
- return 0;
-
- *out = delta;
-
- return 1;
+ return a_width - b_width;
}
-static int cmp_in_block_with_wsd(const struct diff_options *o,
- const struct moved_entry *cur,
- const struct moved_entry *match,
- struct moved_block *pmb,
- int n)
+static int cmp_in_block_with_wsd(const struct moved_entry *cur,
+ const struct emitted_diff_symbol *l,
+ struct moved_block *pmb)
{
- struct emitted_diff_symbol *l = &o->emitted_symbols->buf[n];
- int al = cur->es->len, bl = match->es->len, cl = l->len;
- const char *a = cur->es->line,
- *b = match->es->line,
- *c = l->line;
- int a_off = cur->es->indent_off,
- a_width = cur->es->indent_width,
- c_off = l->indent_off,
- c_width = l->indent_width;
+ int a_width = cur->es->indent_width, b_width = l->indent_width;
int delta;
- /*
- * We need to check if 'cur' is equal to 'match'. As those
- * are from the same (+/-) side, we do not need to adjust for
- * indent changes. However these were found using fuzzy
- * matching so we do have to check if they are equal. Here we
- * just check the lengths. We delay calling memcmp() to check
- * the contents until later as if the length comparison for a
- * and c fails we can avoid the call all together.
- */
- if (al != bl)
+ /* The text of each line must match */
+ if (cur->es->id != l->id)
return 1;
- /* If 'l' and 'cur' are both blank then they match. */
- if (a_width == INDENT_BLANKLINE && c_width == INDENT_BLANKLINE)
+ /*
+ * If 'l' and 'cur' are both blank then we don't need to check the
+ * indent. We only need to check cur as we know the strings match.
+ * */
+ if (a_width == INDENT_BLANKLINE)
return 0;
/*
* The indent changes of the block are known and stored in pmb->wsd;
* however we need to check if the indent changes of the current line
- * match those of the current block and that the text of 'l' and 'cur'
- * after the indentation match.
+ * match those of the current block.
*/
- if (cur->es->s == DIFF_SYMBOL_PLUS)
- delta = a_width - c_width;
- else
- delta = c_width - a_width;
+ delta = b_width - a_width;
/*
* If the previous lines of this block were all blank then set its
@@ -937,166 +896,165 @@
if (pmb->wsd == INDENT_BLANKLINE)
pmb->wsd = delta;
- return !(delta == pmb->wsd && al - a_off == cl - c_off &&
- !memcmp(a, b, al) && !
- memcmp(a + a_off, c + c_off, al - a_off));
+ return delta != pmb->wsd;
}
-static int moved_entry_cmp(const void *hashmap_cmp_fn_data,
- const struct hashmap_entry *eptr,
- const struct hashmap_entry *entry_or_key,
- const void *keydata)
+struct interned_diff_symbol {
+ struct hashmap_entry ent;
+ struct emitted_diff_symbol *es;
+};
+
+static int interned_diff_symbol_cmp(const void *hashmap_cmp_fn_data,
+ const struct hashmap_entry *eptr,
+ const struct hashmap_entry *entry_or_key,
+ const void *keydata)
{
const struct diff_options *diffopt = hashmap_cmp_fn_data;
- const struct moved_entry *a, *b;
+ const struct emitted_diff_symbol *a, *b;
unsigned flags = diffopt->color_moved_ws_handling
& XDF_WHITESPACE_FLAGS;
- a = container_of(eptr, const struct moved_entry, ent);
- b = container_of(entry_or_key, const struct moved_entry, ent);
+ a = container_of(eptr, const struct interned_diff_symbol, ent)->es;
+ b = container_of(entry_or_key, const struct interned_diff_symbol, ent)->es;
- if (diffopt->color_moved_ws_handling &
- COLOR_MOVED_WS_ALLOW_INDENTATION_CHANGE)
- /*
- * As there is not specific white space config given,
- * we'd need to check for a new block, so ignore all
- * white space. The setup of the white space
- * configuration for the next block is done else where
- */
- flags |= XDF_IGNORE_WHITESPACE;
-
- return !xdiff_compare_lines(a->es->line, a->es->len,
- b->es->line, b->es->len,
- flags);
+ return !xdiff_compare_lines(a->line + a->indent_off,
+ a->len - a->indent_off,
+ b->line + b->indent_off,
+ b->len - b->indent_off, flags);
}
-static struct moved_entry *prepare_entry(struct diff_options *o,
- int line_no)
+static void prepare_entry(struct diff_options *o, struct emitted_diff_symbol *l,
+ struct interned_diff_symbol *s)
{
- struct moved_entry *ret = xmalloc(sizeof(*ret));
- struct emitted_diff_symbol *l = &o->emitted_symbols->buf[line_no];
unsigned flags = o->color_moved_ws_handling & XDF_WHITESPACE_FLAGS;
- unsigned int hash = xdiff_hash_string(l->line, l->len, flags);
+ unsigned int hash = xdiff_hash_string(l->line + l->indent_off,
+ l->len - l->indent_off, flags);
- hashmap_entry_init(&ret->ent, hash);
- ret->es = l;
- ret->next_line = NULL;
-
- return ret;
+ hashmap_entry_init(&s->ent, hash);
+ s->es = l;
}
-static void add_lines_to_move_detection(struct diff_options *o,
- struct hashmap *add_lines,
- struct hashmap *del_lines)
+struct moved_entry_list {
+ struct moved_entry *add, *del;
+};
+
+static struct moved_entry_list *add_lines_to_move_detection(struct diff_options *o,
+ struct mem_pool *entry_mem_pool)
{
struct moved_entry *prev_line = NULL;
-
+ struct mem_pool interned_pool;
+ struct hashmap interned_map;
+ struct moved_entry_list *entry_list = NULL;
+ size_t entry_list_alloc = 0;
+ unsigned id = 0;
int n;
- for (n = 0; n < o->emitted_symbols->nr; n++) {
- struct hashmap *hm;
- struct moved_entry *key;
- switch (o->emitted_symbols->buf[n].s) {
- case DIFF_SYMBOL_PLUS:
- hm = add_lines;
- break;
- case DIFF_SYMBOL_MINUS:
- hm = del_lines;
- break;
- default:
+ hashmap_init(&interned_map, interned_diff_symbol_cmp, o, 8096);
+ mem_pool_init(&interned_pool, 1024 * 1024);
+
+ for (n = 0; n < o->emitted_symbols->nr; n++) {
+ struct interned_diff_symbol key;
+ struct emitted_diff_symbol *l = &o->emitted_symbols->buf[n];
+ struct interned_diff_symbol *s;
+ struct moved_entry *entry;
+
+ if (l->s != DIFF_SYMBOL_PLUS && l->s != DIFF_SYMBOL_MINUS) {
prev_line = NULL;
continue;
}
if (o->color_moved_ws_handling &
COLOR_MOVED_WS_ALLOW_INDENTATION_CHANGE)
- fill_es_indent_data(&o->emitted_symbols->buf[n]);
- key = prepare_entry(o, n);
- if (prev_line && prev_line->es->s == o->emitted_symbols->buf[n].s)
- prev_line->next_line = key;
+ fill_es_indent_data(l);
- hashmap_add(hm, &key->ent);
- prev_line = key;
+ prepare_entry(o, l, &key);
+ s = hashmap_get_entry(&interned_map, &key, ent, &key.ent);
+ if (s) {
+ l->id = s->es->id;
+ } else {
+ l->id = id;
+ ALLOC_GROW_BY(entry_list, id, 1, entry_list_alloc);
+ hashmap_add(&interned_map,
+ memcpy(mem_pool_alloc(&interned_pool,
+ sizeof(key)),
+ &key, sizeof(key)));
+ }
+ entry = mem_pool_alloc(entry_mem_pool, sizeof(*entry));
+ entry->es = l;
+ entry->next_line = NULL;
+ if (prev_line && prev_line->es->s == l->s)
+ prev_line->next_line = entry;
+ prev_line = entry;
+ if (l->s == DIFF_SYMBOL_PLUS) {
+ entry->next_match = entry_list[l->id].add;
+ entry_list[l->id].add = entry;
+ } else {
+ entry->next_match = entry_list[l->id].del;
+ entry_list[l->id].del = entry;
+ }
}
+
+ hashmap_clear(&interned_map);
+ mem_pool_discard(&interned_pool, 0);
+
+ return entry_list;
}
static void pmb_advance_or_null(struct diff_options *o,
- struct moved_entry *match,
- struct hashmap *hm,
+ struct emitted_diff_symbol *l,
struct moved_block *pmb,
- int pmb_nr)
+ int *pmb_nr)
{
- int i;
- for (i = 0; i < pmb_nr; i++) {
+ int i, j;
+
+ for (i = 0, j = 0; i < *pmb_nr; i++) {
+ int match;
struct moved_entry *prev = pmb[i].match;
struct moved_entry *cur = (prev && prev->next_line) ?
prev->next_line : NULL;
- if (cur && !hm->cmpfn(o, &cur->ent, &match->ent, NULL)) {
- pmb[i].match = cur;
- } else {
- pmb[i].match = NULL;
+
+ if (o->color_moved_ws_handling &
+ COLOR_MOVED_WS_ALLOW_INDENTATION_CHANGE)
+ match = cur &&
+ !cmp_in_block_with_wsd(cur, l, &pmb[i]);
+ else
+ match = cur && cur->es->id == l->id;
+
+ if (match) {
+ pmb[j] = pmb[i];
+ pmb[j++].match = cur;
}
}
+ *pmb_nr = j;
}
-static void pmb_advance_or_null_multi_match(struct diff_options *o,
- struct moved_entry *match,
- struct hashmap *hm,
- struct moved_block *pmb,
- int pmb_nr, int n)
+static void fill_potential_moved_blocks(struct diff_options *o,
+ struct moved_entry *match,
+ struct emitted_diff_symbol *l,
+ struct moved_block **pmb_p,
+ int *pmb_alloc_p, int *pmb_nr_p)
+
{
- int i;
- char *got_match = xcalloc(1, pmb_nr);
+ struct moved_block *pmb = *pmb_p;
+ int pmb_alloc = *pmb_alloc_p, pmb_nr = *pmb_nr_p;
- hashmap_for_each_entry_from(hm, match, ent) {
- for (i = 0; i < pmb_nr; i++) {
- struct moved_entry *prev = pmb[i].match;
- struct moved_entry *cur = (prev && prev->next_line) ?
- prev->next_line : NULL;
- if (!cur)
- continue;
- if (!cmp_in_block_with_wsd(o, cur, match, &pmb[i], n))
- got_match[i] |= 1;
- }
+ /*
+ * The current line is the start of a new block.
+ * Setup the set of potential blocks.
+ */
+ for (; match; match = match->next_match) {
+ ALLOC_GROW(pmb, pmb_nr + 1, pmb_alloc);
+ if (o->color_moved_ws_handling &
+ COLOR_MOVED_WS_ALLOW_INDENTATION_CHANGE)
+ pmb[pmb_nr].wsd = compute_ws_delta(l, match->es);
+ else
+ pmb[pmb_nr].wsd = 0;
+ pmb[pmb_nr++].match = match;
}
- for (i = 0; i < pmb_nr; i++) {
- if (got_match[i]) {
- /* Advance to the next line */
- pmb[i].match = pmb[i].match->next_line;
- } else {
- moved_block_clear(&pmb[i]);
- }
- }
-
- free(got_match);
-}
-
-static int shrink_potential_moved_blocks(struct moved_block *pmb,
- int pmb_nr)
-{
- int lp, rp;
-
- /* Shrink the set of potential block to the remaining running */
- for (lp = 0, rp = pmb_nr - 1; lp <= rp;) {
- while (lp < pmb_nr && pmb[lp].match)
- lp++;
- /* lp points at the first NULL now */
-
- while (rp > -1 && !pmb[rp].match)
- rp--;
- /* rp points at the last non-NULL */
-
- if (lp < pmb_nr && rp > -1 && lp < rp) {
- pmb[lp] = pmb[rp];
- memset(&pmb[rp], 0, sizeof(pmb[rp]));
- rp--;
- lp++;
- }
- }
-
- /* Remember the number of running sets */
- return rp + 1;
+ *pmb_p = pmb;
+ *pmb_alloc_p = pmb_alloc;
+ *pmb_nr_p = pmb_nr;
}
/*
@@ -1115,6 +1073,8 @@
* NEEDSWORK: This uses the same heuristic as blame_entry_score() in blame.c.
* Think of a way to unify them.
*/
+#define DIFF_SYMBOL_MOVED_LINE_ZEBRA_MASK \
+ (DIFF_SYMBOL_MOVED_LINE | DIFF_SYMBOL_MOVED_LINE_ALT)
static int adjust_last_block(struct diff_options *o, int n, int block_length)
{
int i, alnum_count = 0;
@@ -1131,95 +1091,85 @@
}
}
for (i = 1; i < block_length + 1; i++)
- o->emitted_symbols->buf[n - i].flags &= ~DIFF_SYMBOL_MOVED_LINE;
+ o->emitted_symbols->buf[n - i].flags &= ~DIFF_SYMBOL_MOVED_LINE_ZEBRA_MASK;
return 0;
}
/* Find blocks of moved code, delegate actual coloring decision to helper */
static void mark_color_as_moved(struct diff_options *o,
- struct hashmap *add_lines,
- struct hashmap *del_lines)
+ struct moved_entry_list *entry_list)
{
struct moved_block *pmb = NULL; /* potentially moved blocks */
int pmb_nr = 0, pmb_alloc = 0;
int n, flipped_block = 0, block_length = 0;
+ enum diff_symbol moved_symbol = DIFF_SYMBOL_BINARY_DIFF_HEADER;
for (n = 0; n < o->emitted_symbols->nr; n++) {
- struct hashmap *hm = NULL;
- struct moved_entry *key;
struct moved_entry *match = NULL;
struct emitted_diff_symbol *l = &o->emitted_symbols->buf[n];
- enum diff_symbol last_symbol = 0;
switch (l->s) {
case DIFF_SYMBOL_PLUS:
- hm = del_lines;
- key = prepare_entry(o, n);
- match = hashmap_get_entry(hm, key, ent, NULL);
- free(key);
+ match = entry_list[l->id].del;
break;
case DIFF_SYMBOL_MINUS:
- hm = add_lines;
- key = prepare_entry(o, n);
- match = hashmap_get_entry(hm, key, ent, NULL);
- free(key);
+ match = entry_list[l->id].add;
break;
default:
flipped_block = 0;
}
- if (!match) {
- int i;
-
- adjust_last_block(o, n, block_length);
- for(i = 0; i < pmb_nr; i++)
- moved_block_clear(&pmb[i]);
+ if (pmb_nr && (!match || l->s != moved_symbol)) {
+ if (!adjust_last_block(o, n, block_length) &&
+ block_length > 1) {
+ /*
+ * Rewind in case there is another match
+ * starting at the second line of the block
+ */
+ match = NULL;
+ n -= block_length;
+ }
pmb_nr = 0;
block_length = 0;
flipped_block = 0;
- last_symbol = l->s;
+ }
+ if (!match) {
+ moved_symbol = DIFF_SYMBOL_BINARY_DIFF_HEADER;
continue;
}
if (o->color_moved == COLOR_MOVED_PLAIN) {
- last_symbol = l->s;
l->flags |= DIFF_SYMBOL_MOVED_LINE;
continue;
}
- if (o->color_moved_ws_handling &
- COLOR_MOVED_WS_ALLOW_INDENTATION_CHANGE)
- pmb_advance_or_null_multi_match(o, match, hm, pmb, pmb_nr, n);
- else
- pmb_advance_or_null(o, match, hm, pmb, pmb_nr);
-
- pmb_nr = shrink_potential_moved_blocks(pmb, pmb_nr);
+ pmb_advance_or_null(o, l, pmb, &pmb_nr);
if (pmb_nr == 0) {
- /*
- * The current line is the start of a new block.
- * Setup the set of potential blocks.
- */
- hashmap_for_each_entry_from(hm, match, ent) {
- ALLOC_GROW(pmb, pmb_nr + 1, pmb_alloc);
- if (o->color_moved_ws_handling &
- COLOR_MOVED_WS_ALLOW_INDENTATION_CHANGE) {
- if (compute_ws_delta(l, match->es,
- &pmb[pmb_nr].wsd))
- pmb[pmb_nr++].match = match;
- } else {
- pmb[pmb_nr].wsd = 0;
- pmb[pmb_nr++].match = match;
- }
- }
+ int contiguous = adjust_last_block(o, n, block_length);
- if (adjust_last_block(o, n, block_length) &&
- pmb_nr && last_symbol != l->s)
+ if (!contiguous && block_length > 1)
+ /*
+ * Rewind in case there is another match
+ * starting at the second line of the block
+ */
+ n -= block_length;
+ else
+ fill_potential_moved_blocks(o, match, l,
+ &pmb, &pmb_alloc,
+ &pmb_nr);
+
+ if (contiguous && pmb_nr && moved_symbol == l->s)
flipped_block = (flipped_block + 1) % 2;
else
flipped_block = 0;
+ if (pmb_nr)
+ moved_symbol = l->s;
+ else
+ moved_symbol = DIFF_SYMBOL_BINARY_DIFF_HEADER;
+
block_length = 0;
}
@@ -1229,17 +1179,12 @@
if (flipped_block && o->color_moved != COLOR_MOVED_BLOCKS)
l->flags |= DIFF_SYMBOL_MOVED_LINE_ALT;
}
- last_symbol = l->s;
}
adjust_last_block(o, n, block_length);
- for(n = 0; n < pmb_nr; n++)
- moved_block_clear(&pmb[n]);
free(pmb);
}
-#define DIFF_SYMBOL_MOVED_LINE_ZEBRA_MASK \
- (DIFF_SYMBOL_MOVED_LINE | DIFF_SYMBOL_MOVED_LINE_ALT)
static void dim_moved_lines(struct diff_options *o)
{
int n;
@@ -1573,7 +1518,9 @@
static void emit_diff_symbol(struct diff_options *o, enum diff_symbol s,
const char *line, int len, unsigned flags)
{
- struct emitted_diff_symbol e = {line, len, flags, 0, 0, s};
+ struct emitted_diff_symbol e = {
+ .line = line, .len = len, .flags = flags, .s = s
+ };
if (o->emitted_symbols)
append_emitted_diff_symbol(o, &e);
@@ -4639,16 +4586,20 @@
options->set_default(options);
if (HAS_MULTI_BITS(options->output_format & check_mask))
- die(_("--name-only, --name-status, --check and -s are mutually exclusive"));
+ die(_("options '%s', '%s', '%s', and '%s' cannot be used together"),
+ "--name-only", "--name-status", "--check", "-s");
if (HAS_MULTI_BITS(options->pickaxe_opts & DIFF_PICKAXE_KINDS_MASK))
- die(_("-G, -S and --find-object are mutually exclusive"));
+ die(_("options '%s', '%s', and '%s' cannot be used together"),
+ "-G", "-S", "--find-object");
if (HAS_MULTI_BITS(options->pickaxe_opts & DIFF_PICKAXE_KINDS_G_REGEX_MASK))
- die(_("-G and --pickaxe-regex are mutually exclusive, use --pickaxe-regex with -S"));
+ die(_("options '%s' and '%s' cannot be used together, use '%s' with '%s'"),
+ "-G", "--pickaxe-regex", "--pickaxe-regex", "-S");
if (HAS_MULTI_BITS(options->pickaxe_opts & DIFF_PICKAXE_KINDS_ALL_OBJFIND_MASK))
- die(_("--pickaxe-all and --find-object are mutually exclusive, use --pickaxe-all with -G and -S"));
+ die(_("options '%s' and '%s' cannot be used together, use '%s' with '%s' and '%s'"),
+ "--pickaxe-all", "--find-object", "--pickaxe-all", "-G", "-S");
/*
* Most of the time we can say "there are changes"
@@ -6345,24 +6296,18 @@
if (o->emitted_symbols) {
if (o->color_moved) {
- struct hashmap add_lines, del_lines;
+ struct mem_pool entry_pool;
+ struct moved_entry_list *entry_list;
- if (o->color_moved_ws_handling &
- COLOR_MOVED_WS_ALLOW_INDENTATION_CHANGE)
- o->color_moved_ws_handling |= XDF_IGNORE_WHITESPACE;
-
- hashmap_init(&del_lines, moved_entry_cmp, o, 0);
- hashmap_init(&add_lines, moved_entry_cmp, o, 0);
-
- add_lines_to_move_detection(o, &add_lines, &del_lines);
- mark_color_as_moved(o, &add_lines, &del_lines);
+ mem_pool_init(&entry_pool, 1024 * 1024);
+ entry_list = add_lines_to_move_detection(o,
+ &entry_pool);
+ mark_color_as_moved(o, entry_list);
if (o->color_moved == COLOR_MOVED_ZEBRA_DIM)
dim_moved_lines(o);
- hashmap_clear_and_free(&add_lines, struct moved_entry,
- ent);
- hashmap_clear_and_free(&del_lines, struct moved_entry,
- ent);
+ mem_pool_discard(&entry_pool, 0);
+ free(entry_list);
}
for (i = 0; i < esm.nr; i++)
@@ -6921,19 +6866,15 @@
size_t *outsize)
{
struct diff_tempfile *temp;
- const char *argv[3];
- const char **arg = argv;
struct child_process child = CHILD_PROCESS_INIT;
struct strbuf buf = STRBUF_INIT;
int err = 0;
temp = prepare_temp_file(r, spec->path, spec);
- *arg++ = pgm;
- *arg++ = temp->name;
- *arg = NULL;
+ strvec_push(&child.args, pgm);
+ strvec_push(&child.args, temp->name);
child.use_shell = 1;
- child.argv = argv;
child.out = -1;
if (start_command(&child)) {
remove_tempfile();
diff --git a/diffcore-delta.c b/diffcore-delta.c
index 5668ace..18d8f76 100644
--- a/diffcore-delta.c
+++ b/diffcore-delta.c
@@ -133,10 +133,10 @@
i = INITIAL_HASH_SIZE;
hash = xmalloc(st_add(sizeof(*hash),
- st_mult(sizeof(struct spanhash), 1<<i)));
+ st_mult(sizeof(struct spanhash), (size_t)1 << i)));
hash->alloc_log2 = i;
hash->free = INITIAL_FREE(i);
- memset(hash->data, 0, sizeof(struct spanhash) * (1<<i));
+ memset(hash->data, 0, sizeof(struct spanhash) * ((size_t)1 << i));
n = 0;
accum1 = accum2 = 0;
@@ -159,7 +159,7 @@
n = 0;
accum1 = accum2 = 0;
}
- QSORT(hash->data, 1ul << hash->alloc_log2, spanhash_cmp);
+ QSORT(hash->data, (size_t)1ul << hash->alloc_log2, spanhash_cmp);
return hash;
}
diff --git a/dir.c b/dir.c
index 5aa6fba..d91295f 100644
--- a/dir.c
+++ b/dir.c
@@ -727,7 +727,7 @@
}
if (given->patternlen < 2 ||
- *given->pattern == '*' ||
+ *given->pattern != '/' ||
strstr(given->pattern, "**")) {
/* Not a cone pattern. */
warning(_("unrecognized pattern: '%s'"), given->pattern);
@@ -819,9 +819,7 @@
/* we already included this at the parent level */
warning(_("your sparse-checkout file may have issues: pattern '%s' is repeated"),
given->pattern);
- hashmap_remove(&pl->parent_hashmap, &translated->ent, &data);
- free(data);
- free(translated);
+ goto clear_hashmaps;
}
return;
@@ -3160,6 +3158,7 @@
int ret = 0, original_len = path->len, len, kept_down = 0;
int only_empty = (flag & REMOVE_DIR_EMPTY_ONLY);
int keep_toplevel = (flag & REMOVE_DIR_KEEP_TOPLEVEL);
+ int purge_original_cwd = (flag & REMOVE_DIR_PURGE_ORIGINAL_CWD);
struct object_id submodule_head;
if ((flag & REMOVE_DIR_KEEP_NESTED_GIT) &&
@@ -3215,9 +3214,14 @@
closedir(dir);
strbuf_setlen(path, original_len);
- if (!ret && !keep_toplevel && !kept_down)
- ret = (!rmdir(path->buf) || errno == ENOENT) ? 0 : -1;
- else if (kept_up)
+ if (!ret && !keep_toplevel && !kept_down) {
+ if (!purge_original_cwd &&
+ startup_info->original_cwd &&
+ !strcmp(startup_info->original_cwd, path->buf))
+ ret = -1; /* Do not remove current working directory */
+ else
+ ret = (!rmdir(path->buf) || errno == ENOENT) ? 0 : -1;
+ } else if (kept_up)
/*
* report the uplevel that it is not an error that we
* did not rmdir() our directory.
@@ -3283,6 +3287,9 @@
slash = dirs + (slash - name);
do {
*slash = '\0';
+ if (startup_info->original_cwd &&
+ !strcmp(startup_info->original_cwd, dirs))
+ break;
} while (rmdir(dirs) == 0 && (slash = strrchr(dirs, '/')));
free(dirs);
}
diff --git a/dir.h b/dir.h
index 83f46c0..8e02dfb 100644
--- a/dir.h
+++ b/dir.h
@@ -495,6 +495,9 @@
/* Remove the contents of path, but leave path itself. */
#define REMOVE_DIR_KEEP_TOPLEVEL 04
+/* Remove the_original_cwd too */
+#define REMOVE_DIR_PURGE_ORIGINAL_CWD 0x08
+
/*
* Remove path and its contents, recursively. flags is a combination
* of the above REMOVE_DIR_* constants. Return 0 on success.
@@ -504,7 +507,11 @@
*/
int remove_dir_recursively(struct strbuf *path, int flag);
-/* tries to remove the path with empty directories along it, ignores ENOENT */
+/*
+ * Tries to remove the path, along with leading empty directories so long as
+ * those empty directories are not startup_info->original_cwd. Ignores
+ * ENOENT.
+ */
int remove_path(const char *path);
int fspathcmp(const char *a, const char *b);
diff --git a/editor.c b/editor.c
index fdd3eea..8b96482 100644
--- a/editor.c
+++ b/editor.c
@@ -1,6 +1,7 @@
#include "cache.h"
#include "config.h"
#include "strbuf.h"
+#include "strvec.h"
#include "run-command.h"
#include "sigchain.h"
@@ -55,7 +56,6 @@
if (strcmp(editor, ":")) {
struct strbuf realpath = STRBUF_INIT;
- const char *args[] = { editor, NULL, NULL };
struct child_process p = CHILD_PROCESS_INIT;
int ret, sig;
int print_waiting_for_editor = advice_enabled(ADVICE_WAITING_FOR_EDITOR) && isatty(2);
@@ -77,10 +77,10 @@
}
strbuf_realpath(&realpath, path, 1);
- args[1] = realpath.buf;
- p.argv = args;
- p.env = env;
+ strvec_pushl(&p.args, editor, realpath.buf, NULL);
+ if (env)
+ strvec_pushv(&p.env_array, (const char **)env);
p.use_shell = 1;
p.trace2_child_class = "editor";
if (start_command(&p) < 0) {
diff --git a/entry.c b/entry.c
index 9b0f968..1c9df62 100644
--- a/entry.c
+++ b/entry.c
@@ -82,11 +82,13 @@
return open(path, O_WRONLY | O_CREAT | O_EXCL, mode);
}
-void *read_blob_entry(const struct cache_entry *ce, unsigned long *size)
+void *read_blob_entry(const struct cache_entry *ce, size_t *size)
{
enum object_type type;
- void *blob_data = read_object_file(&ce->oid, &type, size);
+ unsigned long ul;
+ void *blob_data = read_object_file(&ce->oid, &type, &ul);
+ *size = ul;
if (blob_data) {
if (type == OBJ_BLOB)
return blob_data;
@@ -271,7 +273,7 @@
int fd, ret, fstat_done = 0;
char *new_blob;
struct strbuf buf = STRBUF_INIT;
- unsigned long size;
+ size_t size;
ssize_t wrote;
size_t newsize = 0;
struct stat st;
diff --git a/entry.h b/entry.h
index 2254c62..252fd24 100644
--- a/entry.h
+++ b/entry.h
@@ -52,7 +52,7 @@
*/
void unlink_entry(const struct cache_entry *ce);
-void *read_blob_entry(const struct cache_entry *ce, unsigned long *size);
+void *read_blob_entry(const struct cache_entry *ce, size_t *size);
int fstat_checkout_output(int fd, const struct checkout *state, struct stat *st);
void update_ce_after_write(const struct checkout *state, struct cache_entry *ce,
struct stat *st);
diff --git a/environment.c b/environment.c
index 9da7f3c..fd0501e 100644
--- a/environment.c
+++ b/environment.c
@@ -17,6 +17,7 @@
#include "commit.h"
#include "strvec.h"
#include "object-store.h"
+#include "tmp-objdir.h"
#include "chdir-notify.h"
#include "shallow.h"
@@ -42,6 +43,7 @@
int zlib_compression_level = Z_BEST_SPEED;
int pack_compression_level = Z_DEFAULT_COMPRESSION;
int fsync_object_files;
+int use_fsync = -1;
size_t packed_git_window_size = DEFAULT_PACKED_GIT_WINDOW_SIZE;
size_t packed_git_limit = DEFAULT_PACKED_GIT_LIMIT;
size_t delta_base_cache_limit = 96 * 1024 * 1024;
@@ -168,6 +170,10 @@
args.graft_file = getenv_safe(&to_free, GRAFT_ENVIRONMENT);
args.index_file = getenv_safe(&to_free, INDEX_ENVIRONMENT);
args.alternate_db = getenv_safe(&to_free, ALTERNATE_DB_ENVIRONMENT);
+ if (getenv(GIT_QUARANTINE_ENVIRONMENT)) {
+ args.disable_ref_updates = 1;
+ }
+
repo_set_gitdir(the_repository, git_dir, &args);
strvec_clear(&to_free);
@@ -331,10 +337,14 @@
void *data)
{
char *path = reparent_relative_path(old_cwd, new_cwd, get_git_dir());
+ struct tmp_objdir *tmp_objdir = tmp_objdir_unapply_primary_odb();
+
trace_printf_key(&trace_setup_key,
"setup: move $GIT_DIR to '%s'",
path);
set_git_dir_1(path);
+ if (tmp_objdir)
+ tmp_objdir_reapply_primary_odb(tmp_objdir, old_cwd, new_cwd);
free(path);
}
diff --git a/fetch-pack.c b/fetch-pack.c
index a9604f3..dd6ec44 100644
--- a/fetch-pack.c
+++ b/fetch-pack.c
@@ -25,6 +25,7 @@
#include "shallow.h"
#include "commit-reach.h"
#include "commit-graph.h"
+#include "sigchain.h"
static int transfer_unpack_limit = -1;
static int fetch_unpack_limit = -1;
@@ -296,7 +297,7 @@
struct packet_reader reader;
if (args->stateless_rpc && multi_ack == 1)
- die(_("--stateless-rpc requires multi_ack_detailed"));
+ die(_("the option '%s' requires '%s'"), "--stateless-rpc", "multi_ack_detailed");
packet_reader_init(&reader, fd[0], NULL, 0,
PACKET_READ_CHOMP_NEWLINE |
@@ -956,6 +957,8 @@
strvec_push(index_pack_args, cmd.args.v[i]);
}
+ sigchain_push(SIGPIPE, SIG_IGN);
+
cmd.in = demux.out;
cmd.git_cmd = 1;
if (start_command(&cmd))
@@ -986,6 +989,8 @@
if (use_sideband && finish_async(&demux))
die(_("error in sideband demultiplexer"));
+ sigchain_pop(SIGPIPE);
+
/*
* Now that index-pack has succeeded, write the promisor file using the
* obtained .keep filename if necessary
@@ -1653,8 +1658,13 @@
receive_wanted_refs(&reader, sought, nr_sought);
/* get the pack(s) */
+ if (git_env_bool("GIT_TRACE_REDACT", 1))
+ reader.options |= PACKET_READ_REDACT_URI_PATH;
if (process_section_header(&reader, "packfile-uris", 1))
receive_packfile_uris(&reader, &packfile_uris);
+ /* We don't expect more URIs. Reset to avoid expensive URI check. */
+ reader.options &= ~PACKET_READ_REDACT_URI_PATH;
+
process_section_header(&reader, "packfile", 0);
/*
diff --git a/fmt-merge-msg.c b/fmt-merge-msg.c
index 5216191..baca57d 100644
--- a/fmt-merge-msg.c
+++ b/fmt-merge-msg.c
@@ -533,14 +533,14 @@
else {
buf = payload.buf;
len = payload.len;
- if (check_signature(payload.buf, payload.len, sig.buf,
- sig.len, &sigc) &&
+ sigc.payload_type = SIGNATURE_PAYLOAD_TAG;
+ sigc.payload = strbuf_detach(&payload, &sigc.payload_len);
+ if (check_signature(&sigc, sig.buf, sig.len) &&
!sigc.output)
strbuf_addstr(&sig, "gpg verification failed.\n");
else
strbuf_addstr(&sig, sigc.output);
}
- signature_check_clear(&sigc);
if (!tag_number++) {
fmt_tag_signature(&tagbuf, &sig, buf, len);
@@ -564,6 +564,7 @@
}
strbuf_release(&payload);
strbuf_release(&sig);
+ signature_check_clear(&sigc);
next:
free(origbuf);
}
@@ -649,12 +650,15 @@
memset(&merge_parents, 0, sizeof(merge_parents));
- /* get current branch */
+ /* learn the commit that we merge into and the current branch name */
current_branch = current_branch_to_free =
resolve_refdup("HEAD", RESOLVE_REF_READING, &head_oid, NULL);
if (!current_branch)
die("No current branch");
- if (starts_with(current_branch, "refs/heads/"))
+
+ if (opts->into_name)
+ current_branch = opts->into_name;
+ else if (starts_with(current_branch, "refs/heads/"))
current_branch += 11;
find_merge_parents(&merge_parents, in, &head_oid);
diff --git a/fmt-merge-msg.h b/fmt-merge-msg.h
index f2ab0e0..9905404 100644
--- a/fmt-merge-msg.h
+++ b/fmt-merge-msg.h
@@ -9,6 +9,7 @@
unsigned add_title:1,
credit_people:1;
int shortlog_len;
+ const char *into_name;
};
extern int merge_log_config;
diff --git a/generate-cmdlist.sh b/generate-cmdlist.sh
index 9dbbb08..205541e 100755
--- a/generate-cmdlist.sh
+++ b/generate-cmdlist.sh
@@ -6,37 +6,38 @@
}
command_list () {
- eval "grep -ve '^#' $exclude_programs" <"$1"
-}
-
-get_categories () {
- tr ' ' '\012'|
- grep -v '^$' |
- sort |
- uniq
+ while read cmd rest
+ do
+ case "$cmd" in
+ "#"* | '')
+ # Ignore comments and allow empty lines
+ continue
+ ;;
+ *)
+ case "$exclude_programs" in
+ *":$cmd:"*)
+ ;;
+ *)
+ echo "$cmd $rest"
+ ;;
+ esac
+ esac
+ done <"$1"
}
category_list () {
- command_list "$1" |
- cut -c 40- |
- get_categories
-}
-
-get_synopsis () {
- sed -n '
- /^NAME/,/'"$1"'/H
- ${
- x
- s/.*'"$1"' - \(.*\)/N_("\1")/
- p
- }' "Documentation/$1.txt"
+ echo "$1" |
+ cut -d' ' -f2- |
+ tr ' ' '\012' |
+ grep -v '^$' |
+ LC_ALL=C sort -u
}
define_categories () {
echo
echo "/* Command categories */"
bit=0
- category_list "$1" |
+ echo "$1" |
while read cat
do
echo "#define CAT_$cat (1UL << $bit)"
@@ -50,7 +51,7 @@
echo "/* Category names */"
echo "static const char *category_names[] = {"
bit=0
- category_list "$1" |
+ echo "$1" |
while read cat
do
echo " \"$cat\", /* (1UL << $bit) */"
@@ -63,27 +64,38 @@
print_command_list () {
echo "static struct cmdname_help command_list[] = {"
- command_list "$1" |
+ echo "$1" |
while read cmd rest
do
- printf " { \"$cmd\", $(get_synopsis $cmd), 0"
- for cat in $(echo "$rest" | get_categories)
+ synopsis=
+ while read line
do
- printf " | CAT_$cat"
- done
+ case "$line" in
+ "$cmd - "*)
+ synopsis=${line#$cmd - }
+ break
+ ;;
+ esac
+ done <"Documentation/$cmd.txt"
+
+ printf '\t{ "%s", N_("%s"), 0' "$cmd" "$synopsis"
+ printf " | CAT_%s" $rest
echo " },"
done
echo "};"
}
-exclude_programs=
+exclude_programs=:
while test "--exclude-program" = "$1"
do
shift
- exclude_programs="$exclude_programs -e \"^$1 \""
+ exclude_programs="$exclude_programs$1:"
shift
done
+commands="$(command_list "$1")"
+categories="$(category_list "$commands")"
+
echo "/* Automatically generated by generate-cmdlist.sh */
struct cmdname_help {
const char *name;
@@ -91,8 +103,8 @@
uint32_t category;
};
"
-define_categories "$1"
+define_categories "$categories"
echo
-define_category_names "$1"
+define_category_names "$categories"
echo
-print_command_list "$1"
+print_command_list "$commands"
diff --git a/git-add--interactive.perl b/git-add--interactive.perl
index bc3a1e8..95887fd 100755
--- a/git-add--interactive.perl
+++ b/git-add--interactive.perl
@@ -1175,15 +1175,17 @@
ReadMode 'cbreak';
my $key = ReadKey 0;
ReadMode 'restore';
- if ($use_termcap and $key eq "\e") {
- while (!defined $term_escapes{$key}) {
- my $next = ReadKey 0.5;
- last if (!defined $next);
- $key .= $next;
+ if (defined $key) {
+ if ($use_termcap and $key eq "\e") {
+ while (!defined $term_escapes{$key}) {
+ my $next = ReadKey 0.5;
+ last if (!defined $next);
+ $key .= $next;
+ }
+ $key =~ s/\e/^[/;
}
- $key =~ s/\e/^[/;
+ print "$key";
}
- print "$key" if defined $key;
print "\n";
return $key;
} else {
diff --git a/git-compat-util.h b/git-compat-util.h
index 4b57ae6..e30a374 100644
--- a/git-compat-util.h
+++ b/git-compat-util.h
@@ -1,6 +1,19 @@
#ifndef GIT_COMPAT_UTIL_H
#define GIT_COMPAT_UTIL_H
+#if __STDC_VERSION__ - 0 < 199901L
+/*
+ * Git is in a testing period for mandatory C99 support in the compiler. If
+ * your compiler is reasonably recent, you can try to enable C99 support (or,
+ * for MSVC, C11 support). If you encounter a problem and can't enable C99
+ * support with your compiler (such as with "-std=gnu99") and don't have access
+ * to one with this support, such as GCC or Clang, you can remove this #if
+ * directive, but please report the details of your system to
+ * git@vger.kernel.org.
+ */
+#error "Required C99 support is in a test phase. Please see git-compat-util.h for more details."
+#endif
+
#ifdef USE_MSVC_CRTDBG
/*
* For these to work they must appear very early in each
@@ -33,14 +46,23 @@
/*
* See if our compiler is known to support flexible array members.
*/
-#if defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) && (!defined(__SUNPRO_C) || (__SUNPRO_C > 0x580))
-# define FLEX_ARRAY /* empty */
+
+/*
+ * Check vendor specific quirks first, before checking the
+ * __STDC_VERSION__, as vendor compilers can lie and we need to be
+ * able to work them around. Note that by not defining FLEX_ARRAY
+ * here, we can fall back to use the "safer but a bit wasteful" one
+ * later.
+ */
+#if defined(__SUNPRO_C) && (__SUNPRO_C <= 0x580)
#elif defined(__GNUC__)
# if (__GNUC__ >= 3)
# define FLEX_ARRAY /* empty */
# else
# define FLEX_ARRAY 0 /* older GNU extension */
# endif
+#elif defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L)
+# define FLEX_ARRAY /* empty */
#endif
/*
@@ -113,6 +135,14 @@
#define unsigned_mult_overflows(a, b) \
((a) && (b) > maximum_unsigned_value_of_type(a) / (a))
+/*
+ * Returns true if the left shift of "a" by "shift" bits will
+ * overflow. The type of "a" must be unsigned.
+ */
+#define unsigned_left_shift_overflows(a, shift) \
+ ((shift) < bitsizeof(a) && \
+ (a) > maximum_unsigned_value_of_type(a) >> (shift))
+
#ifdef __GNUC__
#define TYPEOF(x) (__typeof__(x))
#else
@@ -480,11 +510,12 @@
struct strbuf;
/* General helper functions */
-void vreportf(const char *prefix, const char *err, va_list params);
NORETURN void usage(const char *err);
NORETURN void usagef(const char *err, ...) __attribute__((format (printf, 1, 2)));
NORETURN void die(const char *err, ...) __attribute__((format (printf, 1, 2)));
NORETURN void die_errno(const char *err, ...) __attribute__((format (printf, 1, 2)));
+int die_message(const char *err, ...) __attribute__((format (printf, 1, 2)));
+int die_message_errno(const char *err, ...) __attribute__((format (printf, 1, 2)));
int error(const char *err, ...) __attribute__((format (printf, 1, 2)));
int error_errno(const char *err, ...) __attribute__((format (printf, 1, 2)));
void warning(const char *err, ...) __attribute__((format (printf, 1, 2)));
@@ -519,6 +550,7 @@
typedef void (*report_fn)(const char *, va_list params);
void set_die_routine(NORETURN_PTR report_fn routine);
+report_fn get_die_message_routine(void);
void set_error_routine(report_fn routine);
report_fn get_error_routine(void);
void set_warn_routine(report_fn routine);
@@ -743,7 +775,7 @@
#ifdef NO_UNSETENV
#define unsetenv gitunsetenv
-void gitunsetenv(const char *);
+int gitunsetenv(const char *);
#endif
#ifdef NO_STRCASESTR
@@ -876,6 +908,23 @@
return a - b;
}
+static inline size_t st_left_shift(size_t a, unsigned shift)
+{
+ if (unsigned_left_shift_overflows(a, shift))
+ die("size_t overflow: %"PRIuMAX" << %u",
+ (uintmax_t)a, shift);
+ return a << shift;
+}
+
+static inline unsigned long cast_size_t_to_ulong(size_t a)
+{
+ if (a != (unsigned long)a)
+ die("object too large to read on this platform: %"
+ PRIuMAX" is cut off to %lu",
+ (uintmax_t)a, (unsigned long)a);
+ return (unsigned long)a;
+}
+
#ifdef HAVE_ALLOCA_H
# include <alloca.h>
# define xalloca(size) (alloca(size))
diff --git a/git-cvsserver.perl b/git-cvsserver.perl
index 64319be..4c81180 100755
--- a/git-cvsserver.perl
+++ b/git-cvsserver.perl
@@ -3607,6 +3607,22 @@
use strict;
use warnings;
use DBI;
+our $_use_fsync;
+
+# n.b. consider using Git.pm
+sub use_fsync {
+ if (!defined($_use_fsync)) {
+ my $x = $ENV{GIT_TEST_FSYNC};
+ if (defined $x) {
+ local $ENV{GIT_CONFIG};
+ delete $ENV{GIT_CONFIG};
+ my $v = ::safe_pipe_capture('git', '-c', "test.fsync=$x",
+ qw(config --type=bool test.fsync));
+ $_use_fsync = defined($v) ? ($v eq "true\n") : 1;
+ }
+ }
+ $_use_fsync;
+}
=head1 METHODS
@@ -3676,6 +3692,9 @@
$self->{dbuser},
$self->{dbpass});
die "Error connecting to database\n" unless defined $self->{dbh};
+ if ($self->{dbdriver} eq 'SQLite' && !use_fsync()) {
+ $self->{dbh}->do('PRAGMA synchronous = OFF');
+ }
$self->{tables} = {};
foreach my $table ( keys %{$self->{dbh}->table_info(undef,undef,undef,'TABLE')->fetchall_hashref('TABLE_NAME')} )
diff --git a/git-filter-branch.sh b/git-filter-branch.sh
index cb89372..3a51d45 100755
--- a/git-filter-branch.sh
+++ b/git-filter-branch.sh
@@ -579,7 +579,7 @@
git hash-object -t tag -w --stdin) ||
die "Could not create new tag object for $ref"
if git cat-file tag "$ref" | \
- sane_grep '^-----BEGIN PGP SIGNATURE-----' >/dev/null 2>&1
+ grep '^-----BEGIN PGP SIGNATURE-----' >/dev/null 2>&1
then
warn "gpg signature stripped from tag object $sha1t"
fi
diff --git a/git-instaweb.sh b/git-instaweb.sh
index 7c55229..4349566 100755
--- a/git-instaweb.sh
+++ b/git-instaweb.sh
@@ -49,7 +49,7 @@
*apache2*|*lighttpd*|*httpd*)
# yes, *httpd* covers *lighttpd* above, but it is there for clarity
# ensure that the apache2/lighttpd command ends with "-f"
- if ! echo "$httpd" | sane_grep -- '-f *$' >/dev/null 2>&1
+ if ! echo "$httpd" | grep -- '-f *$' >/dev/null 2>&1
then
httpd="$httpd -f"
fi
@@ -380,10 +380,7 @@
DirectoryIndex gitweb.cgi
EOF
- # check to see if Dennis Stosberg's mod_perl compatibility patch
- # (<20060621130708.Gcbc6e5c@leonov.stosberg.net>) has been applied
- if test -f "$module_path/mod_perl.so" &&
- sane_grep 'MOD_PERL' "$root/gitweb.cgi" >/dev/null
+ if test -f "$module_path/mod_perl.so"
then
# favor mod_perl if available
cat >> "$conf" <<EOF
@@ -402,7 +399,7 @@
# plain-old CGI
resolve_full_httpd
list_mods=$(echo "$full_httpd" | sed 's/-f$/-l/')
- $list_mods | sane_grep 'mod_cgi\.c' >/dev/null 2>&1 || \
+ $list_mods | grep 'mod_cgi\.c' >/dev/null 2>&1 || \
if test -f "$module_path/mod_cgi.so"
then
echo "LoadModule cgi_module $module_path/mod_cgi.so" >> "$conf"
diff --git a/git-p4.py b/git-p4.py
index 2b45002..cb37545 100755
--- a/git-p4.py
+++ b/git-p4.py
@@ -56,6 +56,21 @@
p4_access_checked = False
+re_ko_keywords = re.compile(br'\$(Id|Header)(:[^$\n]+)?\$')
+re_k_keywords = re.compile(br'\$(Id|Header|Author|Date|DateTime|Change|File|Revision)(:[^$\n]+)?\$')
+
+def format_size_human_readable(num):
+ """ Returns a number of units (typically bytes) formatted as a human-readable
+ string.
+ """
+ if num < 1024:
+ return '{:d} B'.format(num)
+ for unit in ["Ki", "Mi", "Gi", "Ti", "Pi", "Ei", "Zi"]:
+ num /= 1024.0
+ if num < 1024.0:
+ return "{:3.1f} {}B".format(num, unit)
+ return "{:.1f} YiB".format(num)
+
def p4_build_cmd(cmd):
"""Build a suitable p4 command line.
@@ -337,17 +352,19 @@
real_cmd = p4_build_cmd(c)
return read_pipe(real_cmd, ignore_error, raw=raw)
-def read_pipe_lines(c):
+def read_pipe_lines(c, raw=False):
if verbose:
sys.stderr.write('Reading pipe: %s\n' % str(c))
expand = not isinstance(c, list)
p = subprocess.Popen(c, stdout=subprocess.PIPE, shell=expand)
pipe = p.stdout
- val = [decode_text_stream(line) for line in pipe.readlines()]
+ lines = pipe.readlines()
+ if not raw:
+ lines = [decode_text_stream(line) for line in lines]
if pipe.close() or p.wait():
die('Command failed: %s' % str(c))
- return val
+ return lines
def p4_read_pipe_lines(c):
"""Specifically invoke p4 on the command supplied. """
@@ -577,20 +594,12 @@
#
def p4_keywords_regexp_for_type(base, type_mods):
if base in ("text", "unicode", "binary"):
- kwords = None
if "ko" in type_mods:
- kwords = 'Id|Header'
+ return re_ko_keywords
elif "k" in type_mods:
- kwords = 'Id|Header|Author|Date|DateTime|Change|File|Revision'
+ return re_k_keywords
else:
return None
- pattern = r"""
- \$ # Starts with a dollar, followed by...
- (%s) # one of the keywords, followed by...
- (:[^$\n]+)? # possibly an old expansion, followed by...
- \$ # another dollar
- """ % kwords
- return pattern
else:
return None
@@ -1532,80 +1541,6 @@
except IOError:
self.getUserMapFromPerforceServer()
-class P4Debug(Command):
- def __init__(self):
- Command.__init__(self)
- self.options = []
- self.description = "A tool to debug the output of p4 -G."
- self.needsGit = False
-
- def run(self, args):
- j = 0
- for output in p4CmdList(args):
- print('Element: %d' % j)
- j += 1
- print(output)
- return True
-
-class P4RollBack(Command):
- def __init__(self):
- Command.__init__(self)
- self.options = [
- optparse.make_option("--local", dest="rollbackLocalBranches", action="store_true")
- ]
- self.description = "A tool to debug the multi-branch import. Don't use :)"
- self.rollbackLocalBranches = False
-
- def run(self, args):
- if len(args) != 1:
- return False
- maxChange = int(args[0])
-
- if "p4ExitCode" in p4Cmd("changes -m 1"):
- die("Problems executing p4");
-
- if self.rollbackLocalBranches:
- refPrefix = "refs/heads/"
- lines = read_pipe_lines("git rev-parse --symbolic --branches")
- else:
- refPrefix = "refs/remotes/"
- lines = read_pipe_lines("git rev-parse --symbolic --remotes")
-
- for line in lines:
- if self.rollbackLocalBranches or (line.startswith("p4/") and line != "p4/HEAD\n"):
- line = line.strip()
- ref = refPrefix + line
- log = extractLogMessageFromGitCommit(ref)
- settings = extractSettingsGitLog(log)
-
- depotPaths = settings['depot-paths']
- change = settings['change']
-
- changed = False
-
- if len(p4Cmd("changes -m 1 " + ' '.join (['%s...@%s' % (p, maxChange)
- for p in depotPaths]))) == 0:
- print("Branch %s did not exist at change %s, deleting." % (ref, maxChange))
- system("git update-ref -d %s `git rev-parse %s`" % (ref, ref))
- continue
-
- while change and int(change) > maxChange:
- changed = True
- if self.verbose:
- print("%s is at %s ; rewinding towards %s" % (ref, change, maxChange))
- system("git update-ref %s \"%s^\"" % (ref, ref))
- log = extractLogMessageFromGitCommit(ref)
- settings = extractSettingsGitLog(log)
-
-
- depotPaths = settings['depot-paths']
- change = settings['change']
-
- if changed:
- print("%s rewound to %s" % (ref, change))
-
- return True
-
class P4Submit(Command, P4UserMap):
conflict_behavior_choices = ("ask", "skip", "quit")
@@ -1753,18 +1688,13 @@
return result
- def patchRCSKeywords(self, file, pattern):
- # Attempt to zap the RCS keywords in a p4 controlled file matching the given pattern
+ def patchRCSKeywords(self, file, regexp):
+ # Attempt to zap the RCS keywords in a p4 controlled file matching the given regex
(handle, outFileName) = tempfile.mkstemp(dir='.')
try:
- outFile = os.fdopen(handle, "w+")
- inFile = open(file, "r")
- regexp = re.compile(pattern, re.VERBOSE)
- for line in inFile.readlines():
- line = regexp.sub(r'$\1$', line)
- outFile.write(line)
- inFile.close()
- outFile.close()
+ with os.fdopen(handle, "wb") as outFile, open(file, "rb") as inFile:
+ for line in inFile.readlines():
+ outFile.write(regexp.sub(br'$\1$', line))
# Forcibly overwrite the original file
os.unlink(file)
shutil.move(outFileName, file)
@@ -2091,25 +2021,24 @@
# the patch to see if that's possible.
if gitConfigBool("git-p4.attemptRCSCleanup"):
file = None
- pattern = None
kwfiles = {}
for file in editedFiles | filesToDelete:
# did this file's delta contain RCS keywords?
- pattern = p4_keywords_regexp_for_file(file)
-
- if pattern:
+ regexp = p4_keywords_regexp_for_file(file)
+ if regexp:
# this file is a possibility...look for RCS keywords.
- regexp = re.compile(pattern, re.VERBOSE)
- for line in read_pipe_lines(["git", "diff", "%s^..%s" % (id, id), file]):
+ for line in read_pipe_lines(
+ ["git", "diff", "%s^..%s" % (id, id), file],
+ raw=True):
if regexp.search(line):
if verbose:
- print("got keyword match on %s in %s in %s" % (pattern, line, file))
- kwfiles[file] = pattern
+ print("got keyword match on %s in %s in %s" % (regex.pattern, line, file))
+ kwfiles[file] = regexp
break
- for file in kwfiles:
+ for file, regexp in kwfiles.items():
if verbose:
- print("zapping %s with %s" % (line,pattern))
+ print("zapping %s with %s" % (line, regexp.pattern))
# File is being deleted, so not open in p4. Must
# disable the read-only bit on windows.
if self.isWindows and file not in editedFiles:
@@ -2966,7 +2895,8 @@
size = int(self.stream_file['fileSize'])
else:
size = 0 # deleted files don't get a fileSize apparently
- sys.stdout.write('\r%s --> %s (%i MB)\n' % (file_path, relPath, size/1024/1024))
+ sys.stdout.write('\r%s --> %s (%s)\n' % (
+ file_path, relPath, format_size_human_readable(size)))
sys.stdout.flush()
(type_base, type_mods) = split_p4_type(file["type"])
@@ -3029,12 +2959,9 @@
# Note that we do not try to de-mangle keywords on utf16 files,
# even though in theory somebody may want that.
- pattern = p4_keywords_regexp_for_type(type_base, type_mods)
- if pattern:
- regexp = re.compile(pattern, re.VERBOSE)
- text = ''.join(decode_text_stream(c) for c in contents)
- text = regexp.sub(r'$\1$', text)
- contents = [ encode_text_stream(text) ]
+ regexp = p4_keywords_regexp_for_type(type_base, type_mods)
+ if regexp:
+ contents = [regexp.sub(br'$\1$', c) for c in contents]
if self.largeFileSystem:
(git_mode, contents) = self.largeFileSystem.processContent(git_mode, relPath, contents)
@@ -3064,9 +2991,8 @@
if not err and 'fileSize' in self.stream_file:
required_bytes = int((4 * int(self.stream_file["fileSize"])) - calcDiskFree())
if required_bytes > 0:
- err = 'Not enough space left on %s! Free at least %i MB.' % (
- os.getcwd(), required_bytes/1024/1024
- )
+ err = 'Not enough space left on %s! Free at least %s.' % (
+ os.getcwd(), format_size_human_readable(required_bytes))
if err:
f = None
@@ -3110,7 +3036,9 @@
size = int(self.stream_file["fileSize"])
if size > 0:
progress = 100*self.stream_file['streamContentSize']/size
- sys.stdout.write('\r%s %d%% (%i MB)' % (self.stream_file['depotFile'], progress, int(size/1024/1024)))
+ sys.stdout.write('\r%s %d%% (%s)' % (
+ self.stream_file['depotFile'], progress,
+ format_size_human_readable(size)))
sys.stdout.flush()
self.stream_have_file_info = True
@@ -3623,7 +3551,8 @@
self.updateOptionDict(description)
if not self.silent:
- sys.stdout.write("\rImporting revision %s (%s%%)" % (change, cnt * 100 / len(changes)))
+ sys.stdout.write("\rImporting revision %s (%d%%)" % (
+ change, (cnt * 100) // len(changes)))
sys.stdout.flush()
cnt = cnt + 1
@@ -4363,13 +4292,11 @@
print("")
commands = {
- "debug" : P4Debug,
"submit" : P4Submit,
"commit" : P4Submit,
"sync" : P4Sync,
"rebase" : P4Rebase,
"clone" : P4Clone,
- "rollback" : P4RollBack,
"branches" : P4Branches,
"unshelve" : P4Unshelve,
}
diff --git a/git-send-email.perl b/git-send-email.perl
index 5262d88..0408722 100755
--- a/git-send-email.perl
+++ b/git-send-email.perl
@@ -40,7 +40,8 @@
sub usage {
print <<EOT;
-git send-email [options] <file | directory | rev-list options >
+git send-email' [<options>] <file|directory>
+git send-email' [<options>] <format-patch options>
git send-email --dump-aliases
Composing:
@@ -113,9 +114,38 @@
exit(1);
}
+sub uniq {
+ my %seen;
+ grep !$seen{$_}++, @_;
+}
+
sub completion_helper {
- print Git::command('format-patch', '--git-completion-helper');
- exit(0);
+ my ($original_opts) = @_;
+ my %not_for_completion = (
+ "git-completion-helper" => undef,
+ "h" => undef,
+ );
+ my @send_email_opts = ();
+
+ foreach my $key (keys %$original_opts) {
+ unless (exists $not_for_completion{$key}) {
+ $key =~ s/!$//;
+
+ if ($key =~ /[:=][si]$/) {
+ $key =~ s/[:=][si]$//;
+ push (@send_email_opts, "--$_=") foreach (split (/\|/, $key));
+ } else {
+ push (@send_email_opts, "--$_") foreach (split (/\|/, $key));
+ }
+ }
+ }
+
+ my @format_patch_opts = split(/ /, Git::command('format-patch', '--git-completion-helper'));
+ my @opts = (@send_email_opts, @format_patch_opts);
+ @opts = uniq (grep !/^$/, @opts);
+ # There's an implicit '\n' here already, no need to add an explicit one.
+ print "@opts";
+ exit(0);
}
# most mail servers generate the Date: header, but not all...
@@ -425,10 +455,11 @@
my $key = "sendemail.identity";
$identity = Git::config(@repo, $key) if exists $known_config_keys{$key};
}
-my $rc = GetOptions(
+my %identity_options = (
"identity=s" => \$identity,
"no-identity" => \$no_identity,
);
+my $rc = GetOptions(%identity_options);
usage() unless $rc;
undef $identity if $no_identity;
@@ -444,14 +475,17 @@
my $help;
my $git_completion_helper;
-$rc = GetOptions("h" => \$help,
- "dump-aliases" => \$dump_aliases);
+my %dump_aliases_options = (
+ "h" => \$help,
+ "dump-aliases" => \$dump_aliases,
+);
+$rc = GetOptions(%dump_aliases_options);
usage() unless $rc;
die __("--dump-aliases incompatible with other options\n")
if !$help and $dump_aliases and @ARGV;
-$rc = GetOptions(
+my %options = (
"sender|from=s" => \$sender,
- "in-reply-to=s" => \$initial_in_reply_to,
+ "in-reply-to=s" => \$initial_in_reply_to,
"reply-to=s" => \$reply_to,
"subject=s" => \$initial_subject,
"to=s" => \@getopt_to,
@@ -508,7 +542,8 @@
"batch-size=i" => \$batch_size,
"relogin-delay=i" => \$relogin_delay,
"git-completion-helper" => \$git_completion_helper,
- );
+);
+$rc = GetOptions(%options);
# Munge any "either config or getopt, not both" variables
my @initial_to = @getopt_to ? @getopt_to : ($no_to ? () : @config_to);
@@ -516,7 +551,8 @@
my @initial_bcc = @getopt_bcc ? @getopt_bcc : ($no_bcc ? () : @config_bcc);
usage() if $help;
-completion_helper() if $git_completion_helper;
+my %all_options = (%options, %dump_aliases_options, %identity_options);
+completion_helper(\%all_options) if $git_completion_helper;
unless ($rc) {
usage();
}
diff --git a/git-sh-setup.sh b/git-sh-setup.sh
index 960982f..b93f392 100644
--- a/git-sh-setup.sh
+++ b/git-sh-setup.sh
@@ -173,14 +173,6 @@
eval "$GIT_PAGER" '"$@"'
}
-sane_grep () {
- GREP_OPTIONS= LC_ALL=C grep @@SANE_TEXT_GREP@@ "$@"
-}
-
-sane_egrep () {
- GREP_OPTIONS= LC_ALL=C egrep @@SANE_TEXT_GREP@@ "$@"
-}
-
is_bare_repository () {
git rev-parse --is-bare-repository
}
diff --git a/git.c b/git.c
index 5ff21be..edda922 100644
--- a/git.c
+++ b/git.c
@@ -185,7 +185,7 @@
*envchanged = 1;
} else if (!strcmp(cmd, "--git-dir")) {
if (*argc < 2) {
- fprintf(stderr, _("no directory given for --git-dir\n" ));
+ fprintf(stderr, _("no directory given for '%s' option\n" ), "--git-dir");
usage(git_usage_string);
}
setenv(GIT_DIR_ENVIRONMENT, (*argv)[1], 1);
@@ -213,7 +213,7 @@
*envchanged = 1;
} else if (!strcmp(cmd, "--work-tree")) {
if (*argc < 2) {
- fprintf(stderr, _("no directory given for --work-tree\n" ));
+ fprintf(stderr, _("no directory given for '%s' option\n" ), "--work-tree");
usage(git_usage_string);
}
setenv(GIT_WORK_TREE_ENVIRONMENT, (*argv)[1], 1);
@@ -297,7 +297,7 @@
*envchanged = 1;
} else if (!strcmp(cmd, "-C")) {
if (*argc < 2) {
- fprintf(stderr, _("no directory given for -C\n" ));
+ fprintf(stderr, _("no directory given for '%s' option\n" ), "-C");
usage(git_usage_string);
}
if ((*argv)[1][0]) {
@@ -421,27 +421,30 @@
int status, help;
struct stat st;
const char *prefix;
+ int run_setup = (p->option & (RUN_SETUP | RUN_SETUP_GENTLY));
- prefix = NULL;
help = argc == 2 && !strcmp(argv[1], "-h");
- if (!help) {
- if (p->option & RUN_SETUP)
- prefix = setup_git_directory();
- else if (p->option & RUN_SETUP_GENTLY) {
- int nongit_ok;
- prefix = setup_git_directory_gently(&nongit_ok);
- }
- precompose_argv_prefix(argc, argv, NULL);
- if (use_pager == -1 && p->option & (RUN_SETUP | RUN_SETUP_GENTLY) &&
- !(p->option & DELAY_PAGER_CONFIG))
- use_pager = check_pager_config(p->cmd);
- if (use_pager == -1 && p->option & USE_PAGER)
- use_pager = 1;
+ if (help && (run_setup & RUN_SETUP))
+ /* demote to GENTLY to allow 'git cmd -h' outside repo */
+ run_setup = RUN_SETUP_GENTLY;
- if ((p->option & (RUN_SETUP | RUN_SETUP_GENTLY)) &&
- startup_info->have_repository) /* get_git_dir() may set up repo, avoid that */
- trace_repo_setup(prefix);
+ if (run_setup & RUN_SETUP) {
+ prefix = setup_git_directory();
+ } else if (run_setup & RUN_SETUP_GENTLY) {
+ int nongit_ok;
+ prefix = setup_git_directory_gently(&nongit_ok);
+ } else {
+ prefix = NULL;
}
+ precompose_argv_prefix(argc, argv, NULL);
+ if (use_pager == -1 && run_setup &&
+ !(p->option & DELAY_PAGER_CONFIG))
+ use_pager = check_pager_config(p->cmd);
+ if (use_pager == -1 && p->option & USE_PAGER)
+ use_pager = 1;
+ if (run_setup && startup_info->have_repository)
+ /* get_git_dir() may set up repo, avoid that */
+ trace_repo_setup(prefix);
commit_pager_choice();
if (!help && get_super_prefix()) {
diff --git a/gpg-interface.c b/gpg-interface.c
index 3e7255a..b52eb0e 100644
--- a/gpg-interface.c
+++ b/gpg-interface.c
@@ -19,8 +19,8 @@
const char **verify_args;
const char **sigs;
int (*verify_signed_buffer)(struct signature_check *sigc,
- struct gpg_format *fmt, const char *payload,
- size_t payload_size, const char *signature,
+ struct gpg_format *fmt,
+ const char *signature,
size_t signature_size);
int (*sign_buffer)(struct strbuf *buffer, struct strbuf *signature,
const char *signing_key);
@@ -53,12 +53,12 @@
};
static int verify_gpg_signed_buffer(struct signature_check *sigc,
- struct gpg_format *fmt, const char *payload,
- size_t payload_size, const char *signature,
+ struct gpg_format *fmt,
+ const char *signature,
size_t signature_size);
static int verify_ssh_signed_buffer(struct signature_check *sigc,
- struct gpg_format *fmt, const char *payload,
- size_t payload_size, const char *signature,
+ struct gpg_format *fmt,
+ const char *signature,
size_t signature_size);
static int sign_buffer_gpg(struct strbuf *buffer, struct strbuf *signature,
const char *signing_key);
@@ -314,8 +314,8 @@
}
static int verify_gpg_signed_buffer(struct signature_check *sigc,
- struct gpg_format *fmt, const char *payload,
- size_t payload_size, const char *signature,
+ struct gpg_format *fmt,
+ const char *signature,
size_t signature_size)
{
struct child_process gpg = CHILD_PROCESS_INIT;
@@ -343,14 +343,13 @@
NULL);
sigchain_push(SIGPIPE, SIG_IGN);
- ret = pipe_command(&gpg, payload, payload_size, &gpg_stdout, 0,
+ ret = pipe_command(&gpg, sigc->payload, sigc->payload_len, &gpg_stdout, 0,
&gpg_stderr, 0);
sigchain_pop(SIGPIPE);
delete_tempfile(&temp);
ret |= !strstr(gpg_stdout.buf, "\n[GNUPG:] GOODSIG ");
- sigc->payload = xmemdupz(payload, payload_size);
sigc->output = strbuf_detach(&gpg_stderr, NULL);
sigc->gpg_status = strbuf_detach(&gpg_stdout, NULL);
@@ -426,8 +425,8 @@
}
static int verify_ssh_signed_buffer(struct signature_check *sigc,
- struct gpg_format *fmt, const char *payload,
- size_t payload_size, const char *signature,
+ struct gpg_format *fmt,
+ const char *signature,
size_t signature_size)
{
struct child_process ssh_keygen = CHILD_PROCESS_INIT;
@@ -440,6 +439,13 @@
struct strbuf ssh_principals_err = STRBUF_INIT;
struct strbuf ssh_keygen_out = STRBUF_INIT;
struct strbuf ssh_keygen_err = STRBUF_INIT;
+ struct strbuf verify_time = STRBUF_INIT;
+ const struct date_mode verify_date_mode = {
+ .type = DATE_STRFTIME,
+ .strftime_fmt = "%Y%m%d%H%M%S",
+ /* SSH signing key validity has no timezone information - Use the local timezone */
+ .local = 1,
+ };
if (!ssh_allowed_signers) {
error(_("gpg.ssh.allowedSignersFile needs to be configured and exist for ssh signature verification"));
@@ -457,11 +463,16 @@
return -1;
}
+ if (sigc->payload_timestamp)
+ strbuf_addf(&verify_time, "-Overify-time=%s",
+ show_date(sigc->payload_timestamp, 0, &verify_date_mode));
+
/* Find the principal from the signers */
strvec_pushl(&ssh_keygen.args, fmt->program,
"-Y", "find-principals",
"-f", ssh_allowed_signers,
"-s", buffer_file->filename.buf,
+ verify_time.buf,
NULL);
ret = pipe_command(&ssh_keygen, NULL, 0, &ssh_principals_out, 0,
&ssh_principals_err, 0);
@@ -479,8 +490,9 @@
"-Y", "check-novalidate",
"-n", "git",
"-s", buffer_file->filename.buf,
+ verify_time.buf,
NULL);
- pipe_command(&ssh_keygen, payload, payload_size,
+ pipe_command(&ssh_keygen, sigc->payload, sigc->payload_len,
&ssh_keygen_out, 0, &ssh_keygen_err, 0);
/*
@@ -513,6 +525,7 @@
"-f", ssh_allowed_signers,
"-I", principal,
"-s", buffer_file->filename.buf,
+ verify_time.buf,
NULL);
if (ssh_revocation_file) {
@@ -526,7 +539,7 @@
}
sigchain_push(SIGPIPE, SIG_IGN);
- ret = pipe_command(&ssh_keygen, payload, payload_size,
+ ret = pipe_command(&ssh_keygen, sigc->payload, sigc->payload_len,
&ssh_keygen_out, 0, &ssh_keygen_err, 0);
sigchain_pop(SIGPIPE);
@@ -540,7 +553,6 @@
}
}
- sigc->payload = xmemdupz(payload, payload_size);
strbuf_stripspace(&ssh_keygen_out, 0);
strbuf_stripspace(&ssh_keygen_err, 0);
/* Add stderr outputs to show the user actual ssh-keygen errors */
@@ -558,12 +570,48 @@
strbuf_release(&ssh_principals_err);
strbuf_release(&ssh_keygen_out);
strbuf_release(&ssh_keygen_err);
+ strbuf_release(&verify_time);
return ret;
}
-int check_signature(const char *payload, size_t plen, const char *signature,
- size_t slen, struct signature_check *sigc)
+static int parse_payload_metadata(struct signature_check *sigc)
+{
+ const char *ident_line = NULL;
+ size_t ident_len;
+ struct ident_split ident;
+ const char *signer_header;
+
+ switch (sigc->payload_type) {
+ case SIGNATURE_PAYLOAD_COMMIT:
+ signer_header = "committer";
+ break;
+ case SIGNATURE_PAYLOAD_TAG:
+ signer_header = "tagger";
+ break;
+ case SIGNATURE_PAYLOAD_UNDEFINED:
+ case SIGNATURE_PAYLOAD_PUSH_CERT:
+ /* Ignore payloads we don't want to parse */
+ return 0;
+ default:
+ BUG("invalid value for sigc->payload_type");
+ }
+
+ ident_line = find_commit_header(sigc->payload, signer_header, &ident_len);
+ if (!ident_line || !ident_len)
+ return 1;
+
+ if (split_ident_line(&ident, ident_line, ident_len))
+ return 1;
+
+ if (!sigc->payload_timestamp && ident.date_begin && ident.date_end)
+ sigc->payload_timestamp = parse_timestamp(ident.date_begin, NULL, 10);
+
+ return 0;
+}
+
+int check_signature(struct signature_check *sigc,
+ const char *signature, size_t slen)
{
struct gpg_format *fmt;
int status;
@@ -575,8 +623,10 @@
if (!fmt)
die(_("bad/incompatible signature '%s'"), signature);
- status = fmt->verify_signed_buffer(sigc, fmt, payload, plen, signature,
- slen);
+ if (parse_payload_metadata(sigc))
+ return 1;
+
+ status = fmt->verify_signed_buffer(sigc, fmt, signature, slen);
if (status && !sigc->output)
return !!status;
@@ -593,7 +643,7 @@
sigc->output;
if (flags & GPG_VERIFY_VERBOSE && sigc->payload)
- fputs(sigc->payload, stdout);
+ fwrite(sigc->payload, 1, sigc->payload_len, stdout);
if (output)
fputs(output, stderr);
@@ -707,6 +757,21 @@
return 0;
}
+/*
+ * Returns 1 if `string` contains a literal ssh key, 0 otherwise
+ * `key` will be set to the start of the actual key if a prefix is present.
+ */
+static int is_literal_ssh_key(const char *string, const char **key)
+{
+ if (skip_prefix(string, "key::", key))
+ return 1;
+ if (starts_with(string, "ssh-")) {
+ *key = string;
+ return 1;
+ }
+ return 0;
+}
+
static char *get_ssh_key_fingerprint(const char *signing_key)
{
struct child_process ssh_keygen = CHILD_PROCESS_INIT;
@@ -714,15 +779,16 @@
struct strbuf fingerprint_stdout = STRBUF_INIT;
struct strbuf **fingerprint;
char *fingerprint_ret;
+ const char *literal_key = NULL;
/*
* With SSH Signing this can contain a filename or a public key
* For textual representation we usually want a fingerprint
*/
- if (starts_with(signing_key, "ssh-")) {
+ if (is_literal_ssh_key(signing_key, &literal_key)) {
strvec_pushl(&ssh_keygen.args, "ssh-keygen", "-lf", "-", NULL);
- ret = pipe_command(&ssh_keygen, signing_key,
- strlen(signing_key), &fingerprint_stdout, 0,
+ ret = pipe_command(&ssh_keygen, literal_key,
+ strlen(literal_key), &fingerprint_stdout, 0,
NULL, 0);
} else {
strvec_pushl(&ssh_keygen.args, "ssh-keygen", "-lf",
@@ -757,6 +823,7 @@
const char **argv;
int n;
char *default_key = NULL;
+ const char *literal_key = NULL;
if (!ssh_default_key_command)
die(_("either user.signingkey or gpg.ssh.defaultKeyCommand needs to be configured"));
@@ -774,7 +841,11 @@
if (!ret) {
keys = strbuf_split_max(&key_stdout, '\n', 2);
- if (keys[0] && starts_with(keys[0]->buf, "ssh-")) {
+ if (keys[0] && is_literal_ssh_key(keys[0]->buf, &literal_key)) {
+ /*
+ * We only use `is_literal_ssh_key` here to check validity
+ * The prefix will be stripped when the key is used.
+ */
default_key = strbuf_detach(keys[0], NULL);
} else {
warning(_("gpg.ssh.defaultKeyCommand succeeded but returned no keys: %s %s"),
@@ -889,19 +960,20 @@
struct tempfile *key_file = NULL, *buffer_file = NULL;
char *ssh_signing_key_file = NULL;
struct strbuf ssh_signature_filename = STRBUF_INIT;
+ const char *literal_key = NULL;
if (!signing_key || signing_key[0] == '\0')
return error(
_("user.signingkey needs to be set for ssh signing"));
- if (starts_with(signing_key, "ssh-")) {
+ if (is_literal_ssh_key(signing_key, &literal_key)) {
/* A literal ssh key */
key_file = mks_tempfile_t(".git_signing_key_tmpXXXXXX");
if (!key_file)
return error_errno(
_("could not create temporary file"));
- keylen = strlen(signing_key);
- if (write_in_full(key_file->fd, signing_key, keylen) < 0 ||
+ keylen = strlen(literal_key);
+ if (write_in_full(key_file->fd, literal_key, keylen) < 0 ||
close_tempfile_gently(key_file) < 0) {
error_errno(_("failed writing ssh signing key to '%s'"),
key_file->filename.buf);
diff --git a/gpg-interface.h b/gpg-interface.h
index beefacb..b30cbdc 100644
--- a/gpg-interface.h
+++ b/gpg-interface.h
@@ -15,8 +15,18 @@
TRUST_ULTIMATE,
};
+enum payload_type {
+ SIGNATURE_PAYLOAD_UNDEFINED,
+ SIGNATURE_PAYLOAD_COMMIT,
+ SIGNATURE_PAYLOAD_TAG,
+ SIGNATURE_PAYLOAD_PUSH_CERT,
+};
+
struct signature_check {
char *payload;
+ size_t payload_len;
+ enum payload_type payload_type;
+ timestamp_t payload_timestamp;
char *output;
char *gpg_status;
@@ -70,9 +80,8 @@
* Either a GPG KeyID or a SSH Key Fingerprint
*/
const char *get_signing_key_id(void);
-int check_signature(const char *payload, size_t plen,
- const char *signature, size_t slen,
- struct signature_check *sigc);
+int check_signature(struct signature_check *sigc,
+ const char *signature, size_t slen);
void print_signature_buffer(const struct signature_check *sigc,
unsigned flags);
diff --git a/grep.c b/grep.c
index fe847a0..7bb0360 100644
--- a/grep.c
+++ b/grep.c
@@ -26,10 +26,10 @@
.pattern_type_option = GREP_PATTERN_TYPE_UNSPECIFIED,
.colors = {
[GREP_COLOR_CONTEXT] = "",
- [GREP_COLOR_FILENAME] = "",
+ [GREP_COLOR_FILENAME] = GIT_COLOR_MAGENTA,
[GREP_COLOR_FUNCTION] = "",
- [GREP_COLOR_LINENO] = "",
- [GREP_COLOR_COLUMNNO] = "",
+ [GREP_COLOR_LINENO] = GIT_COLOR_GREEN,
+ [GREP_COLOR_COLUMNNO] = GIT_COLOR_GREEN,
[GREP_COLOR_MATCH_CONTEXT] = GIT_COLOR_BOLD_RED,
[GREP_COLOR_MATCH_SELECTED] = GIT_COLOR_BOLD_RED,
[GREP_COLOR_SELECTED] = "",
@@ -362,6 +362,7 @@
int jitret;
int patinforet;
size_t jitsizearg;
+ int literal = !opt->ignore_case && (p->fixed || p->is_fixed);
/*
* Call pcre2_general_context_create() before calling any
@@ -382,8 +383,7 @@
}
options |= PCRE2_CASELESS;
}
- if (!opt->ignore_locale && is_utf8_locale() && has_non_ascii(p->pattern) &&
- !(!opt->ignore_case && (p->fixed || p->is_fixed)))
+ if (!opt->ignore_locale && is_utf8_locale() && !literal)
options |= (PCRE2_UTF | PCRE2_MATCH_INVALID_UTF);
#ifdef GIT_PCRE2_VERSION_10_36_OR_HIGHER
@@ -699,6 +699,14 @@
return compile_pattern_or(list);
}
+static struct grep_expr *grep_not_expr(struct grep_expr *expr)
+{
+ struct grep_expr *z = xcalloc(1, sizeof(*z));
+ z->node = GREP_NODE_NOT;
+ z->u.unary = expr;
+ return z;
+}
+
static struct grep_expr *grep_true_expr(void)
{
struct grep_expr *z = xcalloc(1, sizeof(*z));
@@ -797,7 +805,7 @@
}
}
- if (opt->all_match || header_expr)
+ if (opt->all_match || opt->no_body_match || header_expr)
opt->extended = 1;
else if (!opt->extended)
return;
@@ -808,6 +816,9 @@
if (p)
die("incomplete pattern expression: %s", p->pattern);
+ if (opt->no_body_match && opt->pattern_expression)
+ opt->pattern_expression = grep_not_expr(opt->pattern_expression);
+
if (!header_expr)
return;
@@ -1057,6 +1068,8 @@
if (h && (*col < 0 || tmp.rm_so < *col))
*col = tmp.rm_so;
}
+ if (x->u.atom->token == GREP_PATTERN_BODY)
+ opt->body_hit |= h;
break;
case GREP_NODE_NOT:
/*
@@ -1825,16 +1838,19 @@
* we do not have to do the two-pass grep when we do not check
* buffer-wide "all-match".
*/
- if (!opt->all_match)
+ if (!opt->all_match && !opt->no_body_match)
return grep_source_1(opt, gs, 0);
/* Otherwise the toplevel "or" terms hit a bit differently.
* We first clear hit markers from them.
*/
clr_hit_marker(opt->pattern_expression);
+ opt->body_hit = 0;
grep_source_1(opt, gs, 1);
- if (!chk_hit_marker(opt->pattern_expression))
+ if (opt->all_match && !chk_hit_marker(opt->pattern_expression))
+ return 0;
+ if (opt->no_body_match && opt->body_hit)
return 0;
return grep_source_1(opt, gs, 0);
diff --git a/grep.h b/grep.h
index 3e8815c..6a1f0ab 100644
--- a/grep.h
+++ b/grep.h
@@ -148,6 +148,8 @@
int word_regexp;
int fixed;
int all_match;
+ int no_body_match;
+ int body_hit;
#define GREP_BINARY_DEFAULT 0
#define GREP_BINARY_NOMATCH 1
#define GREP_BINARY_TEXT 2
diff --git a/hash.h b/hash.h
index 9e25c40..5d40368 100644
--- a/hash.h
+++ b/hash.h
@@ -95,12 +95,18 @@
/* Number of algorithms supported (including unknown). */
#define GIT_HASH_NALGOS (GIT_HASH_SHA256 + 1)
+/* "sha1", big-endian */
+#define GIT_SHA1_FORMAT_ID 0x73686131
+
/* The length in bytes and in hex digits of an object name (SHA-1 value). */
#define GIT_SHA1_RAWSZ 20
#define GIT_SHA1_HEXSZ (2 * GIT_SHA1_RAWSZ)
/* The block size of SHA-1. */
#define GIT_SHA1_BLKSZ 64
+/* "s256", big-endian */
+#define GIT_SHA256_FORMAT_ID 0x73323536
+
/* The length in bytes and in hex digits of an object name (SHA-256 value). */
#define GIT_SHA256_RAWSZ 32
#define GIT_SHA256_HEXSZ (2 * GIT_SHA256_RAWSZ)
diff --git a/help.c b/help.c
index 973e47c..7144490 100644
--- a/help.c
+++ b/help.c
@@ -643,7 +643,7 @@
else if (autocorrect == AUTOCORRECT_PROMPT) {
char *answer;
struct strbuf msg = STRBUF_INIT;
- strbuf_addf(&msg, _("Run '%s' instead? (y/N)"), assumed);
+ strbuf_addf(&msg, _("Run '%s' instead [y/N]? "), assumed);
answer = git_prompt(msg.buf, PROMPT_ECHO);
strbuf_release(&msg);
if (!(starts_with(answer, "y") ||
diff --git a/http-backend.c b/http-backend.c
index 3d6e2ff..807fb88 100644
--- a/http-backend.c
+++ b/http-backend.c
@@ -480,7 +480,7 @@
strvec_pushf(&cld.env_array,
"GIT_COMMITTER_EMAIL=%s@http.%s", user, host);
- cld.argv = argv;
+ strvec_pushv(&cld.args, argv);
if (buffer_input || gzipped_request || req_len >= 0)
cld.in = -1;
cld.git_cmd = 1;
@@ -659,8 +659,9 @@
{
if (dead <= 1) {
struct strbuf hdr = STRBUF_INIT;
+ report_fn die_message_fn = get_die_message_routine();
- vreportf("fatal: ", err, params);
+ die_message_fn(err, params);
http_status(&hdr, 500, "Internal Server Error");
hdr_nocache(&hdr);
diff --git a/http-fetch.c b/http-fetch.c
index fa64246..58b394c 100644
--- a/http-fetch.c
+++ b/http-fetch.c
@@ -4,6 +4,7 @@
#include "http.h"
#include "walker.h"
#include "strvec.h"
+#include "urlmatch.h"
static const char http_fetch_usage[] = "git http-fetch "
"[-c] [-t] [-a] [-v] [--recover] [-w ref] [--stdin | --packfile=hash | commit-id] url";
@@ -63,8 +64,17 @@
if (start_active_slot(preq->slot)) {
run_active_slot(preq->slot);
if (results.curl_result != CURLE_OK) {
- die("Unable to get pack file %s\n%s", preq->url,
- curl_errorstr);
+ struct url_info url;
+ char *nurl = url_normalize(preq->url, &url);
+ if (!nurl || !git_env_bool("GIT_TRACE_REDACT", 1)) {
+ die("unable to get pack file '%s'\n%s", preq->url,
+ curl_errorstr);
+ } else {
+ die("failed to get '%.*s' url from '%.*s' "
+ "(full URL redacted due to GIT_TRACE_REDACT setting)\n%s",
+ (int)url.scheme_len, url.url,
+ (int)url.host_len, &url.url[url.host_off], curl_errorstr);
+ }
}
} else {
die("Unable to start request");
@@ -131,7 +141,7 @@
if (packfile) {
if (!index_pack_args.nr)
- die(_("--packfile requires --index-pack-args"));
+ die(_("the option '%s' requires '%s'"), "--packfile", "--index-pack-args");
fetch_single_packfile(&packfile_hash, argv[arg],
index_pack_args.v);
@@ -140,7 +150,7 @@
}
if (index_pack_args.nr)
- die(_("--index-pack-args can only be used with --packfile"));
+ die(_("the option '%s' requires '%s'"), "--index-pack-args", "--packfile");
if (commits_on_stdin) {
commits = walker_targets_stdin(&commit_id, &write_ref);
diff --git a/http.c b/http.c
index f92859f..229da4d 100644
--- a/http.c
+++ b/http.c
@@ -2126,8 +2126,9 @@
ip.git_cmd = 1;
ip.in = tmpfile_fd;
- ip.argv = preq->index_pack_args ? preq->index_pack_args
- : default_index_pack_args;
+ strvec_pushv(&ip.args, preq->index_pack_args ?
+ preq->index_pack_args :
+ default_index_pack_args);
if (preq->preserve_index_pack_stdout)
ip.out = 0;
diff --git a/log-tree.c b/log-tree.c
index 644893f..d3e7a40 100644
--- a/log-tree.c
+++ b/log-tree.c
@@ -513,8 +513,9 @@
if (parse_signed_commit(commit, &payload, &signature, the_hash_algo) <= 0)
goto out;
- status = check_signature(payload.buf, payload.len, signature.buf,
- signature.len, &sigc);
+ sigc.payload_type = SIGNATURE_PAYLOAD_COMMIT;
+ sigc.payload = strbuf_detach(&payload, &sigc.payload_len);
+ status = check_signature(&sigc, signature.buf, signature.len);
if (status && !sigc.output)
show_sig_lines(opt, status, "No signature\n");
else
@@ -583,8 +584,9 @@
status = -1;
if (parse_signature(extra->value, extra->len, &payload, &signature)) {
/* could have a good signature */
- status = check_signature(payload.buf, payload.len,
- signature.buf, signature.len, &sigc);
+ sigc.payload_type = SIGNATURE_PAYLOAD_TAG;
+ sigc.payload = strbuf_detach(&payload, &sigc.payload_len);
+ status = check_signature(&sigc, signature.buf, signature.len);
if (sigc.output)
strbuf_addstr(&verify_message, sigc.output);
else
diff --git a/merge-ort.c b/merge-ort.c
index 0342f10..c319797 100644
--- a/merge-ort.c
+++ b/merge-ort.c
@@ -3841,9 +3841,22 @@
if (opt->renormalize &&
blob_unchanged(opt, &ci->stages[0], &ci->stages[side],
path)) {
- ci->merged.is_null = 1;
- ci->merged.clean = 1;
- assert(!ci->df_conflict && !ci->path_conflict);
+ if (!ci->path_conflict) {
+ /*
+ * Blob unchanged after renormalization, so
+ * there's no modify/delete conflict after all;
+ * we can just remove the file.
+ */
+ ci->merged.is_null = 1;
+ ci->merged.clean = 1;
+ /*
+ * file goes away => even if there was a
+ * directory/file conflict there isn't one now.
+ */
+ ci->df_conflict = 0;
+ } else {
+ /* rename/delete, so conflict remains */
+ }
} else if (ci->path_conflict &&
oideq(&ci->stages[0].oid, &ci->stages[side].oid)) {
/*
diff --git a/mergesort.c b/mergesort.c
index 6216835..bd9c6ef 100644
--- a/mergesort.c
+++ b/mergesort.c
@@ -63,7 +63,7 @@
void *next = get_next_fn(list);
if (next)
set_next_fn(list, NULL);
- for (i = 0; n & (1 << i); i++)
+ for (i = 0; n & ((size_t)1 << i); i++)
list = llist_merge(ranks[i], list, get_next_fn,
set_next_fn, compare_fn);
n++;
diff --git a/midx.c b/midx.c
index 8433086..837b46b 100644
--- a/midx.c
+++ b/midx.c
@@ -57,15 +57,15 @@
return m->data + m->data_len - the_hash_algo->rawsz;
}
-char *get_midx_filename(const char *object_dir)
+void get_midx_filename(struct strbuf *out, const char *object_dir)
{
- return xstrfmt("%s/pack/multi-pack-index", object_dir);
+ strbuf_addf(out, "%s/pack/multi-pack-index", object_dir);
}
-char *get_midx_rev_filename(struct multi_pack_index *m)
+void get_midx_rev_filename(struct strbuf *out, struct multi_pack_index *m)
{
- return xstrfmt("%s/pack/multi-pack-index-%s.rev",
- m->object_dir, hash_to_hex(get_midx_checksum(m)));
+ get_midx_filename(out, m->object_dir);
+ strbuf_addf(out, "-%s.rev", hash_to_hex(get_midx_checksum(m)));
}
static int midx_read_oid_fanout(const unsigned char *chunk_start,
@@ -89,28 +89,30 @@
size_t midx_size;
void *midx_map = NULL;
uint32_t hash_version;
- char *midx_name = get_midx_filename(object_dir);
+ struct strbuf midx_name = STRBUF_INIT;
uint32_t i;
const char *cur_pack_name;
struct chunkfile *cf = NULL;
- fd = git_open(midx_name);
+ get_midx_filename(&midx_name, object_dir);
+
+ fd = git_open(midx_name.buf);
if (fd < 0)
goto cleanup_fail;
if (fstat(fd, &st)) {
- error_errno(_("failed to read %s"), midx_name);
+ error_errno(_("failed to read %s"), midx_name.buf);
goto cleanup_fail;
}
midx_size = xsize_t(st.st_size);
if (midx_size < MIDX_MIN_SIZE) {
- error(_("multi-pack-index file %s is too small"), midx_name);
+ error(_("multi-pack-index file %s is too small"), midx_name.buf);
goto cleanup_fail;
}
- FREE_AND_NULL(midx_name);
+ strbuf_release(&midx_name);
midx_map = xmmap(NULL, midx_size, PROT_READ, MAP_PRIVATE, fd, 0);
close(fd);
@@ -179,12 +181,13 @@
trace2_data_intmax("midx", the_repository, "load/num_packs", m->num_packs);
trace2_data_intmax("midx", the_repository, "load/num_objects", m->num_objects);
+ free_chunkfile(cf);
return m;
cleanup_fail:
free(m);
- free(midx_name);
- free(cf);
+ strbuf_release(&midx_name);
+ free_chunkfile(cf);
if (midx_map)
munmap(midx_map, midx_size);
if (0 <= fd)
@@ -1130,7 +1133,7 @@
const char *refs_snapshot,
unsigned flags)
{
- char *midx_name;
+ struct strbuf midx_name = STRBUF_INIT;
unsigned char midx_hash[GIT_MAX_RAWSZ];
uint32_t i;
struct hashfile *f = NULL;
@@ -1141,10 +1144,10 @@
int result = 0;
struct chunkfile *cf;
- midx_name = get_midx_filename(object_dir);
- if (safe_create_leading_directories(midx_name))
+ get_midx_filename(&midx_name, object_dir);
+ if (safe_create_leading_directories(midx_name.buf))
die_errno(_("unable to create leading directories of %s"),
- midx_name);
+ midx_name.buf);
if (!packs_to_include) {
/*
@@ -1373,7 +1376,7 @@
pack_name_concat_len += MIDX_CHUNK_ALIGNMENT -
(pack_name_concat_len % MIDX_CHUNK_ALIGNMENT);
- hold_lock_file_for_update(&lk, midx_name, LOCK_DIE_ON_ERROR);
+ hold_lock_file_for_update(&lk, midx_name.buf, LOCK_DIE_ON_ERROR);
f = hashfd(get_lock_file_fd(&lk), get_lock_file_path(&lk));
if (ctx.nr - dropped_packs == 0) {
@@ -1410,9 +1413,9 @@
ctx.pack_order = midx_pack_order(&ctx);
if (flags & MIDX_WRITE_REV_INDEX)
- write_midx_reverse_index(midx_name, midx_hash, &ctx);
+ write_midx_reverse_index(midx_name.buf, midx_hash, &ctx);
if (flags & MIDX_WRITE_BITMAP) {
- if (write_midx_bitmap(midx_name, midx_hash, &ctx,
+ if (write_midx_bitmap(midx_name.buf, midx_hash, &ctx,
refs_snapshot, flags) < 0) {
error(_("could not write multi-pack bitmap"));
result = 1;
@@ -1442,7 +1445,7 @@
free(ctx.entries);
free(ctx.pack_perm);
free(ctx.pack_order);
- free(midx_name);
+ strbuf_release(&midx_name);
return result;
}
@@ -1506,20 +1509,22 @@
void clear_midx_file(struct repository *r)
{
- char *midx = get_midx_filename(r->objects->odb->path);
+ struct strbuf midx = STRBUF_INIT;
+
+ get_midx_filename(&midx, r->objects->odb->path);
if (r->objects && r->objects->multi_pack_index) {
close_midx(r->objects->multi_pack_index);
r->objects->multi_pack_index = NULL;
}
- if (remove_path(midx))
- die(_("failed to clear multi-pack-index at %s"), midx);
+ if (remove_path(midx.buf))
+ die(_("failed to clear multi-pack-index at %s"), midx.buf);
clear_midx_files_ext(r->objects->odb->path, ".bitmap", NULL);
clear_midx_files_ext(r->objects->odb->path, ".rev", NULL);
- free(midx);
+ strbuf_release(&midx);
}
static int verify_midx_error;
@@ -1572,12 +1577,15 @@
if (!m) {
int result = 0;
struct stat sb;
- char *filename = get_midx_filename(object_dir);
- if (!stat(filename, &sb)) {
+ struct strbuf filename = STRBUF_INIT;
+
+ get_midx_filename(&filename, object_dir);
+
+ if (!stat(filename.buf, &sb)) {
error(_("multi-pack-index file exists, but failed to parse"));
result = 1;
}
- free(filename);
+ strbuf_release(&filename);
return result;
}
@@ -1610,7 +1618,7 @@
* Remaining tests assume that we have objects, so we can
* return here.
*/
- return verify_midx_error;
+ goto cleanup;
}
if (flags & MIDX_PROGRESS)
@@ -1688,7 +1696,9 @@
}
stop_progress(&progress);
+cleanup:
free(pairs);
+ close_midx(m);
return verify_midx_error;
}
diff --git a/midx.h b/midx.h
index 6e32297..b7d79a5 100644
--- a/midx.h
+++ b/midx.h
@@ -48,8 +48,8 @@
#define MIDX_WRITE_BITMAP_HASH_CACHE (1 << 3)
const unsigned char *get_midx_checksum(struct multi_pack_index *m);
-char *get_midx_filename(const char *object_dir);
-char *get_midx_rev_filename(struct multi_pack_index *m);
+void get_midx_filename(struct strbuf *out, const char *object_dir);
+void get_midx_rev_filename(struct strbuf *out, struct multi_pack_index *m);
struct multi_pack_index *load_multi_pack_index(const char *object_dir, int local);
int prepare_midx_pack(struct repository *r, struct multi_pack_index *m, uint32_t pack_int_id);
diff --git a/object-file.c b/object-file.c
index c3d866a..8be57f4 100644
--- a/object-file.c
+++ b/object-file.c
@@ -165,7 +165,6 @@
BUG("trying to finalize unknown hash");
}
-
const struct git_hash_algo hash_algos[GIT_HASH_NALGOS] = {
{
NULL,
@@ -184,8 +183,7 @@
},
{
"sha1",
- /* "sha1", big-endian */
- 0x73686131,
+ GIT_SHA1_FORMAT_ID,
GIT_SHA1_RAWSZ,
GIT_SHA1_HEXSZ,
GIT_SHA1_BLKSZ,
@@ -200,8 +198,7 @@
},
{
"sha256",
- /* "s256", big-endian */
- 0x73323536,
+ GIT_SHA256_FORMAT_ID,
GIT_SHA256_RAWSZ,
GIT_SHA256_HEXSZ,
GIT_SHA256_BLKSZ,
@@ -683,6 +680,49 @@
'\n', NULL, 0);
}
+struct object_directory *set_temporary_primary_odb(const char *dir, int will_destroy)
+{
+ struct object_directory *new_odb;
+
+ /*
+ * Make sure alternates are initialized, or else our entry may be
+ * overwritten when they are.
+ */
+ prepare_alt_odb(the_repository);
+
+ /*
+ * Make a new primary odb and link the old primary ODB in as an
+ * alternate
+ */
+ new_odb = xcalloc(1, sizeof(*new_odb));
+ new_odb->path = xstrdup(dir);
+
+ /*
+ * Disable ref updates while a temporary odb is active, since
+ * the objects in the database may roll back.
+ */
+ new_odb->disable_ref_updates = 1;
+ new_odb->will_destroy = will_destroy;
+ new_odb->next = the_repository->objects->odb;
+ the_repository->objects->odb = new_odb;
+ return new_odb->next;
+}
+
+void restore_primary_odb(struct object_directory *restore_odb, const char *old_path)
+{
+ struct object_directory *cur_odb = the_repository->objects->odb;
+
+ if (strcmp(old_path, cur_odb->path))
+ BUG("expected %s as primary object store; found %s",
+ old_path, cur_odb->path);
+
+ if (cur_odb->next != restore_odb)
+ BUG("we expect the old primary object store to be the first alternate");
+
+ the_repository->objects->odb = restore_odb;
+ free_object_directory(cur_odb);
+}
+
/*
* Compute the exact path an alternate is at and returns it. In case of
* error NULL is returned and the human readable error is added to `err`
@@ -797,7 +837,7 @@
}
}
- cmd->env = local_repo_env;
+ strvec_pushv(&cmd->env_array, (const char **)local_repo_env);
cmd->out = -1;
}
@@ -1306,7 +1346,7 @@
int parse_loose_header(const char *hdr, struct object_info *oi)
{
const char *type_buf = hdr;
- unsigned long size;
+ size_t size;
int type, type_len = 0;
/*
@@ -1341,12 +1381,12 @@
if (c > 9)
break;
hdr++;
- size = size * 10 + c;
+ size = st_add(st_mult(size, 10), c);
}
}
if (oi->sizep)
- *oi->sizep = size;
+ *oi->sizep = cast_size_t_to_ulong(size);
/*
* The length must be followed by a zero byte
@@ -1809,8 +1849,11 @@
/* Finalize a file on disk, and close it. */
static void close_loose_object(int fd)
{
- if (fsync_object_files)
- fsync_or_die(fd, "loose object file");
+ if (!the_repository->objects->odb->will_destroy) {
+ if (fsync_object_files)
+ fsync_or_die(fd, "loose object file");
+ }
+
if (close(fd) != 0)
die_errno(_("error when closing loose object file"));
}
@@ -2425,7 +2468,7 @@
struct strbuf buf = STRBUF_INIT;
size_t word_bits = bitsizeof(odb->loose_objects_subdir_seen[0]);
size_t word_index = subdir_nr / word_bits;
- size_t mask = 1u << (subdir_nr % word_bits);
+ size_t mask = (size_t)1u << (subdir_nr % word_bits);
uint32_t *bitmap;
if (subdir_nr < 0 ||
diff --git a/object-store.h b/object-store.h
index 952efb6..6f89482 100644
--- a/object-store.h
+++ b/object-store.h
@@ -28,6 +28,18 @@
struct oidtree *loose_objects_cache;
/*
+ * This is a temporary object store created by the tmp_objdir
+ * facility. Disable ref updates since the objects in the store
+ * might be discarded on rollback.
+ */
+ int disable_ref_updates;
+
+ /*
+ * This object store is ephemeral, so there is no need to fsync.
+ */
+ int will_destroy;
+
+ /*
* Path to the alternative object store. If this is a relative path,
* it is relative to the current working directory.
*/
@@ -59,6 +71,17 @@
void add_to_alternates_memory(const char *dir);
/*
+ * Replace the current writable object directory with the specified temporary
+ * object directory; returns the former primary object directory.
+ */
+struct object_directory *set_temporary_primary_odb(const char *dir, int will_destroy);
+
+/*
+ * Restore a previous ODB replaced by set_temporary_main_odb.
+ */
+void restore_primary_odb(struct object_directory *restore_odb, const char *old_path);
+
+/*
* Populate and return the loose object cache array corresponding to the
* given object ID.
*/
@@ -68,6 +91,9 @@
/* Empty the loose object cache for the specified object directory. */
void odb_clear_loose_cache(struct object_directory *odb);
+/* Clear and free the specified object directory */
+void free_object_directory(struct object_directory *odb);
+
struct packed_git {
struct hashmap_entry packmap_ent;
struct packed_git *next;
diff --git a/object.c b/object.c
index 23a24e6..c37501f 100644
--- a/object.c
+++ b/object.c
@@ -199,7 +199,7 @@
case OBJ_BLOB:
return (struct object *)lookup_blob(r, oid);
default:
- die("BUG: unknown object type %d", type);
+ BUG("unknown object type %d", type);
}
}
@@ -513,7 +513,7 @@
return o;
}
-static void free_object_directory(struct object_directory *odb)
+void free_object_directory(struct object_directory *odb)
{
free(odb->path);
odb_clear_loose_cache(odb);
diff --git a/pack-bitmap.c b/pack-bitmap.c
index f47a0a7..f772d3c 100644
--- a/pack-bitmap.c
+++ b/pack-bitmap.c
@@ -292,9 +292,12 @@
char *midx_bitmap_filename(struct multi_pack_index *midx)
{
- return xstrfmt("%s-%s.bitmap",
- get_midx_filename(midx->object_dir),
- hash_to_hex(get_midx_checksum(midx)));
+ struct strbuf buf = STRBUF_INIT;
+
+ get_midx_filename(&buf, midx->object_dir);
+ strbuf_addf(&buf, "-%s.bitmap", hash_to_hex(get_midx_checksum(midx)));
+
+ return strbuf_detach(&buf, NULL);
}
char *pack_bitmap_filename(struct packed_git *p)
@@ -324,10 +327,12 @@
}
if (bitmap_git->pack || bitmap_git->midx) {
+ struct strbuf buf = STRBUF_INIT;
+ get_midx_filename(&buf, midx->object_dir);
/* ignore extra bitmap file; we can only handle one */
- warning("ignoring extra bitmap file: %s",
- get_midx_filename(midx->object_dir));
+ warning("ignoring extra bitmap file: %s", buf.buf);
close(fd);
+ strbuf_release(&buf);
return -1;
}
@@ -1721,6 +1726,12 @@
else
die("mismatch in bitmap results");
+ bitmap_free(result);
+ bitmap_free(tdata.base);
+ bitmap_free(tdata.commits);
+ bitmap_free(tdata.trees);
+ bitmap_free(tdata.blobs);
+ bitmap_free(tdata.tags);
free_bitmap_index(bitmap_git);
}
@@ -1748,7 +1759,7 @@
struct object_id oid;
uint32_t i, index_pos;
- if (!bitmap_git->hashes)
+ if (!bitmap_git || !bitmap_git->hashes)
goto cleanup;
for (i = 0; i < bitmap_num_objects(bitmap_git); i++) {
@@ -1848,9 +1859,17 @@
ewah_pool_free(b->trees);
ewah_pool_free(b->blobs);
ewah_pool_free(b->tags);
+ if (b->bitmaps) {
+ struct stored_bitmap *sb;
+ kh_foreach_value(b->bitmaps, sb, {
+ ewah_pool_free(sb->root);
+ free(sb);
+ });
+ }
kh_destroy_oid_map(b->bitmaps);
free(b->ext_index.objects);
free(b->ext_index.hashes);
+ kh_destroy_oid_pos(b->ext_index.positions);
bitmap_free(b->result);
bitmap_free(b->haves);
if (bitmap_is_midx(b)) {
diff --git a/pack-revindex.c b/pack-revindex.c
index 0e4a31d..70d0fba 100644
--- a/pack-revindex.c
+++ b/pack-revindex.c
@@ -296,14 +296,14 @@
int load_midx_revindex(struct multi_pack_index *m)
{
- char *revindex_name;
+ struct strbuf revindex_name = STRBUF_INIT;
int ret;
if (m->revindex_data)
return 0;
- revindex_name = get_midx_rev_filename(m);
+ get_midx_rev_filename(&revindex_name, m);
- ret = load_revindex_from_disk(revindex_name,
+ ret = load_revindex_from_disk(revindex_name.buf,
m->num_objects,
&m->revindex_map,
&m->revindex_len);
@@ -313,7 +313,7 @@
m->revindex_data = (const uint32_t *)((const char *)m->revindex_map + RIDX_HEADER_SIZE);
cleanup:
- free(revindex_name);
+ strbuf_release(&revindex_name);
return ret;
}
diff --git a/packfile.c b/packfile.c
index 89402cf..835b2d2 100644
--- a/packfile.c
+++ b/packfile.c
@@ -324,7 +324,8 @@
}
}
-void close_pack_revindex(struct packed_git *p) {
+static void close_pack_revindex(struct packed_git *p)
+{
if (!p->revindex_map)
return;
@@ -1060,7 +1061,7 @@
unsigned long len, enum object_type *type, unsigned long *sizep)
{
unsigned shift;
- unsigned long size, c;
+ size_t size, c;
unsigned long used = 0;
c = buf[used++];
@@ -1068,16 +1069,16 @@
size = c & 15;
shift = 4;
while (c & 0x80) {
- if (len <= used || bitsizeof(long) <= shift) {
+ if (len <= used || (bitsizeof(long) - 7) < shift) {
error("bad object header");
size = used = 0;
break;
}
c = buf[used++];
- size += (c & 0x7f) << shift;
+ size = st_add(size, st_left_shift(c & 0x7f, shift));
shift += 7;
}
- *sizep = size;
+ *sizep = cast_size_t_to_ulong(size);
return used;
}
diff --git a/packfile.h b/packfile.h
index 1861467..a3f6723 100644
--- a/packfile.h
+++ b/packfile.h
@@ -90,7 +90,6 @@
unsigned char *use_pack(struct packed_git *, struct pack_window **, off_t, unsigned long *);
void close_pack_windows(struct packed_git *);
-void close_pack_revindex(struct packed_git *);
void close_pack(struct packed_git *);
void close_object_store(struct raw_object_store *o);
void unuse_pack(struct pack_window **);
diff --git a/pager.c b/pager.c
index 52f27a6..27877f8 100644
--- a/pager.c
+++ b/pager.c
@@ -8,7 +8,7 @@
#define DEFAULT_PAGER "less"
#endif
-static struct child_process pager_process = CHILD_PROCESS_INIT;
+static struct child_process pager_process;
static const char *pager_program;
/* Is the value coming back from term_columns() just a guess? */
@@ -124,6 +124,8 @@
setenv("GIT_PAGER_IN_USE", "true", 1);
+ child_process_init(&pager_process);
+
/* spawn the pager */
prepare_pager_args(&pager_process, pager);
pager_process.in = -1;
diff --git a/parallel-checkout.c b/parallel-checkout.c
index ed9c999..8dd7e7b 100644
--- a/parallel-checkout.c
+++ b/parallel-checkout.c
@@ -261,7 +261,7 @@
struct stream_filter *filter;
struct strbuf buf = STRBUF_INIT;
char *blob;
- unsigned long size;
+ size_t size;
ssize_t wrote;
/* Sanity check */
diff --git a/parse-options-cb.c b/parse-options-cb.c
index 3c811e1..d346dbe 100644
--- a/parse-options-cb.c
+++ b/parse-options-cb.c
@@ -1,5 +1,6 @@
#include "git-compat-util.h"
#include "parse-options.h"
+#include "branch.h"
#include "cache.h"
#include "commit.h"
#include "color.h"
@@ -293,3 +294,18 @@
return 0;
}
+
+int parse_opt_tracking_mode(const struct option *opt, const char *arg, int unset)
+{
+ if (unset)
+ *(enum branch_track *)opt->value = BRANCH_TRACK_NEVER;
+ else if (!arg || !strcmp(arg, "direct"))
+ *(enum branch_track *)opt->value = BRANCH_TRACK_EXPLICIT;
+ else if (!strcmp(arg, "inherit"))
+ *(enum branch_track *)opt->value = BRANCH_TRACK_INHERIT;
+ else
+ return error(_("option `%s' expects \"%s\" or \"%s\""),
+ "--track", "direct", "inherit");
+
+ return 0;
+}
diff --git a/parse-options.c b/parse-options.c
index fc5b43f..a828303 100644
--- a/parse-options.c
+++ b/parse-options.c
@@ -404,8 +404,9 @@
return PARSE_OPT_UNKNOWN;
}
-static int parse_nodash_opt(struct parse_opt_ctx_t *p, const char *arg,
- const struct option *options)
+static enum parse_opt_result parse_nodash_opt(struct parse_opt_ctx_t *p,
+ const char *arg,
+ const struct option *options)
{
const struct option *all_opts = options;
@@ -415,7 +416,7 @@
if (options->short_name == arg[0] && arg[1] == '\0')
return get_value(p, options, all_opts, OPT_SHORT);
}
- return -2;
+ return PARSE_OPT_ERROR;
}
static void check_typos(const char *arg, const struct option *options)
@@ -1075,6 +1076,6 @@
const char * const *usagestr,
const struct option *options)
{
- fprintf(stderr, "fatal: %s\n\n", msg);
+ die_message("%s\n", msg); /* The extra \n is intentional */
usage_with_options(usagestr, options);
}
diff --git a/parse-options.h b/parse-options.h
index 275fb44..e22846d 100644
--- a/parse-options.h
+++ b/parse-options.h
@@ -301,6 +301,8 @@
const char *, int);
int parse_opt_passthru(const struct option *, const char *, int);
int parse_opt_passthru_argv(const struct option *, const char *, int);
+/* value is enum branch_track* */
+int parse_opt_tracking_mode(const struct option *, const char *, int);
#define OPT__VERBOSE(var, h) OPT_COUNTUP('v', "verbose", (var), (h))
#define OPT__QUIET(var, h) OPT_COUNTUP('q', "quiet", (var), (h))
diff --git a/pathspec.h b/pathspec.h
index 2341dc9..402ebb8 100644
--- a/pathspec.h
+++ b/pathspec.h
@@ -58,8 +58,7 @@
#define GUARD_PATHSPEC(ps, mask) \
do { \
if ((ps)->magic & ~(mask)) \
- die("BUG:%s:%d: unsupported magic %x", \
- __FILE__, __LINE__, (ps)->magic & ~(mask)); \
+ BUG("unsupported magic %x", (ps)->magic & ~(mask)); \
} while (0)
/* parse_pathspec flags */
diff --git a/perl/Git/SVN.pm b/perl/Git/SVN.pm
index 35ff5a6..6ce2e28 100644
--- a/perl/Git/SVN.pm
+++ b/perl/Git/SVN.pm
@@ -6,7 +6,7 @@
use vars qw/$_no_metadata
$_repack $_repack_flags $_use_svm_props $_head
$_use_svnsync_props $no_reuse_existing
- $_use_log_author $_add_author_from $_localtime/;
+ $_use_log_author $_add_author_from $_localtime $_use_fsync/;
use Carp qw/croak/;
use File::Path qw/mkpath/;
use IPC::Open3;
@@ -2269,6 +2269,19 @@
}
}
+# TODO: move this to Git.pm?
+sub use_fsync {
+ if (!defined($_use_fsync)) {
+ my $x = $ENV{GIT_TEST_FSYNC};
+ if (defined $x) {
+ my $v = command_oneline('-c', "test.fsync=$x",
+ qw(config --type=bool test.fsync));
+ $_use_fsync = defined($v) ? ($v eq "true\n") : 1;
+ }
+ }
+ $_use_fsync;
+}
+
sub rev_map_set {
my ($self, $rev, $commit, $update_ref, $uuid) = @_;
defined $commit or die "missing arg3\n";
@@ -2290,7 +2303,7 @@
my $sync;
# both of these options make our .rev_db file very, very important
# and we can't afford to lose it because rebuild() won't work
- if ($self->use_svm_props || $self->no_metadata) {
+ if (($self->use_svm_props || $self->no_metadata) && use_fsync()) {
require File::Copy;
$sync = 1;
File::Copy::copy($db, $db_lock) or die "rev_map_set(@_): ",
diff --git a/pkt-line.c b/pkt-line.c
index 2dc8ac2..8e43c2d 100644
--- a/pkt-line.c
+++ b/pkt-line.c
@@ -370,6 +370,32 @@
return (val < 0) ? val : (val << 8) | hex2chr(lenbuf_hex + 2);
}
+static char *find_packfile_uri_path(const char *buffer)
+{
+ const char *URI_MARK = "://";
+ char *path;
+ int len;
+
+ /* First char is sideband mark */
+ buffer += 1;
+
+ len = strspn(buffer, "0123456789abcdefABCDEF");
+ /* size of SHA1 and SHA256 hash */
+ if (!(len == 40 || len == 64) || buffer[len] != ' ')
+ return NULL; /* required "<hash>SP" not seen */
+
+ path = strstr(buffer + len + 1, URI_MARK);
+ if (!path)
+ return NULL;
+
+ path = strchr(path + strlen(URI_MARK), '/');
+ if (!path || !*(path + 1))
+ return NULL;
+
+ /* position after '/' */
+ return ++path;
+}
+
enum packet_read_status packet_read_with_status(int fd, char **src_buffer,
size_t *src_len, char *buffer,
unsigned size, int *pktlen,
@@ -377,6 +403,7 @@
{
int len;
char linelen[4];
+ char *uri_path_start;
if (get_packet_data(fd, src_buffer, src_len, linelen, 4, options) < 0) {
*pktlen = -1;
@@ -427,7 +454,18 @@
len--;
buffer[len] = 0;
- packet_trace(buffer, len, 0);
+ if (options & PACKET_READ_REDACT_URI_PATH &&
+ (uri_path_start = find_packfile_uri_path(buffer))) {
+ const char *redacted = "<redacted>";
+ struct strbuf tracebuf = STRBUF_INIT;
+ strbuf_insert(&tracebuf, 0, buffer, len);
+ strbuf_splice(&tracebuf, uri_path_start - buffer,
+ strlen(uri_path_start), redacted, strlen(redacted));
+ packet_trace(tracebuf.buf, tracebuf.len, 0);
+ strbuf_release(&tracebuf);
+ } else {
+ packet_trace(buffer, len, 0);
+ }
if ((options & PACKET_READ_DIE_ON_ERR_PACKET) &&
starts_with(buffer, "ERR "))
diff --git a/pkt-line.h b/pkt-line.h
index 467ae01..6d2a63d 100644
--- a/pkt-line.h
+++ b/pkt-line.h
@@ -87,6 +87,7 @@
#define PACKET_READ_CHOMP_NEWLINE (1u<<1)
#define PACKET_READ_DIE_ON_ERR_PACKET (1u<<2)
#define PACKET_READ_GENTLE_ON_READ_ERROR (1u<<3)
+#define PACKET_READ_REDACT_URI_PATH (1u<<4)
int packet_read(int fd, char *buffer, unsigned size, int options);
/*
diff --git a/po/README.md b/po/README.md
index dcd8436..19fabb4 100644
--- a/po/README.md
+++ b/po/README.md
@@ -221,6 +221,10 @@
- Adjust the strings so that they're easy to translate. Most of the
advice in `info '(gettext)Preparing Strings'` applies here.
+- Strings referencing numbers of items may need to be split into singular and
+ plural forms; see the Q\_() wrapper in the C sub-section below for an
+ example.
+
- If something is unclear or ambiguous you can use a "TRANSLATORS"
comment to tell the translators what to make of it. These will be
extracted by xgettext(1) and put in the "po/\*.po" files, e.g. from
diff --git a/po/bg.po b/po/bg.po
index 51d7c72..8328531 100644
--- a/po/bg.po
+++ b/po/bg.po
@@ -1,7 +1,7 @@
# Bulgarian translation of git po-file.
# Copyright (C) 2014, 2015, 2016, 2017, 2018, 2019, 2020, 2021 Alexander Shopov <ash@kambanaria.org>.
# This file is distributed under the same license as the git package.
-# Alexander Shopov <ash@kambanaria.org>, 2014, 2015, 2016, 2017, 2018, 2019, 2020, 2021.
+# Alexander Shopov <ash@kambanaria.org>, 2014, 2015, 2016, 2017, 2018, 2019, 2020, 2021, 2022.
#
# ========================
# DICTIONARY TO MERGE IN GIT GUI
@@ -33,6 +33,7 @@
# switch to branch преминавам към клон
# sparse entry/blob частично изтеглена директория/път/обект-BLOB
# sparse index частичен индекс
+# sparcity частичност
# revision range диапазон на версиите
# cover letter придружаващо писмо
# reference repository еталонно хранилище
@@ -163,6 +164,10 @@
# prefetch предварително доставяне
# scheduler планиращ модул
# snapshot снимка
+# enlistment зачислена директория
+# zealous merge засилено сливане
+# unregister отчислявам
+# ma