Merge branch 'debian-sid' into debian-stable-backports
* debian-sid: (525 commits)
debian: new upstream release
Git 2.27
t: avoid alternation (not POSIX) in grep's BRE
l10n: zh_TW.po: v2.27.0 round 2 (0 untranslated)
l10n: zh_TW.po: v2.27.0 round 1 (0 untranslated)
l10n: de.po: Fix typo in the German translation of octopus
l10n: de.po: Update German translation for Git 2.27.0
l10n: it.po: update for Git 2.27.0 round #2
Documentation: correct hash environment variable
l10n: tr: v2.27.0 round 2
l10n: fr.po v2.27.0 rnd 2
l10n: bg.po: Updated Bulgarian translation (4875t)
l10n: Update Catalan translation
l10n: sv.po: Update Swedish translation (4875t0f0u)
l10n: vi(4875t): Updated Vietnamses translation for 2.27.0rd2
l10n: zh_CN: for git v2.27.0 l10n round 1~2
l10n: git.pot: v2.27.0 round 2 (+1)
debian: new upstream release candidate
Git 2.27-rc2
rev-list-options.txt: start a list for `show-pulls`
...
Signed-off-by: Jonathan Nieder <jrnieder@gmail.com>
diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml
new file mode 100644
index 0000000..84a5dcf
--- /dev/null
+++ b/.github/workflows/main.yml
@@ -0,0 +1,305 @@
+name: CI/PR
+
+on: [push, pull_request]
+
+env:
+ DEVELOPER: 1
+
+jobs:
+ ci-config:
+ runs-on: ubuntu-latest
+ outputs:
+ enabled: ${{ steps.check-ref.outputs.enabled }}
+ steps:
+ - name: try to clone ci-config branch
+ continue-on-error: true
+ run: |
+ git -c protocol.version=2 clone \
+ --no-tags \
+ --single-branch \
+ -b ci-config \
+ --depth 1 \
+ --no-checkout \
+ --filter=blob:none \
+ https://github.com/${{ github.repository }} \
+ config-repo &&
+ cd config-repo &&
+ git checkout HEAD -- ci/config
+ - id: check-ref
+ name: check whether CI is enabled for ref
+ run: |
+ enabled=yes
+ if test -x config-repo/ci/config/allow-ref &&
+ ! config-repo/ci/config/allow-ref '${{ github.ref }}'
+ then
+ enabled=no
+ fi
+ echo "::set-output name=enabled::$enabled"
+
+ windows-build:
+ needs: ci-config
+ if: needs.ci-config.outputs.enabled == 'yes'
+ runs-on: windows-latest
+ steps:
+ - uses: actions/checkout@v1
+ - name: download git-sdk-64-minimal
+ shell: bash
+ run: |
+ ## Get artifact
+ urlbase=https://dev.azure.com/git-for-windows/git/_apis/build/builds
+ id=$(curl "$urlbase?definitions=22&statusFilter=completed&resultFilter=succeeded&\$top=1" |
+ jq -r ".value[] | .id")
+ download_url="$(curl "$urlbase/$id/artifacts" |
+ jq -r '.value[] | select(.name == "git-sdk-64-minimal").resource.downloadUrl')"
+ curl --connect-timeout 10 --retry 5 --retry-delay 0 --retry-max-time 240 \
+ -o artifacts.zip "$download_url"
+
+ ## Unzip and remove the artifact
+ unzip artifacts.zip
+ rm artifacts.zip
+ - name: build
+ shell: powershell
+ env:
+ HOME: ${{runner.workspace}}
+ MSYSTEM: MINGW64
+ NO_PERL: 1
+ run: |
+ & .\git-sdk-64-minimal\usr\bin\bash.exe -lc @"
+ printf '%s\n' /git-sdk-64-minimal/ >>.git/info/exclude
+
+ ci/make-test-artifacts.sh artifacts
+ "@
+ - name: upload build artifacts
+ uses: actions/upload-artifact@v1
+ with:
+ name: windows-artifacts
+ path: artifacts
+ - name: upload git-sdk-64-minimal
+ uses: actions/upload-artifact@v1
+ with:
+ name: git-sdk-64-minimal
+ path: git-sdk-64-minimal
+ windows-test:
+ runs-on: windows-latest
+ needs: [windows-build]
+ strategy:
+ matrix:
+ nr: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
+ steps:
+ - uses: actions/checkout@v1
+ - name: download build artifacts
+ uses: actions/download-artifact@v1
+ with:
+ name: windows-artifacts
+ path: ${{github.workspace}}
+ - name: extract build artifacts
+ shell: bash
+ run: tar xf artifacts.tar.gz
+ - name: download git-sdk-64-minimal
+ uses: actions/download-artifact@v1
+ with:
+ name: git-sdk-64-minimal
+ path: ${{github.workspace}}/git-sdk-64-minimal/
+ - name: test
+ shell: powershell
+ run: |
+ & .\git-sdk-64-minimal\usr\bin\bash.exe -lc @"
+ # Let Git ignore the SDK
+ printf '%s\n' /git-sdk-64-minimal/ >>.git/info/exclude
+
+ ci/run-test-slice.sh ${{matrix.nr}} 10
+ "@
+ - name: ci/print-test-failures.sh
+ if: failure()
+ shell: powershell
+ run: |
+ & .\git-sdk-64-minimal\usr\bin\bash.exe -lc ci/print-test-failures.sh
+ - name: Upload failed tests' directories
+ if: failure() && env.FAILED_TEST_ARTIFACTS != ''
+ uses: actions/upload-artifact@v1
+ with:
+ name: failed-tests-windows
+ path: ${{env.FAILED_TEST_ARTIFACTS}}
+ vs-build:
+ needs: ci-config
+ if: needs.ci-config.outputs.enabled == 'yes'
+ env:
+ MSYSTEM: MINGW64
+ NO_PERL: 1
+ GIT_CONFIG_PARAMETERS: "'user.name=CI' 'user.email=ci@git'"
+ runs-on: windows-latest
+ steps:
+ - uses: actions/checkout@v1
+ - name: download git-sdk-64-minimal
+ shell: bash
+ run: |
+ ## Get artifact
+ urlbase=https://dev.azure.com/git-for-windows/git/_apis/build/builds
+ id=$(curl "$urlbase?definitions=22&statusFilter=completed&resultFilter=succeeded&\$top=1" |
+ jq -r ".value[] | .id")
+ download_url="$(curl "$urlbase/$id/artifacts" |
+ jq -r '.value[] | select(.name == "git-sdk-64-minimal").resource.downloadUrl')"
+ curl --connect-timeout 10 --retry 5 --retry-delay 0 --retry-max-time 240 \
+ -o artifacts.zip "$download_url"
+
+ ## Unzip and remove the artifact
+ unzip artifacts.zip
+ rm artifacts.zip
+ - name: generate Visual Studio solution
+ shell: powershell
+ run: |
+ & .\git-sdk-64-minimal\usr\bin\bash.exe -lc @"
+ make NDEBUG=1 DEVELOPER=1 vcxproj
+ "@
+ if (!$?) { exit(1) }
+ - name: download vcpkg artifacts
+ shell: powershell
+ run: |
+ $urlbase = "https://dev.azure.com/git/git/_apis/build/builds"
+ $id = ((Invoke-WebRequest -UseBasicParsing "${urlbase}?definitions=9&statusFilter=completed&resultFilter=succeeded&`$top=1").content | ConvertFrom-JSON).value[0].id
+ $downloadUrl = ((Invoke-WebRequest -UseBasicParsing "${urlbase}/$id/artifacts").content | ConvertFrom-JSON).value[0].resource.downloadUrl
+ (New-Object Net.WebClient).DownloadFile($downloadUrl, "compat.zip")
+ Expand-Archive compat.zip -DestinationPath . -Force
+ Remove-Item compat.zip
+ - name: add msbuild to PATH
+ uses: microsoft/setup-msbuild@v1.0.0
+ - name: MSBuild
+ run: msbuild git.sln -property:Configuration=Release -property:Platform=x64 -maxCpuCount:4 -property:PlatformToolset=v142
+ - name: bundle artifact tar
+ shell: powershell
+ env:
+ MSVC: 1
+ VCPKG_ROOT: ${{github.workspace}}\compat\vcbuild\vcpkg
+ run: |
+ & compat\vcbuild\vcpkg_copy_dlls.bat release
+ if (!$?) { exit(1) }
+ & git-sdk-64-minimal\usr\bin\bash.exe -lc @"
+ mkdir -p artifacts &&
+ eval \"`$(make -n artifacts-tar INCLUDE_DLLS_IN_ARTIFACTS=YesPlease ARTIFACTS_DIRECTORY=artifacts 2>&1 | grep ^tar)\"
+ "@
+ - name: upload build artifacts
+ uses: actions/upload-artifact@v1
+ with:
+ name: vs-artifacts
+ path: artifacts
+ vs-test:
+ runs-on: windows-latest
+ needs: [vs-build, windows-build]
+ strategy:
+ matrix:
+ nr: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
+ steps:
+ - uses: actions/checkout@v1
+ - name: download git-sdk-64-minimal
+ uses: actions/download-artifact@v1
+ with:
+ name: git-sdk-64-minimal
+ path: ${{github.workspace}}/git-sdk-64-minimal/
+ - name: download build artifacts
+ uses: actions/download-artifact@v1
+ with:
+ name: vs-artifacts
+ path: ${{github.workspace}}
+ - name: extract build artifacts
+ shell: bash
+ run: tar xf artifacts.tar.gz
+ - name: test (parallel)
+ shell: powershell
+ env:
+ MSYSTEM: MINGW64
+ NO_SVN_TESTS: 1
+ GIT_TEST_SKIP_REBASE_P: 1
+ run: |
+ & .\git-sdk-64-minimal\usr\bin\bash.exe -lc @"
+ # Let Git ignore the SDK and the test-cache
+ printf '%s\n' /git-sdk-64-minimal/ /test-cache/ >>.git/info/exclude
+
+ cd t &&
+ PATH=\"`$PWD/helper:`$PATH\" &&
+ test-tool.exe run-command testsuite --jobs=10 -V -x --write-junit-xml \
+ `$(test-tool.exe path-utils slice-tests \
+ ${{matrix.nr}} 10 t[0-9]*.sh)
+ "@
+ regular:
+ needs: ci-config
+ if: needs.ci-config.outputs.enabled == 'yes'
+ strategy:
+ matrix:
+ vector:
+ - jobname: linux-clang
+ cc: clang
+ pool: ubuntu-latest
+ - jobname: linux-gcc
+ cc: gcc
+ pool: ubuntu-latest
+ - jobname: osx-clang
+ cc: clang
+ pool: macos-latest
+ - jobname: osx-gcc
+ cc: gcc
+ pool: macos-latest
+ - jobname: GETTEXT_POISON
+ cc: gcc
+ pool: ubuntu-latest
+ env:
+ CC: ${{matrix.vector.cc}}
+ jobname: ${{matrix.vector.jobname}}
+ runs-on: ${{matrix.vector.pool}}
+ steps:
+ - uses: actions/checkout@v1
+ - run: ci/install-dependencies.sh
+ - run: ci/run-build-and-tests.sh
+ - run: ci/print-test-failures.sh
+ if: failure()
+ - name: Upload failed tests' directories
+ if: failure() && env.FAILED_TEST_ARTIFACTS != ''
+ uses: actions/upload-artifact@v1
+ with:
+ name: failed-tests-${{matrix.vector.jobname}}
+ path: ${{env.FAILED_TEST_ARTIFACTS}}
+ dockerized:
+ needs: ci-config
+ if: needs.ci-config.outputs.enabled == 'yes'
+ strategy:
+ matrix:
+ vector:
+ - jobname: linux-musl
+ image: alpine
+ - jobname: Linux32
+ image: daald/ubuntu32:xenial
+ env:
+ jobname: ${{matrix.vector.jobname}}
+ runs-on: ubuntu-latest
+ container: ${{matrix.vector.image}}
+ steps:
+ - uses: actions/checkout@v1
+ - run: ci/install-docker-dependencies.sh
+ - run: ci/run-build-and-tests.sh
+ - run: ci/print-test-failures.sh
+ if: failure()
+ - name: Upload failed tests' directories
+ if: failure() && env.FAILED_TEST_ARTIFACTS != ''
+ uses: actions/upload-artifact@v1
+ with:
+ name: failed-tests-${{matrix.vector.jobname}}
+ path: ${{env.FAILED_TEST_ARTIFACTS}}
+ static-analysis:
+ needs: ci-config
+ if: needs.ci-config.outputs.enabled == 'yes'
+ env:
+ jobname: StaticAnalysis
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@v1
+ - run: ci/install-dependencies.sh
+ - run: ci/run-static-analysis.sh
+ documentation:
+ needs: ci-config
+ if: needs.ci-config.outputs.enabled == 'yes'
+ env:
+ jobname: Documentation
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@v1
+ - run: ci/install-dependencies.sh
+ - run: ci/test-documentation.sh
diff --git a/.gitignore b/.gitignore
index aebe7c0..ee509a2 100644
--- a/.gitignore
+++ b/.gitignore
@@ -25,6 +25,7 @@
/git-bisect--helper
/git-blame
/git-branch
+/git-bugreport
/git-bundle
/git-cat-file
/git-check-attr
@@ -83,7 +84,6 @@
/git-init-db
/git-interpret-trailers
/git-instaweb
-/git-legacy-stash
/git-log
/git-ls-files
/git-ls-remote
@@ -189,6 +189,7 @@
/gitweb/gitweb.cgi
/gitweb/static/gitweb.js
/gitweb/static/gitweb.min.*
+/config-list.h
/command-list.h
*.tar.gz
*.dsc
diff --git a/.travis.yml b/.travis.yml
index fc5730b..05f3e3f 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -16,7 +16,7 @@
matrix:
include:
- - env: jobname=GIT_TEST_GETTEXT_POISON
+ - env: jobname=GETTEXT_POISON
os: linux
compiler:
addons:
@@ -32,7 +32,15 @@
services:
- docker
before_install:
- script: ci/run-linux32-docker.sh
+ 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:
diff --git a/Documentation/CodingGuidelines b/Documentation/CodingGuidelines
index ed4e443..227f46a 100644
--- a/Documentation/CodingGuidelines
+++ b/Documentation/CodingGuidelines
@@ -91,16 +91,10 @@
- No shell arrays.
- - No strlen ${#parameter}.
-
- No pattern replacement ${parameter/pattern/string}.
- We use Arithmetic Expansion $(( ... )).
- - Inside Arithmetic Expansion, spell shell variables with $ in front
- of them, as some shells do not grok $((x)) while accepting $(($x))
- just fine (e.g. dash older than 0.5.4).
-
- We do not use Process Substitution <(list) or >(list).
- Do not write control structures on a single line with semicolon.
@@ -238,6 +232,18 @@
while( condition )
func (bar+1);
+ - Do not explicitly compare an integral value with constant 0 or '\0',
+ or a pointer value with constant NULL. For instance, to validate that
+ counted array <ptr, cnt> is initialized but has no elements, write:
+
+ if (!ptr || cnt)
+ BUG("empty array expected");
+
+ and not:
+
+ if (ptr == NULL || cnt != 0);
+ BUG("empty array expected");
+
- We avoid using braces unnecessarily. I.e.
if (bla) {
diff --git a/Documentation/Makefile b/Documentation/Makefile
index 8fe829c..15d9d04 100644
--- a/Documentation/Makefile
+++ b/Documentation/Makefile
@@ -30,6 +30,7 @@
MAN7_TXT += gitcvs-migration.txt
MAN7_TXT += gitdiffcore.txt
MAN7_TXT += giteveryday.txt
+MAN7_TXT += gitfaq.txt
MAN7_TXT += gitglossary.txt
MAN7_TXT += gitnamespaces.txt
MAN7_TXT += gitremote-helpers.txt
@@ -149,32 +150,9 @@
-include ../config.mak.autogen
-include ../config.mak
-#
-# For docbook-xsl ...
-# -1.68.1, no extra settings are needed?
-# 1.69.0, set ASCIIDOC_ROFF?
-# 1.69.1-1.71.0, set DOCBOOK_SUPPRESS_SP?
-# 1.71.1, set ASCIIDOC_ROFF?
-# 1.72.0, set DOCBOOK_XSL_172.
-# 1.73.0-, no extra settings are needed
-#
-
-ifdef DOCBOOK_XSL_172
-ASCIIDOC_EXTRA += -a git-asciidoc-no-roff
-MANPAGE_XSL = manpage-1.72.xsl
-else
- ifndef ASCIIDOC_ROFF
- # docbook-xsl after 1.72 needs the regular XSL, but will not
- # pass-thru raw roff codes from asciidoc.conf, so turn them off.
- ASCIIDOC_EXTRA += -a git-asciidoc-no-roff
- endif
-endif
ifndef NO_MAN_BOLD_LITERAL
XMLTO_EXTRA += -m manpage-bold-literal.xsl
endif
-ifdef DOCBOOK_SUPPRESS_SP
-XMLTO_EXTRA += -m manpage-suppress-sp.xsl
-endif
# Newer DocBook stylesheet emits warning cruft in the output when
# this is not set, and if set it shows an absolute link. Older
diff --git a/Documentation/MyFirstObjectWalk.txt b/Documentation/MyFirstObjectWalk.txt
index aa828df..c3f2d1a 100644
--- a/Documentation/MyFirstObjectWalk.txt
+++ b/Documentation/MyFirstObjectWalk.txt
@@ -357,9 +357,6 @@
...
while ((commit = get_revision(rev))) {
- if (!commit)
- continue;
-
strbuf_reset(&prettybuf);
pp_commit_easy(CMIT_FMT_ONELINE, commit, &prettybuf);
puts(prettybuf.buf);
diff --git a/Documentation/RelNotes/2.27.0.txt b/Documentation/RelNotes/2.27.0.txt
new file mode 100644
index 0000000..15518d0
--- /dev/null
+++ b/Documentation/RelNotes/2.27.0.txt
@@ -0,0 +1,525 @@
+Git 2.27 Release Notes
+======================
+
+Updates since v2.26
+-------------------
+
+Backward compatibility notes
+
+ * When "git describe C" finds that commit C is pointed by a signed or
+ annotated tag, which records T as its tagname in the object, the
+ command gives T as its answer. Even if the user renames or moves
+ such a tag from its natural location in the "refs/tags/" hierarchy,
+ "git describe C" would still give T as the answer, but in such a
+ case "git show T^0" would no longer work as expected. There may be
+ nothing at "refs/tags/T" or even worse there may be a different tag
+ instead.
+
+ Starting from this version, "git describe" will always use the
+ "long" version, as if the "--long" option were given, when giving
+ its output based on such a misplaced tag to work around the problem.
+
+ * "git pull" issues a warning message until the pull.rebase
+ configuration variable is explicitly given, which some existing
+ users may find annoying---those who prefer not to rebase need to
+ set the variable to false to squelch the warning.
+
+ * The transport protocol version 2, which was promoted to the default
+ in Git 2.26 release, turned out to have some remaining rough edges,
+ so it has been demoted from the default.
+
+
+UI, Workflows & Features
+
+ * A handful of options to configure SSL when talking to proxies have
+ been added.
+
+ * Smudge/clean conversion filters are now given more information
+ (e.g. the object of the tree-ish in which the blob being converted
+ appears, in addition to its path, which has already been given).
+
+ * When "git describe C" finds an annotated tag with tagname A to be
+ the best name to explain commit C, and the tag is stored in a
+ "wrong" place in the refs/tags hierarchy, e.g. refs/tags/B, the
+ command gave a warning message but used A (not B) to describe C.
+ If C is exactly at the tag, the describe output would be "A", but
+ "git rev-parse A^0" would not be equal as "git rev-parse C^0". The
+ behavior of the command has been changed to use the "long" form
+ i.e. A-0-gOBJECTNAME, which is correctly interpreted by rev-parse.
+
+ * "git pull" learned to warn when no pull.rebase configuration
+ exists, and neither --[no-]rebase nor --ff-only is given (which
+ would result a merge).
+
+ * "git p4" learned four new hooks and also "--no-verify" option to
+ bypass them (and the existing "p4-pre-submit" hook).
+
+ * "git pull" shares many options with underlying "git fetch", but
+ some of them were not documented and some of those that would make
+ sense to pass down were not passed down.
+
+ * "git rebase" learned the "--no-gpg-sign" option to countermand
+ commit.gpgSign the user may have.
+
+ * The output from "git format-patch" uses RFC 2047 encoding for
+ non-ASCII letters on From: and Subject: headers, so that it can
+ directly be fed to e-mail programs. A new option has been added
+ to produce these headers in raw.
+
+ * "git log" learned "--show-pulls" that helps pathspec limited
+ history views; a merge commit that takes the whole change from a
+ side branch, which is normally omitted from the output, is shown
+ in addition to the commits that introduce real changes.
+
+ * The interactive input from various codepaths are consolidated and
+ any prompt possibly issued earlier are fflush()ed before we read.
+
+ * Allow "git rebase" to reapply all local commits, even if the may be
+ already in the upstream, without checking first.
+
+ * The 'pack.useSparse' configuration variable now defaults to 'true',
+ enabling an optimization that has been experimental since Git 2.21.
+
+ * "git rebase" happens to call some hooks meant for "checkout" and
+ "commit" by this was not a designed behaviour than historical
+ accident. This has been documented.
+
+ * "git merge" learns the "--autostash" option.
+
+ * "sparse-checkout" UI improvements.
+
+ * "git update-ref --stdin" learned a handful of new verbs to let the
+ user control ref update transactions more explicitly, which helps
+ as an ingredient to implement two-phase commit-style atomic
+ ref-updates across multiple repositories.
+
+ * "git commit-graph write" learned different ways to write out split
+ files.
+
+ * Introduce an extension to the commit-graph to make it efficient to
+ check for the paths that were modified at each commit using Bloom
+ filters.
+
+ * The approxidate parser learns to parse seconds with fraction and
+ ignore fractional part.
+
+ * The userdiff patterns for Markdown documents have been added.
+
+ * The sparse-checkout patterns have been forbidden from excluding all
+ paths, leaving an empty working tree, for a long time. This
+ limitation has been lifted.
+
+ * "git restore --staged --worktree" now defaults to take the contents
+ out of "HEAD", instead of erring out.
+
+ * "git p4" learned to recover from a (broken) state where a directory
+ and a file are recorded at the same path in the Perforce repository
+ the same way as their clients do.
+
+ * "git multi-pack-index repack" has been taught to honor some
+ repack.* configuration variables.
+
+
+Performance, Internal Implementation, Development Support etc.
+
+ * The advise API has been revamped to allow more systematic enumeration of
+ advice knobs in the future.
+
+ * SHA-256 transition continues.
+
+ * The code to interface with GnuPG has been refactored.
+
+ * "git stash" has kept an escape hatch to use the scripted version
+ for a few releases, which got stale. It has been removed.
+
+ * Enable tests that require GnuPG on Windows.
+
+ * Minor test usability improvement.
+
+ * Trace2 enhancement to allow logging of the environment variables.
+
+ * Test clean-up continues.
+
+ * Perf-test update.
+
+ * A Windows-specific test element has been made more robust against
+ misuse from both user's environment and programmer's errors.
+
+ * Various tests have been updated to work around issues found with
+ shell utilities that come with busybox etc.
+
+ * The config API made mixed uses of int and size_t types to represent
+ length of various pieces of text it parsed, which has been updated
+ to use the correct type (i.e. size_t) throughout.
+
+ * The "--decorate-refs" and "--decorate-refs-exclude" options "git
+ log" takes have learned a companion configuration variable
+ log.excludeDecoration that sits at the lowest priority in the
+ family.
+
+ * A new CI job to build and run test suite on linux with musl libc
+ has been added.
+
+ * Update the CI configuration to use GitHub Actions, retiring the one
+ based on Azure Pipelines.
+
+ * The directory traversal code had redundant recursive calls which
+ made its performance characteristics exponential with respect to
+ the depth of the tree, which was corrected.
+
+ * "git blame" learns to take advantage of the "changed-paths" Bloom
+ filter stored in the commit-graph file.
+
+ * The "bugreport" tool has been added.
+
+ * The object walk with object filter "--filter=tree:0" can now take
+ advantage of the pack bitmap when available.
+
+ * Instead of always building all branches at GitHub via Actions,
+ users can specify which branches to build.
+
+ * Codepaths that show progress meter have been taught to also use the
+ start_progress() and the stop_progress() calls as a "region" to be
+ traced.
+
+ * Instead of downloading Windows SDK for CI jobs for windows builds
+ from an external site (wingit.blob.core.windows.net), use the one
+ created in the windows-build job, to work around quota issues at
+ the external site.
+
+
+Fixes since v2.26
+-----------------
+
+ * The real_path() convenience function can easily be misused; with a
+ bit of code refactoring in the callers' side, its use has been
+ eliminated.
+ (merge 49d3c4b481 am/real-path-fix later to maint).
+
+ * Update "git p4" to work with Python 3.
+ (merge 6bb40ed20a yz/p4-py3 later to maint).
+
+ * The mechanism to prevent "git commit" from making an empty commit
+ or amending during an interrupted cherry-pick was broken during the
+ rewrite of "git rebase" in C, which has been corrected.
+ (merge 430b75f720 pw/advise-rebase-skip later to maint).
+
+ * Fix "git checkout --recurse-submodules" of a nested submodule
+ hierarchy.
+ (merge 846f34d351 pb/recurse-submodules-fix later to maint).
+
+ * The "--fork-point" mode of "git rebase" regressed when the command
+ was rewritten in C back in 2.20 era, which has been corrected.
+ (merge f08132f889 at/rebase-fork-point-regression-fix later to maint).
+
+ * The import-tars importer (in contrib/fast-import/) used to create
+ phony files at the top-level of the repository when the archive
+ contains global PAX headers, which made its own logic to detect and
+ omit the common leading directory ineffective, which has been
+ corrected.
+ (merge c839fcff65 js/import-tars-do-not-make-phony-files-from-pax-headers later to maint).
+
+ * Simplify the commit ancestry connectedness check in a partial clone
+ repository in which "promised" objects are assumed to be obtainable
+ lazily on-demand from promisor remote repositories.
+ (merge 2b98478c6f jt/connectivity-check-optim-in-partial-clone later to maint).
+
+ * The server-end of the v2 protocol to serve "git clone" and "git
+ fetch" was not prepared to see a delim packets at unexpected
+ places, which led to a crash.
+ (merge cacae4329f jk/harden-protocol-v2-delim-handling later to maint).
+
+ * When fed a midx that records no objects, some codepaths tried to
+ loop from 0 through (num_objects-1), which, due to integer
+ arithmetic wrapping around, made it nonsense operation with out of
+ bounds array accesses. The code has been corrected to reject such
+ an midx file.
+ (merge 796d61cdc0 dr/midx-avoid-int-underflow later to maint).
+
+ * Utitiles run via the run_command() API were not spawned correctly
+ on Cygwin, when the paths to them are given as a full path with
+ backslashes.
+ (merge 05ac8582bc ak/run-command-on-cygwin-fix later to maint).
+
+ * "git pull --rebase" tried to run a rebase even after noticing that
+ the pull results in a fast-forward and no rebase is needed nor
+ sensible, for the past few years due to a mistake nobody noticed.
+ (merge fbae70ddc6 en/pull-do-not-rebase-after-fast-forwarding later to maint).
+
+ * "git rebase" with the merge backend did not work well when the
+ rebase.abbreviateCommands configuration was set.
+ (merge de9f1d3ef4 ag/rebase-merge-allow-ff-under-abbrev-command later to maint).
+
+ * The logic to auto-follow tags by "git clone --single-branch" was
+ not careful to avoid lazy-fetching unnecessary tags, which has been
+ corrected.
+ (merge 167a575e2d jk/use-quick-lookup-in-clone-for-tag-following later to maint).
+
+ * "git rebase -i" did not leave the reflog entries correctly.
+ (merge 1f6965f994 en/sequencer-reflog-action later to maint).
+
+ * The more aggressive updates to remote-tracking branches we had for
+ the past 7 years or so were not reflected in the documentation,
+ which has been corrected.
+ (merge a44088435c pb/pull-fetch-doc later to maint).
+
+ * We've left the command line parsing of "git log :/a/b/" broken for
+ about a full year without anybody noticing, which has been
+ corrected.
+ (merge 0220461071 jc/missing-ref-store-fix later to maint).
+
+ * Misc fixes for Windows.
+ (merge 3efc128cd5 js/mingw-fixes later to maint).
+
+ * "git rebase" (again) learns to honor "--no-keep-empty", which lets
+ the user to discard commits that are empty from the beginning (as
+ opposed to the ones that become empty because of rebasing). The
+ interactive rebase also marks commits that are empty in the todo.
+ (merge 50ed76148a en/rebase-no-keep-empty later to maint).
+
+ * Parsing the host part out of URL for the credential helper has been corrected.
+ (merge 4c5971e18a jk/credential-parsing-end-of-host-in-URL later to maint).
+
+ * Document the recommended way to abort a failing test early (e.g. by
+ exiting a loop), which is to say "return 1".
+ (merge 7cc112dc95 jc/doc-test-leaving-early later to maint).
+
+ * The code that refreshes the last access and modified time of
+ on-disk packfiles and loose object files have been updated.
+ (merge 312cd76130 lr/freshen-file-fix later to maint).
+
+ * Validation of push certificate has been made more robust against
+ timing attacks.
+ (merge 719483e547 bc/constant-memequal later to maint).
+
+ * The custom hash function used by "git fast-import" has been
+ replaced with the one from hashmap.c, which gave us a nice
+ performance boost.
+ (merge d8410a816b jk/fast-import-use-hashmap later to maint).
+
+ * The "git submodule" command did not initialize a few variables it
+ internally uses and was affected by variable settings leaked from
+ the environment.
+ (merge 65d100c4dd lx/submodule-clear-variables later to maint).
+
+ * Raise the minimum required version of docbook-xsl package to 1.74,
+ as 1.74.0 was from late 2008, which is more than 10 years old, and
+ drop compatibility cruft from our documentation suite.
+ (merge 3c255ad660 ma/doc-discard-docbook-xsl-1.73 later to maint).
+
+ * "git log" learns "--[no-]mailmap" as a synonym to "--[no-]use-mailmap"
+ (merge 88acccda38 jc/log-no-mailmap later to maint).
+
+ * "git commit-graph write --expire-time=<timestamp>" did not use the
+ given timestamp correctly, which has been corrected.
+ (merge b09b785c78 ds/commit-graph-expiry-fix later to maint).
+
+ * Tests update to use "test-chmtime" instead of "touch -t".
+ (merge e892a56845 ds/t5319-touch-fix later to maint).
+
+ * "git diff" in a partial clone learned to avoid lazy loading blob
+ objects in more casese when they are not needed.
+ (merge 95acf11a3d jt/avoid-prefetch-when-able-in-diff later to maint).
+
+ * "git push --atomic" used to show failures for refs that weren't
+ even pushed, which has been corrected.
+ (merge dfe1b7f19c jx/atomic-push later to maint).
+
+ * Code in builtin/*, i.e. those can only be called from within
+ built-in subcommands, that implements bulk of a couple of
+ subcommands have been moved to libgit.a so that they could be used
+ by others.
+ (merge 9460fd48b5 dl/libify-a-few later to maint).
+
+ * Allowing the user to split a patch hunk while "git stash -p" does
+ not work well; a band-aid has been added to make this (partially)
+ work better.
+
+ * "git diff-tree --pretty --notes" used to hit an assertion failure,
+ as it forgot to initialize the notes subsystem.
+ (merge 5778b22b3d tb/diff-tree-with-notes later to maint).
+
+ * "git range-diff" fixes.
+ (merge 8d1675eb7f vd/range-diff-with-custom-pretty-format-fix later to maint).
+
+ * "git grep" did not quote a path with unusual character like other
+ commands (like "git diff", "git status") do, but did quote when run
+ from a subdirectory, both of which has been corrected.
+ (merge 45115d8490 mt/grep-cquote-path later to maint).
+
+ * GNU/Hurd is also among the ones that need the fopen() wrapper.
+ (merge 274a1328fb jc/gnu-hurd-lets-fread-read-dirs later to maint).
+
+ * Those fetching over protocol v2 from linux-next and other kernel
+ repositories are reporting that v2 often fetches way too much than
+ needed.
+ (merge 11c7f2a30b jn/demote-proto2-from-default later to maint).
+
+ * The upload-pack protocol v2 gave up too early before finding a
+ common ancestor, resulting in a wasteful fetch from a fork of a
+ project. This has been corrected to match the behaviour of v0
+ protocol.
+ (merge 2f0a093dd6 jt/v2-fetch-nego-fix later to maint).
+
+ * The build procedure did not use the libcurl library and its include
+ files correctly for a custom-built installation.
+ (merge 0573831950 jk/build-with-right-curl later to maint).
+
+ * Tighten "git mailinfo" to notice and error out when decoded result
+ contains NUL in it.
+ (merge 3919997447 dd/mailinfo-with-nul later to maint).
+
+ * Fix in-core inconsistency after fetching into a shallow repository
+ that broke the code to write out commit-graph.
+ (merge 37b9dcabfc tb/reset-shallow later to maint).
+
+ * The commit-graph code exhausted file descriptors easily when it
+ does not have to.
+ (merge c8828530b7 tb/commit-graph-fd-exhaustion-fix later to maint).
+
+ * The multi-pack-index left mmapped file descriptors open when it
+ does not have to.
+ (merge 6c7ff7cf7f ds/multi-pack-index later to maint).
+
+ * Recent update to Homebrew used by macOS folks breaks build by
+ moving gettext library and necessary headers.
+ (merge a0b3108618 ds/build-homebrew-gettext-fix later to maint).
+
+ * Incompatible options "--root" and "--fork-point" of "git rebase"
+ have been marked and documented as being incompatible.
+ (merge a35413c378 en/rebase-root-and-fork-point-are-incompatible later to maint).
+
+ * Error and verbose trace messages from "git push" did not redact
+ credential material embedded in URLs.
+ (merge d192fa5006 js/anonymise-push-url-in-errors later to maint).
+
+ * Update the parser used for credential.<URL>.<variable>
+ configuration, to handle <URL>s with '/' in them correctly.
+ (merge b44d0118ac bc/wildcard-credential later to maint).
+
+ * Recent updates broke parsing of "credential.<url>.<key>" where
+ <url> is not a full URL (e.g. [credential "https://"] helper = ...)
+ stopped working, which has been corrected.
+ (merge 9a121b0d22 js/partial-urlmatch-2.17 later to maint).
+ (merge cd93e6c029 js/partial-urlmatch later to maint).
+
+ * Some of the files commit-graph subsystem keeps on disk did not
+ correctly honor the core.sharedRepository settings and some were
+ left read-write.
+
+ * In error messages that "git switch" mentions its option to create a
+ new branch, "-b/-B" options were shown, where "-c/-C" options
+ should be, which has been corrected.
+ (merge 7c16ef7577 dl/switch-c-option-in-error-message later to maint).
+
+ * With the recent tightening of the code that is used to parse
+ various parts of a URL for use in the credential subsystem, a
+ hand-edited credential-store file causes the credential helper to
+ die, which is a bit too harsh to the users. Demote the error
+ behaviour to just ignore and keep using well-formed lines instead.
+ (merge c03859a665 cb/credential-store-ignore-bogus-lines later to maint).
+
+ * The samples in the credential documentation has been updated to
+ make it clear that we depict what would appear in the .git/config
+ file, by adding appropriate quotes as needed..
+ (merge 177681a07e jk/credential-sample-update later to maint).
+
+ * "git branch" and other "for-each-ref" variants accepted multiple
+ --sort=<key> options in the increasing order of precedence, but it
+ had a few breakages around "--ignore-case" handling, and tie-breaking
+ with the refname, which have been fixed.
+ (merge 7c5045fc18 jk/for-each-ref-multi-key-sort-fix later to maint).
+
+ * The coding guideline for shell scripts instructed to refer to a
+ variable with dollar-sign inside arithmetic expansion to work
+ around a bug in old versions of dash, which is a thing of the past.
+ Now we are not forbidden from writing $((var+1)).
+ (merge 32b5fe7f0e jk/arith-expansion-coding-guidelines later to maint).
+
+ * The <stdlib.h> header on NetBSD brings in its own definition of
+ hmac() function (eek), which conflicts with our own and unrelated
+ function with the same name. Our function has been renamed to work
+ around the issue.
+ (merge 3013118eb8 cb/avoid-colliding-with-netbsd-hmac later to maint).
+
+ * The basic test did not honor $TEST_SHELL_PATH setting, which has
+ been corrected.
+ (merge 0555e4af58 cb/t0000-use-the-configured-shell later to maint).
+
+ * Minor in-code comments and documentation updates around credential
+ API.
+ (merge 1aed817f99 cb/credential-doc-fixes later to maint).
+
+ * Teach "am", "commit", "merge" and "rebase", when they are run with
+ the "--quiet" option, to pass "--quiet" down to "gc --auto".
+ (merge 7c3e9e8cfb jc/auto-gc-quiet later to maint).
+
+ * The code to skip unmerged paths in the index when sparse checkout
+ is in use would have made out-of-bound access of the in-core index
+ when the last path was unmerged, which has been corrected.
+
+ * Serving a "git fetch" client over "git://" and "ssh://" protocols
+ using the on-wire protocol version 2 was buggy on the server end
+ when the client needs to make a follow-up request to
+ e.g. auto-follow tags.
+ (merge 08450ef791 cc/upload-pack-v2-fetch-fix later to maint).
+
+ * "git bisect replay" had trouble with input files when they used
+ CRLF line ending, which has been corrected.
+ (merge 6c722cbe5a cw/bisect-replay-with-dos later to maint).
+
+ * "rebase -i" segfaulted when rearranging a sequence that has a
+ fix-up that applies another fix-up (which may or may not be a
+ fix-up of yet another step).
+ (merge 02471e7e20 js/rebase-autosquash-double-fixup-fix later to maint).
+
+ * "git fsck" ensures that the paths recorded in tree objects are
+ sorted and without duplicates, but it failed to notice a case where
+ a blob is followed by entries that sort before a tree with the same
+ name. This has been corrected.
+ (merge 9068cfb20f rs/fsck-duplicate-names-in-trees later to maint).
+
+ * Code clean-up by removing a compatibility implementation of a
+ function we no longer use.
+ (merge 84b0115f0d cb/no-more-gmtime later to maint).
+
+ * When a binary file gets modified and renamed on both sides of history
+ to different locations, both files would be written to the working
+ tree but both would have the contents from "ours". This has been
+ corrected so that the path from each side gets their original content.
+
+ * Fix for a copy-and-paste error introduced during 2.20 era.
+ (merge e68a5272b1 ds/multi-pack-verify later to maint).
+
+ * Update an unconditional use of "grep -a" with a perl script in a test.
+ (merge 1eb7371236 dd/t5703-grep-a-fix later to maint).
+
+ * Other code cleanup, docfix, build fix, etc.
+ (merge 564956f358 jc/maintain-doc later to maint).
+ (merge 7422b2a0a1 sg/commit-slab-clarify-peek later to maint).
+ (merge 9c688735f6 rs/doc-passthru-fetch-options later to maint).
+ (merge 757c2ba3e2 en/oidset-uninclude-hashmap later to maint).
+ (merge 8312aa7d74 jc/config-tar later to maint).
+ (merge d00a5bdd50 ss/submodule-foreach-cb later to maint).
+ (merge 64d1022e14 ar/test-style-fixes later to maint).
+ (merge 4a465443a6 ds/doc-clone-filter later to maint).
+ (merge bb2dbe301b jk/t3419-drop-expensive-tests later to maint).
+ (merge d3507cc712 js/test-junit-finalization-fix later to maint).
+ (merge 2149b6748f bc/faq later to maint).
+ (merge 12dc0879f1 jk/test-cleanup later to maint).
+ (merge 344420bf0f pb/rebase-doc-typofix later to maint).
+ (merge 7cd54d37dc dl/wrapper-fix-indentation later to maint).
+ (merge 78725ebda9 jc/allow-strlen-substitution-in-shell-scripts later to maint).
+ (merge 2ecfcdecc6 jm/gitweb-fastcgi-utf8 later to maint).
+ (merge 0740d0a5d3 jk/oid-array-cleanups later to maint).
+ (merge a1aba0c95c js/t0007-typofix later to maint).
+ (merge 76ba7fa225 ma/config-doc-fix later to maint).
+ (merge 826f0c0df2 js/subtree-doc-update-to-asciidoctor-2 later to maint).
+ (merge 88eaf361e0 eb/mboxrd-doc later to maint).
+ (merge 051cc54941 tm/zsh-complete-switch-restore later to maint).
+ (merge 39102cf4fe ms/doc-revision-illustration-fix later to maint).
+ (merge 4d9378bfad eb/gitweb-more-trailers later to maint).
+ (merge bdccbf7047 mt/doc-worktree-ref later to maint).
+ (merge ce9baf234f dl/push-recurse-submodules-fix later to maint).
+ (merge 4153274052 bc/doc-credential-helper-value later to maint).
+ (merge 5c7bb0146e jc/codingstyle-compare-with-null later to maint).
diff --git a/Documentation/asciidoc.conf b/Documentation/asciidoc.conf
index 8fc4b67..3e4c139 100644
--- a/Documentation/asciidoc.conf
+++ b/Documentation/asciidoc.conf
@@ -31,24 +31,6 @@
endif::backend-docbook[]
ifdef::backend-docbook[]
-ifndef::git-asciidoc-no-roff[]
-# "unbreak" docbook-xsl v1.68 for manpages. v1.69 works with or without this.
-# v1.72 breaks with this because it replaces dots not in roff requests.
-[listingblock]
-<example><title>{title}</title>
-<literallayout class="monospaced">
-ifdef::doctype-manpage[]
- .ft C
-endif::doctype-manpage[]
-|
-ifdef::doctype-manpage[]
- .ft
-endif::doctype-manpage[]
-</literallayout>
-{title#}</example>
-endif::git-asciidoc-no-roff[]
-
-ifdef::git-asciidoc-no-roff[]
ifdef::doctype-manpage[]
# The following two small workarounds insert a simple paragraph after screen
[listingblock]
@@ -67,7 +49,6 @@
{title#}</para></formalpara>
{title%}<simpara></simpara>
endif::doctype-manpage[]
-endif::git-asciidoc-no-roff[]
endif::backend-docbook[]
ifdef::doctype-manpage[]
diff --git a/Documentation/config.txt b/Documentation/config.txt
index 08b13ba..ef0768b 100644
--- a/Documentation/config.txt
+++ b/Documentation/config.txt
@@ -3,11 +3,12 @@
The Git configuration file contains a number of variables that affect
the Git commands' behavior. The files `.git/config` and optionally
-`config.worktree` (see `extensions.worktreeConfig` below) in each
-repository are used to store the configuration for that repository, and
-`$HOME/.gitconfig` is used to store a per-user configuration as
-fallback values for the `.git/config` file. The file `/etc/gitconfig`
-can be used to store a system-wide default configuration.
+`config.worktree` (see the "CONFIGURATION FILE" section of
+linkgit:git-worktree[1]) in each repository are used to store the
+configuration for that repository, and `$HOME/.gitconfig` is used to
+store a per-user configuration as fallback values for the `.git/config`
+file. The file `/etc/gitconfig` can be used to store a system-wide
+default configuration.
The configuration variables are used by both the Git plumbing
and the porcelains. The variables are divided into sections, wherein
@@ -220,12 +221,12 @@
; affected by the condition
[includeIf "gitdir:/path/to/group/"]
path = foo.inc
-----
- ; include only if we are in a worktree where foo-branch is
- ; currently checked out
- [includeIf "onbranch:foo-branch"]
- path = foo.inc
+; include only if we are in a worktree where foo-branch is
+; currently checked out
+[includeIf "onbranch:foo-branch"]
+ path = foo.inc
+----
Values
~~~~~~
@@ -447,6 +448,8 @@
include::config/tag.txt[]
+include::config/tar.txt[]
+
include::config/trace2.txt[]
include::config/transfer.txt[]
diff --git a/Documentation/config/credential.txt b/Documentation/config/credential.txt
index 60fb318..9d01641 100644
--- a/Documentation/config/credential.txt
+++ b/Documentation/config/credential.txt
@@ -1,9 +1,13 @@
credential.helper::
Specify an external helper to be called when a username or
password credential is needed; the helper may consult external
- storage to avoid prompting the user for the credentials. Note
- that multiple helpers may be defined. See linkgit:gitcredentials[7]
- for details.
+ storage to avoid prompting the user for the credentials. This is
+ normally the name of a credential helper with possible
+ arguments, but may also be an absolute path with arguments or, if
+ preceded by `!`, shell commands.
++
+Note that multiple helpers may be defined. See linkgit:gitcredentials[7]
+for details and examples.
credential.useHttpPath::
When acquiring credentials, consider the "path" component of an http
diff --git a/Documentation/config/feature.txt b/Documentation/config/feature.txt
index 875f8c8..4e3a5c0 100644
--- a/Documentation/config/feature.txt
+++ b/Documentation/config/feature.txt
@@ -12,9 +12,6 @@
setting if you are interested in providing feedback on experimental
features. The new default values are:
+
-* `pack.useSparse=true` uses a new algorithm when constructing a pack-file
-which can improve `git push` performance in repos with many files.
-+
* `fetch.negotiationAlgorithm=skipping` may improve fetch negotiation times by
skipping more commits at a time, reducing the number of round trips.
+
diff --git a/Documentation/config/fetch.txt b/Documentation/config/fetch.txt
index f119402..b1a9b14 100644
--- a/Documentation/config/fetch.txt
+++ b/Documentation/config/fetch.txt
@@ -1,11 +1,14 @@
fetch.recurseSubmodules::
- This option can be either set to a boolean value or to 'on-demand'.
+ This option controls whether `git fetch` (and the underlying fetch
+ in `git pull`) will recursively fetch into populated submodules.
+ This option can be set either to a boolean value or to 'on-demand'.
Setting it to a boolean changes the behavior of fetch and pull to
- unconditionally recurse into submodules when set to true or to not
- recurse at all when set to false. When set to 'on-demand' (the default
- value), fetch and pull will only recurse into a populated submodule
- when its superproject retrieves a commit that updates the submodule's
+ recurse unconditionally into submodules when set to true or to not
+ recurse at all when set to false. When set to 'on-demand', fetch and
+ pull will only recurse into a populated submodule when its
+ superproject retrieves a commit that updates the submodule's
reference.
+ Defaults to 'on-demand', or to the value of 'submodule.recurse' if set.
fetch.fsckObjects::
If it is set to true, git-fetch-pack will check all fetched
diff --git a/Documentation/config/format.txt b/Documentation/config/format.txt
index 45c7bd5..564e809 100644
--- a/Documentation/config/format.txt
+++ b/Documentation/config/format.txt
@@ -57,6 +57,11 @@
`.patch`. Use this variable to change that suffix (make sure to
include the dot if you want it).
+format.encodeEmailHeaders::
+ Encode email headers that have non-ASCII characters with
+ "Q-encoding" (described in RFC 2047) for email transmission.
+ Defaults to true.
+
format.pretty::
The default pretty format for log/show/whatchanged command,
See linkgit:git-log[1], linkgit:git-show[1],
diff --git a/Documentation/config/http.txt b/Documentation/config/http.txt
index e806033..3968fbb 100644
--- a/Documentation/config/http.txt
+++ b/Documentation/config/http.txt
@@ -29,6 +29,27 @@
* `ntlm` - NTLM authentication (compare the --ntlm option of `curl(1)`)
--
+http.proxySSLCert::
+ The pathname of a file that stores a client certificate to use to authenticate
+ with an HTTPS proxy. Can be overridden by the `GIT_PROXY_SSL_CERT` environment
+ variable.
+
+http.proxySSLKey::
+ The pathname of a file that stores a private key to use to authenticate with
+ an HTTPS proxy. Can be overridden by the `GIT_PROXY_SSL_KEY` environment
+ variable.
+
+http.proxySSLCertPasswordProtected::
+ Enable Git's password prompt for the proxy SSL certificate. Otherwise OpenSSL
+ will prompt the user, possibly many times, if the certificate or private key
+ is encrypted. Can be overriden by the `GIT_PROXY_SSL_CERT_PASSWORD_PROTECTED`
+ environment variable.
+
+http.proxySSLCAInfo::
+ Pathname to the file containing the certificate bundle that should be used to
+ verify the proxy with when using an HTTPS proxy. Can be overriden by the
+ `GIT_PROXY_SSL_CAINFO` environment variable.
+
http.emptyAuth::
Attempt authentication without seeking a username or password. This
can be used to attempt GSS-Negotiate authentication without specifying
diff --git a/Documentation/config/log.txt b/Documentation/config/log.txt
index e9e1e39..208d5fd 100644
--- a/Documentation/config/log.txt
+++ b/Documentation/config/log.txt
@@ -18,6 +18,12 @@
names are shown. This is the same as the `--decorate` option
of the `git log`.
+log.excludeDecoration::
+ Exclude the specified patterns from the log decorations. This is
+ similar to the `--decorate-refs-exclude` command-line option, but
+ the config option can be overridden by the `--decorate-refs`
+ option.
+
log.follow::
If `true`, `git log` will act as if the `--follow` option was used when
a single <path> is given. This has the same limitations as `--follow`,
diff --git a/Documentation/config/merge.txt b/Documentation/config/merge.txt
index 6a31393..cb2ed58 100644
--- a/Documentation/config/merge.txt
+++ b/Documentation/config/merge.txt
@@ -70,6 +70,16 @@
Whether to print the diffstat between ORIG_HEAD and the merge result
at the end of the merge. True by default.
+merge.autoStash::
+ When set to true, automatically create a temporary stash entry
+ before the operation begins, and apply it after the operation
+ ends. This means that you can run merge on a dirty worktree.
+ However, use with care: the final stash application after a
+ successful merge might result in non-trivial conflicts.
+ This option can be overridden by the `--no-autostash` and
+ `--autostash` options of linkgit:git-merge[1].
+ Defaults to false.
+
merge.tool::
Controls which merge tool is used by linkgit:git-mergetool[1].
The list below shows the valid built-in values.
diff --git a/Documentation/config/pack.txt b/Documentation/config/pack.txt
index 0dac580..837f1b1 100644
--- a/Documentation/config/pack.txt
+++ b/Documentation/config/pack.txt
@@ -119,8 +119,8 @@
objects. This can have significant performance benefits when
computing a pack to send a small change. However, it is possible
that extra objects are added to the pack-file if the included
- commits contain certain types of direct renames. Default is `false`
- unless `feature.experimental` is enabled.
+ commits contain certain types of direct renames. Default is
+ `true`.
pack.writeBitmaps (deprecated)::
This is a deprecated synonym for `repack.writeBitmaps`.
diff --git a/Documentation/config/protocol.txt b/Documentation/config/protocol.txt
index 756591d..0b40141 100644
--- a/Documentation/config/protocol.txt
+++ b/Documentation/config/protocol.txt
@@ -48,7 +48,7 @@
If set, clients will attempt to communicate with a server
using the specified protocol version. If the server does
not support it, communication falls back to version 0.
- If unset, the default is `2`.
+ If unset, the default is `0`.
Supported versions:
+
--
diff --git a/Documentation/config/push.txt b/Documentation/config/push.txt
index 0a7aa32..f5e5b38 100644
--- a/Documentation/config/push.txt
+++ b/Documentation/config/push.txt
@@ -112,3 +112,5 @@
is 'no' then default behavior of ignoring submodules when pushing
is retained. You may override this configuration at time of push by
specifying '--recurse-submodules=check|on-demand|no'.
+ If not set, 'no' is used by default, unless 'submodule.recurse' is
+ set (in which case a 'true' value means 'on-demand').
diff --git a/Documentation/config/stash.txt b/Documentation/config/stash.txt
index abc7ef4..00eb354 100644
--- a/Documentation/config/stash.txt
+++ b/Documentation/config/stash.txt
@@ -1,17 +1,9 @@
stash.useBuiltin::
- Set to `false` to use the legacy shell script implementation of
- linkgit:git-stash[1]. Is `true` by default, which means use
- the built-in rewrite of it in C.
-+
-The C rewrite is first included with Git version 2.22 (and Git for Windows
-version 2.19). This option serves as an escape hatch to re-enable the
-legacy version in case any bugs are found in the rewrite. This option and
-the shell script version of linkgit:git-stash[1] will be removed in some
-future release.
-+
-If you find some reason to set this option to `false`, other than
-one-off testing, you should report the behavior difference as a bug in
-Git (see https://git-scm.com/community for details).
+ Unused configuration variable. Used in Git versions 2.22 to
+ 2.26 as an escape hatch to enable the legacy shellscript
+ implementation of stash. Now the built-in rewrite of it in C
+ is always used. Setting this will emit a warning, to alert any
+ remaining users that setting this now does nothing.
stash.showPatch::
If this is set to true, the `git stash show` command without an
diff --git a/Documentation/config/submodule.txt b/Documentation/config/submodule.txt
index b331771..d7a63c8 100644
--- a/Documentation/config/submodule.txt
+++ b/Documentation/config/submodule.txt
@@ -59,9 +59,17 @@
submodule.recurse::
Specifies if commands recurse into submodules by default. This
- applies to all commands that have a `--recurse-submodules` option,
- except `clone`.
+ applies to all commands that have a `--recurse-submodules` option
+ (`checkout`, `fetch`, `grep`, `pull`, `push`, `read-tree`, `reset`,
+ `restore` and `switch`) except `clone` and `ls-files`.
Defaults to false.
+ When set to true, it can be deactivated via the
+ `--no-recurse-submodules` option. Note that some Git commands
+ lacking this option may call some of the above commands affected by
+ `submodule.recurse`; for instance `git remote update` will call
+ `git fetch` but does not have a `--no-recurse-submodules` option.
+ For these commands a workaround is to temporarily change the
+ configuration value by using `git -c submodule.recurse=0`.
submodule.fetchJobs::
Specifies how many submodules are fetched/cloned at the same time.
diff --git a/Documentation/config/tag.txt b/Documentation/config/tag.txt
index 6d9110d..5062a05 100644
--- a/Documentation/config/tag.txt
+++ b/Documentation/config/tag.txt
@@ -15,10 +15,3 @@
convenient to use an agent to avoid typing your gpg passphrase
several times. Note that this option doesn't affect tag signing
behavior enabled by "-u <keyid>" or "--local-user=<keyid>" options.
-
-tar.umask::
- This variable can be used to restrict the permission bits of
- tar archive entries. The default is 0002, which turns off the
- world write bit. The special value "user" indicates that the
- archiving user's umask will be used instead. See umask(2) and
- linkgit:git-archive[1].
diff --git a/Documentation/config/tar.txt b/Documentation/config/tar.txt
new file mode 100644
index 0000000..de8ff48
--- /dev/null
+++ b/Documentation/config/tar.txt
@@ -0,0 +1,6 @@
+tar.umask::
+ This variable can be used to restrict the permission bits of
+ tar archive entries. The default is 0002, which turns off the
+ world write bit. The special value "user" indicates that the
+ archiving user's umask will be used instead. See umask(2) and
+ linkgit:git-archive[1].
diff --git a/Documentation/config/trace2.txt b/Documentation/config/trace2.txt
index 4ce0b9a..01d3afd 100644
--- a/Documentation/config/trace2.txt
+++ b/Documentation/config/trace2.txt
@@ -48,6 +48,15 @@
May be overridden by the `GIT_TRACE2_CONFIG_PARAMS` environment
variable. Unset by default.
+trace2.envVars::
+ A comma-separated list of "important" environment variables that should
+ be recorded in the trace2 output. For example,
+ `GIT_HTTP_USER_AGENT,GIT_CONFIG` would cause the trace2 output to
+ contain events listing the overrides for HTTP user agent and the
+ location of the Git configuration file (assuming any are set). May be
+ overriden by the `GIT_TRACE2_ENV_VARS` environment variable. Unset by
+ default.
+
trace2.destinationDebug::
Boolean. When true Git will print error messages when a
trace target destination cannot be opened for writing.
diff --git a/Documentation/date-formats.txt b/Documentation/date-formats.txt
index 6926e0a..f1097fa 100644
--- a/Documentation/date-formats.txt
+++ b/Documentation/date-formats.txt
@@ -20,7 +20,9 @@
ISO 8601::
Time and date specified by the ISO 8601 standard, for example
`2005-04-07T22:13:13`. The parser accepts a space instead of the
- `T` character as well.
+ `T` character as well. Fractional parts of a second will be ignored,
+ for example `2005-04-07T22:13:13.019` will be treated as
+ `2005-04-07T22:13:13`.
+
NOTE: In addition, the date part is accepted in the following formats:
`YYYY.MM.DD`, `MM/DD/YYYY` and `DD.MM.YYYY`.
diff --git a/Documentation/fetch-options.txt b/Documentation/fetch-options.txt
index a115a1a..6e2a160 100644
--- a/Documentation/fetch-options.txt
+++ b/Documentation/fetch-options.txt
@@ -61,10 +61,8 @@
See also the `fetch.negotiationAlgorithm` configuration variable
documented in linkgit:git-config[1].
-ifndef::git-pull[]
--dry-run::
Show what would be done, without making any changes.
-endif::git-pull[]
-f::
--force::
@@ -95,6 +93,7 @@
--[no-]write-commit-graph::
Write a commit-graph after fetching. This overrides the config
setting `fetch.writeCommitGraph`.
+endif::git-pull[]
-p::
--prune::
@@ -107,6 +106,7 @@
was cloned with the --mirror option), then they are also
subject to pruning. Supplying `--prune-tags` is a shorthand for
providing the tag refspec.
+ifndef::git-pull[]
+
See the PRUNING section below for more details.
@@ -133,7 +133,6 @@
behavior for a remote may be specified with the remote.<name>.tagOpt
setting. See linkgit:git-config[1].
-ifndef::git-pull[]
--refmap=<refspec>::
When fetching refs listed on the command line, use the
specified refspec (can be given more than once) to map the
@@ -154,6 +153,7 @@
is used (though tags may be pruned anyway if they are also the
destination of an explicit refspec; see `--prune`).
+ifndef::git-pull[]
--recurse-submodules[=yes|on-demand|no]::
This option controls if and under what conditions new commits of
populated submodules should be fetched too. It can be used as a
@@ -163,7 +163,9 @@
value. Use 'on-demand' to only recurse into a populated submodule
when the superproject retrieves a commit that updates the submodule's
reference to a commit that isn't already in the local submodule
- clone.
+ clone. By default, 'on-demand' is used, unless
+ `fetch.recurseSubmodules` is set (see linkgit:git-config[1]).
+endif::git-pull[]
-j::
--jobs=<n>::
@@ -177,9 +179,11 @@
Typically, parallel recursive and multi-remote fetches will be faster. By
default fetches are performed sequentially, not in parallel.
+ifndef::git-pull[]
--no-recurse-submodules::
Disable recursive fetching of submodules (this has the same effect as
using the `--recurse-submodules=no` option).
+endif::git-pull[]
--set-upstream::
If the remote is fetched successfully, pull and add upstream
@@ -188,6 +192,7 @@
see `branch.<name>.merge` and `branch.<name>.remote` in
linkgit:git-config[1].
+ifndef::git-pull[]
--submodule-prefix=<path>::
Prepend <path> to paths printed in informative messages
such as "Fetching submodule foo". This option is used
@@ -200,7 +205,6 @@
recursion (such as settings in linkgit:gitmodules[5] and
linkgit:git-config[1]) override this option, as does
specifying --[no-]recurse-submodules directly.
-endif::git-pull[]
-u::
--update-head-ok::
@@ -210,6 +214,7 @@
to communicate with 'git fetch', and unless you are
implementing your own Porcelain you are not supposed to
use it.
+endif::git-pull[]
--upload-pack <upload-pack>::
When given, and the repository to fetch from is handled
diff --git a/Documentation/git-am.txt b/Documentation/git-am.txt
index ab5754e..38c0852 100644
--- a/Documentation/git-am.txt
+++ b/Documentation/git-am.txt
@@ -148,9 +148,12 @@
-S[<keyid>]::
--gpg-sign[=<keyid>]::
+--no-gpg-sign::
GPG-sign commits. The `keyid` argument is optional and
defaults to the committer identity; if specified, it must be
- stuck to the option without a space.
+ stuck to the option without a space. `--no-gpg-sign` is useful to
+ countermand both `commit.gpgSign` configuration variable, and
+ earlier `--gpg-sign`.
--continue::
-r::
diff --git a/Documentation/git-bugreport.txt b/Documentation/git-bugreport.txt
new file mode 100644
index 0000000..9edad66
--- /dev/null
+++ b/Documentation/git-bugreport.txt
@@ -0,0 +1,53 @@
+git-bugreport(1)
+================
+
+NAME
+----
+git-bugreport - Collect information for user to file a bug report
+
+SYNOPSIS
+--------
+[verse]
+'git bugreport' [(-o | --output-directory) <path>] [(-s | --suffix) <format>]
+
+DESCRIPTION
+-----------
+Captures information about the user's machine, Git client, and repository state,
+as well as a form requesting information about the behavior the user observed,
+into a single text file which the user can then share, for example to the Git
+mailing list, in order to report an observed bug.
+
+The following information is requested from the user:
+
+ - Reproduction steps
+ - Expected behavior
+ - Actual behavior
+
+The following information is captured automatically:
+
+ - 'git version --build-options'
+ - uname sysname, release, version, and machine strings
+ - Compiler-specific info string
+ - A list of enabled hooks
+
+This tool is invoked via the typical Git setup process, which means that in some
+cases, it might not be able to launch - for example, if a relevant config file
+is unreadable. In this kind of scenario, it may be helpful to manually gather
+the kind of information listed above when manually asking for help.
+
+OPTIONS
+-------
+-o <path>::
+--output-directory <path>::
+ Place the resulting bug report file in `<path>` instead of the root of
+ the Git repository.
+
+-s <format>::
+--suffix <format>::
+ Specify an alternate suffix for the bugreport name, to create a file
+ named 'git-bugreport-<formatted suffix>'. This should take the form of a
+ strftime(3) format string; the current local time will be used.
+
+GIT
+---
+Part of the linkgit:git[1] suite
diff --git a/Documentation/git-checkout.txt b/Documentation/git-checkout.txt
index c8fb995..5b697ee 100644
--- a/Documentation/git-checkout.txt
+++ b/Documentation/git-checkout.txt
@@ -292,11 +292,11 @@
--recurse-submodules::
--no-recurse-submodules::
- Using `--recurse-submodules` will update the content of all initialized
+ Using `--recurse-submodules` will update the content of all active
submodules according to the commit recorded in the superproject. If
local modifications in a submodule would be overwritten the checkout
will fail unless `-f` is used. If nothing (or `--no-recurse-submodules`)
- is used, the work trees of submodules will not be updated.
+ is used, submodules working trees will not be updated.
Just like linkgit:git-submodule[1], this will detach `HEAD` of the
submodule.
diff --git a/Documentation/git-cherry-pick.txt b/Documentation/git-cherry-pick.txt
index 83ce51a..75feeef 100644
--- a/Documentation/git-cherry-pick.txt
+++ b/Documentation/git-cherry-pick.txt
@@ -109,9 +109,12 @@
-S[<keyid>]::
--gpg-sign[=<keyid>]::
+--no-gpg-sign::
GPG-sign commits. The `keyid` argument is optional and
defaults to the committer identity; if specified, it must be
- stuck to the option without a space.
+ stuck to the option without a space. `--no-gpg-sign` is useful to
+ countermand both `commit.gpgSign` configuration variable, and
+ earlier `--gpg-sign`.
--ff::
If the current HEAD is the same as the parent of the
diff --git a/Documentation/git-clone.txt b/Documentation/git-clone.txt
index bf24f18..08d6045 100644
--- a/Documentation/git-clone.txt
+++ b/Documentation/git-clone.txt
@@ -15,7 +15,8 @@
[--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] [--] <repository>
+ [--[no-]remote-submodules] [--jobs <n>] [--sparse]
+ [--filter=<filter>] [--] <repository>
[<directory>]
DESCRIPTION
@@ -162,6 +163,16 @@
of the repository. The sparse-checkout file can be
modified to grow the working directory as needed.
+--filter=<filter-spec>::
+ Use the partial clone feature and request that the server sends
+ a subset of reachable objects according to a given object filter.
+ When using `--filter`, the supplied `<filter-spec>` is used for
+ the partial clone filter. For example, `--filter=blob:none` will
+ filter out all blobs (file contents) until needed by Git. Also,
+ `--filter=blob:limit=<size>` will filter out all blobs of size
+ at least `<size>`. For more details on filter specifications, see
+ the `--filter` option in linkgit:git-rev-list[1].
+
--mirror::
Set up a mirror of the source repository. This implies `--bare`.
Compared to `--bare`, `--mirror` not only maps local branches of the
diff --git a/Documentation/git-commit-graph.txt b/Documentation/git-commit-graph.txt
index 28d1fee..a3d9967 100644
--- a/Documentation/git-commit-graph.txt
+++ b/Documentation/git-commit-graph.txt
@@ -57,11 +57,24 @@
With the `--append` option, include all commits that are present in the
existing commit-graph file.
+
-With the `--split` option, write the commit-graph as a chain of multiple
-commit-graph files stored in `<dir>/info/commit-graphs`. The new commits
-not already in the commit-graph are added in a new "tip" file. This file
-is merged with the existing file if the following merge conditions are
-met:
+With the `--changed-paths` option, compute and write information about the
+paths changed between a commit and its first parent. This operation can
+take a while on large repositories. It provides significant performance gains
+for getting history of a directory or a file with `git log -- <path>`.
++
+With the `--split[=<strategy>]` option, write the commit-graph as a
+chain of multiple commit-graph files stored in
+`<dir>/info/commit-graphs`. Commit-graph layers are merged based on the
+strategy and other splitting options. The new commits not already in the
+commit-graph are added in a new "tip" file. This file is merged with the
+existing file if the following merge conditions are met:
++
+* If `--split=no-merge` is specified, a merge is never performed, and
+the remaining options are ignored. `--split=replace` overwrites the
+existing chain with a new one. A bare `--split` defers to the remaining
+options. (Note that merging a chain of commit graphs replaces the
+existing chain with a length-1 chain where the first and only
+incremental holds the entire graph).
+
* If `--size-multiple=<X>` is not specified, let `X` equal 2. If the new
tip file would have `N` commits and the previous tip has `M` commits and
diff --git a/Documentation/git-commit-tree.txt b/Documentation/git-commit-tree.txt
index ec15ee8..2e2c581 100644
--- a/Documentation/git-commit-tree.txt
+++ b/Documentation/git-commit-tree.txt
@@ -61,13 +61,11 @@
-S[<keyid>]::
--gpg-sign[=<keyid>]::
+--no-gpg-sign::
GPG-sign commits. The `keyid` argument is optional and
defaults to the committer identity; if specified, it must be
- stuck to the option without a space.
-
---no-gpg-sign::
- Do not GPG-sign commit, to countermand a `--gpg-sign` option
- given earlier on the command line.
+ stuck to the option without a space. `--no-gpg-sign` is useful to
+ countermand a `--gpg-sign` option given earlier on the command line.
Commit Information
------------------
diff --git a/Documentation/git-commit.txt b/Documentation/git-commit.txt
index 13f6539..a3baea3 100644
--- a/Documentation/git-commit.txt
+++ b/Documentation/git-commit.txt
@@ -348,13 +348,12 @@
-S[<keyid>]::
--gpg-sign[=<keyid>]::
+--no-gpg-sign::
GPG-sign commits. The `keyid` argument is optional and
defaults to the committer identity; if specified, it must be
- stuck to the option without a space.
-
---no-gpg-sign::
- Countermand `commit.gpgSign` configuration variable that is
- set to force each and every commit to be signed.
+ stuck to the option without a space. `--no-gpg-sign` is useful to
+ countermand both `commit.gpgSign` configuration variable, and
+ earlier `--gpg-sign`.
\--::
Do not interpret any more arguments as options.
diff --git a/Documentation/git-credential-store.txt b/Documentation/git-credential-store.txt
index 693dd9d..76b0798 100644
--- a/Documentation/git-credential-store.txt
+++ b/Documentation/git-credential-store.txt
@@ -94,6 +94,10 @@
https://user:pass@example.com
------------------------------
+No other kinds of lines (e.g. empty lines or comment lines) are
+allowed in the file, even though some may be silently ignored. Do
+not view or edit the file with editors.
+
When Git needs authentication for a particular URL context,
credential-store will consider that context a pattern to match against
each entry in the credentials file. If the protocol, hostname, and
diff --git a/Documentation/git-credential.txt b/Documentation/git-credential.txt
index 6f0c7ca..31c81c4 100644
--- a/Documentation/git-credential.txt
+++ b/Documentation/git-credential.txt
@@ -103,17 +103,20 @@
`git credential` reads and/or writes (depending on the action used)
credential information in its standard input/output. This information
can correspond either to keys for which `git credential` will obtain
-the login/password information (e.g. host, protocol, path), or to the
-actual credential data to be obtained (login/password).
+the login information (e.g. host, protocol, path), or to the actual
+credential data to be obtained (username/password).
The credential is split into a set of named attributes, with one
-attribute per line. Each attribute is
-specified by a key-value pair, separated by an `=` (equals) sign,
-followed by a newline. The key may contain any bytes except `=`,
-newline, or NUL. The value may contain any bytes except newline or NUL.
+attribute per line. Each attribute is specified by a key-value pair,
+separated by an `=` (equals) sign, followed by a newline.
+
+The key may contain any bytes except `=`, newline, or NUL. The value may
+contain any bytes except newline or NUL.
+
In both cases, all bytes are treated as-is (i.e., there is no quoting,
and one cannot transmit a value with newline or NUL in it). The list of
attributes is terminated by a blank line or end-of-file.
+
Git understands the following attributes:
`protocol`::
@@ -123,7 +126,8 @@
`host`::
- The remote hostname for a network credential.
+ The remote hostname for a network credential. This includes
+ the port number if one was specified (e.g., "example.com:8088").
`path`::
@@ -134,7 +138,7 @@
`username`::
The credential's username, if we already have one (e.g., from a
- URL, from the user, or from a previously run helper).
+ URL, the configuration, the user, or from a previously run helper).
`password`::
@@ -146,8 +150,12 @@
value is parsed as a URL and treated as if its constituent parts
were read (e.g., `url=https://example.com` would behave as if
`protocol=https` and `host=example.com` had been provided). This
- can help callers avoid parsing URLs themselves. Note that any
- components which are missing from the URL (e.g., there is no
- username in the example above) will be set to empty; if you want
- to provide a URL and override some attributes, provide the URL
- attribute first, followed by any overrides.
+ can help callers avoid parsing URLs themselves.
++
+Note that specifying a protocol is mandatory and if the URL
+doesn't specify a hostname (e.g., "cert:///path/to/file") the
+credential will contain a hostname attribute whose value is an
+empty string.
++
+Components which are missing from the URL (e.g., there is no
+username in the example above) will be left unset.
diff --git a/Documentation/git-fast-import.txt b/Documentation/git-fast-import.txt
index 7889f95..77c6b3d 100644
--- a/Documentation/git-fast-import.txt
+++ b/Documentation/git-fast-import.txt
@@ -122,6 +122,26 @@
Relative and non-relative marks may be combined by interweaving
--(no-)-relative-marks with the --(import|export)-marks= options.
+Submodule Rewriting
+~~~~~~~~~~~~~~~~~~~
+
+--rewrite-submodules-from=<name>:<file>::
+--rewrite-submodules-to=<name>:<file>::
+ Rewrite the object IDs for the submodule specified by <name> from the values
+ used in the from <file> to those used in the to <file>. The from marks should
+ have been created by `git fast-export`, and the to marks should have been
+ created by `git fast-import` when importing that same submodule.
++
+<name> may be any arbitrary string not containing a colon character, but the
+same value must be used with both options when specifying corresponding marks.
+Multiple submodules may be specified with different values for <name>. It is an
+error not to use these options in corresponding pairs.
++
+These options are primarily useful when converting a repository from one hash
+algorithm to another; without them, fast-import will fail if it encounters a
+submodule because it has no way of writing the object ID into the new hash
+algorithm.
+
Performance and Compression Tuning
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
diff --git a/Documentation/git-format-patch.txt b/Documentation/git-format-patch.txt
index 0d4f895..0f81d04 100644
--- a/Documentation/git-format-patch.txt
+++ b/Documentation/git-format-patch.txt
@@ -24,6 +24,7 @@
[(--reroll-count|-v) <n>]
[--to=<email>] [--cc=<email>]
[--[no-]cover-letter] [--quiet]
+ [--[no-]encode-email-headers]
[--no-notes | --notes[=<ref>]]
[--interdiff=<previous>]
[--range-diff=<previous> [--creation-factor=<percent>]]
@@ -253,6 +254,13 @@
containing the branch description, shortlog and the overall diffstat. You can
fill in a description in the file before sending it out.
+--encode-email-headers::
+--no-encode-email-headers::
+ Encode email headers that have non-ASCII characters with
+ "Q-encoding" (described in RFC 2047), instead of outputting the
+ headers verbatim. Defaults to the value of the
+ `format.encodeEmailHeaders` configuration variable.
+
--interdiff=<previous>::
As a reviewer aid, insert an interdiff into the cover letter,
or as commentary of the lone patch of a 1-patch series, showing
diff --git a/Documentation/git-grep.txt b/Documentation/git-grep.txt
index ddb6acc..a7f9bc9 100644
--- a/Documentation/git-grep.txt
+++ b/Documentation/git-grep.txt
@@ -93,7 +93,7 @@
with `--no-index`.
--recurse-submodules::
- Recursively search in each submodule that has been initialized and
+ Recursively search in each submodule that is active and
checked out in the repository. When used in combination with the
<tree> option the prefix of all submodule output will be the name of
the parent project's <tree> object. This option has no effect
@@ -206,8 +206,10 @@
-z::
--null::
- Output \0 instead of the character that normally follows a
- file name.
+ Use \0 as the delimiter for pathnames in the output, and print
+ them verbatim. Without this option, pathnames with "unusual"
+ characters are quoted as explained for the configuration
+ variable core.quotePath (see git-config(1)).
-o::
--only-matching::
diff --git a/Documentation/git-init.txt b/Documentation/git-init.txt
index 32880aa..adc6adf 100644
--- a/Documentation/git-init.txt
+++ b/Documentation/git-init.txt
@@ -10,7 +10,7 @@
--------
[verse]
'git init' [-q | --quiet] [--bare] [--template=<template_directory>]
- [--separate-git-dir <git dir>]
+ [--separate-git-dir <git dir>] [--object-format=<format]
[--shared[=<permissions>]] [directory]
@@ -48,6 +48,11 @@
Create a bare repository. If `GIT_DIR` environment is not set, it is set to the
current working directory.
+--object-format=<format>::
+
+Specify the given object format (hash algorithm) for the repository. The valid
+values are 'sha1' and (if enabled) 'sha256'. 'sha1' is the default.
+
--template=<template_directory>::
Specify the directory from which templates will be used. (See the "TEMPLATE
diff --git a/Documentation/git-log.txt b/Documentation/git-log.txt
index bed09bb..20e6d21 100644
--- a/Documentation/git-log.txt
+++ b/Documentation/git-log.txt
@@ -43,12 +43,16 @@
If no `--decorate-refs` is given, pretend as if all refs were
included. For each candidate, do not use it for decoration if it
matches any patterns given to `--decorate-refs-exclude` or if it
- doesn't match any of the patterns given to `--decorate-refs`.
+ doesn't match any of the patterns given to `--decorate-refs`. The
+ `log.excludeDecoration` config option allows excluding refs from
+ the decorations, but an explicit `--decorate-refs` pattern will
+ override a match in `log.excludeDecoration`.
--source::
Print out the ref name given on the command line by which each
commit was reached.
+--[no-]mailmap::
--[no-]use-mailmap::
Use mailmap file to map author and committer names and email
addresses to canonical real names and email addresses. See
diff --git a/Documentation/git-ls-files.txt b/Documentation/git-ls-files.txt
index 8461c0e..3cb2ebb 100644
--- a/Documentation/git-ls-files.txt
+++ b/Documentation/git-ls-files.txt
@@ -148,7 +148,7 @@
top directory.
--recurse-submodules::
- Recursively calls ls-files on each submodule in the repository.
+ Recursively calls ls-files on each active submodule in the repository.
Currently there is only support for the --cached mode.
--abbrev[=<n>]::
diff --git a/Documentation/git-merge.txt b/Documentation/git-merge.txt
index 092529c..3819fad 100644
--- a/Documentation/git-merge.txt
+++ b/Documentation/git-merge.txt
@@ -94,7 +94,8 @@
--abort::
Abort the current conflict resolution process, and
- try to reconstruct the pre-merge state.
+ try to reconstruct the pre-merge state. If an autostash entry is
+ present, apply it to the worktree.
+
If there were uncommitted worktree changes present when the merge
started, 'git merge --abort' will in some cases be unable to
@@ -102,11 +103,15 @@
commit or stash your changes before running 'git merge'.
+
'git merge --abort' is equivalent to 'git reset --merge' when
-`MERGE_HEAD` is present.
+`MERGE_HEAD` is present unless `MERGE_AUTOSTASH` is also present in
+which case 'git merge --abort' applies the stash entry to the worktree
+whereas 'git reset --merge' will save the stashed changes in the stash
+list.
--quit::
Forget about the current merge in progress. Leave the index
- and the working tree as-is.
+ and the working tree as-is. If `MERGE_AUTOSTASH` is present, the
+ stash entry will be saved to the stash list.
--continue::
After a 'git merge' stops due to conflicts you can conclude the
diff --git a/Documentation/git-multi-pack-index.txt b/Documentation/git-multi-pack-index.txt
index 642d9ac..0c66194 100644
--- a/Documentation/git-multi-pack-index.txt
+++ b/Documentation/git-multi-pack-index.txt
@@ -56,6 +56,9 @@
file is created, rewrite the multi-pack-index to reference the
new pack-file. A later run of 'git multi-pack-index expire' will
delete the pack-files that were part of this batch.
++
+If `repack.packKeptObjects` is `false`, then any pack-files with an
+associated `.keep` file will not be selected for the batch to repack.
EXAMPLES
diff --git a/Documentation/git-p4.txt b/Documentation/git-p4.txt
index 3494a1d..dab9609 100644
--- a/Documentation/git-p4.txt
+++ b/Documentation/git-p4.txt
@@ -374,14 +374,55 @@
been submitted. Implies --disable-rebase. Can also be set with
git-p4.disableP4Sync. Sync with origin/master still goes ahead if possible.
-Hook for submit
-~~~~~~~~~~~~~~~
+Hooks for submit
+----------------
+
+p4-pre-submit
+~~~~~~~~~~~~~
+
The `p4-pre-submit` hook is executed if it exists and is executable.
The hook takes no parameters and nothing from standard input. Exiting with
non-zero status from this script prevents `git-p4 submit` from launching.
+It can be bypassed with the `--no-verify` command line option.
One usage scenario is to run unit tests in the hook.
+p4-prepare-changelist
+~~~~~~~~~~~~~~~~~~~~~
+
+The `p4-prepare-changelist` hook is executed right after preparing
+the default changelist message and before the editor is started.
+It takes one parameter, the name of the file that contains the
+changelist text. Exiting with a non-zero status from the script
+will abort the process.
+
+The purpose of the hook is to edit the message file in place,
+and it is not supressed by the `--no-verify` option. This hook
+is called even if `--prepare-p4-only` is set.
+
+p4-changelist
+~~~~~~~~~~~~~
+
+The `p4-changelist` hook is executed after the changelist
+message has been edited by the user. It can be bypassed with the
+`--no-verify` option. It takes a single parameter, the name
+of the file that holds the proposed changelist text. Exiting
+with a non-zero status causes the command to abort.
+
+The hook is allowed to edit the changelist file and can be used
+to normalize the text into some project standard format. It can
+also be used to refuse the Submit after inspect the message file.
+
+p4-post-changelist
+~~~~~~~~~~~~~~~~~~
+
+The `p4-post-changelist` hook is invoked after the submit has
+successfully occured in P4. It takes no parameters and is meant
+primarily for notification and cannot affect the outcome of the
+git p4 submit action.
+
+
+
Rebase options
~~~~~~~~~~~~~~
These options can be used to modify 'git p4 rebase' behavior.
diff --git a/Documentation/git-pack-objects.txt b/Documentation/git-pack-objects.txt
index fecdf26..eaa2f2a 100644
--- a/Documentation/git-pack-objects.txt
+++ b/Documentation/git-pack-objects.txt
@@ -14,7 +14,7 @@
[--local] [--incremental] [--window=<n>] [--depth=<n>]
[--revs [--unpacked | --all]] [--keep-pack=<pack-name>]
[--stdout [--filter=<filter-spec>] | base-name]
- [--shallow] [--keep-true-parents] [--sparse] < object-list
+ [--shallow] [--keep-true-parents] [--[no-]sparse] < object-list
DESCRIPTION
@@ -196,14 +196,16 @@
Add --no-reuse-object if you want to force a uniform compression
level on all data no matter the source.
---sparse::
- Use the "sparse" algorithm to determine which objects to include in
+--[no-]sparse::
+ Toggle the "sparse" algorithm to determine which objects to include in
the pack, when combined with the "--revs" option. This algorithm
only walks trees that appear in paths that introduce new objects.
This can have significant performance benefits when computing
a pack to send a small change. However, it is possible that extra
objects are added to the pack-file if the included commits contain
- certain types of direct renames.
+ certain types of direct renames. If this option is not included,
+ it defaults to the value of `pack.useSparse`, which is true unless
+ otherwise specified.
--thin::
Create a "thin" pack by omitting the common objects between a
diff --git a/Documentation/git-pull.txt b/Documentation/git-pull.txt
index dfb901f..5c3fb67 100644
--- a/Documentation/git-pull.txt
+++ b/Documentation/git-pull.txt
@@ -85,8 +85,9 @@
Pass --verbose to git-fetch and git-merge.
--[no-]recurse-submodules[=yes|on-demand|no]::
- This option controls if new commits of all populated submodules should
- be fetched and updated, too (see linkgit:git-config[1] and
+ This option controls if new commits of populated submodules should
+ be fetched, and if the working trees of active submodules should be
+ updated, too (see linkgit:git-fetch[1], linkgit:git-config[1] and
linkgit:gitmodules[5]).
+
If the checkout is done via rebase, local submodule commits are rebased as well.
@@ -133,15 +134,6 @@
--no-rebase::
Override earlier --rebase.
---autostash::
---no-autostash::
- Before starting rebase, stash local modifications away (see
- linkgit:git-stash[1]) if needed, and apply the stash entry when
- done. `--no-autostash` is useful to override the `rebase.autoStash`
- configuration variable (see linkgit:git-config[1]).
-+
-This option is only valid when "--rebase" is used.
-
Options related to fetching
~~~~~~~~~~~~~~~~~~~~~~~~~~~
@@ -229,9 +221,9 @@
$ git pull origin next
------------------------------------------------
+
-This leaves a copy of `next` temporarily in FETCH_HEAD, but
-does not update any remote-tracking branches. Using remote-tracking
-branches, the same can be done by invoking fetch and merge:
+This leaves a copy of `next` temporarily in FETCH_HEAD, and
+updates the remote-tracking branch `origin/next`.
+The same can be done by invoking fetch and merge:
+
------------------------------------------------
$ git fetch origin
diff --git a/Documentation/git-read-tree.txt b/Documentation/git-read-tree.txt
index da33f84..5fa8bab 100644
--- a/Documentation/git-read-tree.txt
+++ b/Documentation/git-read-tree.txt
@@ -116,9 +116,9 @@
located in.
--[no-]recurse-submodules::
- Using --recurse-submodules will update the content of all initialized
+ Using --recurse-submodules will update the content of all active
submodules according to the commit recorded in the superproject by
- calling read-tree recursively, also setting the submodules HEAD to be
+ calling read-tree recursively, also setting the submodules' HEAD to be
detached at that commit.
--no-sparse-checkout::
diff --git a/Documentation/git-rebase.txt b/Documentation/git-rebase.txt
index f7a6033..4624cfd 100644
--- a/Documentation/git-rebase.txt
+++ b/Documentation/git-rebase.txt
@@ -256,7 +256,8 @@
--quit::
Abort the rebase operation but HEAD is not reset back to the
original branch. The index and working tree are also left
- unchanged as a result.
+ unchanged as a result. If a temporary stash entry was created
+ using --autostash, it will be saved to the stash list.
--apply:
Use applying strategies to rebase (calling `git-am`
@@ -277,20 +278,51 @@
Other options, like --exec, will use the default of drop unless
-i/--interactive is explicitly specified.
+
-Note that commits which start empty are kept, and commits which are
-clean cherry-picks (as determined by `git log --cherry-mark ...`) are
-always dropped.
+Note that commits which start empty are kept (unless --no-keep-empty
+is specified), and commits which are clean cherry-picks (as determined
+by `git log --cherry-mark ...`) are detected and dropped as a
+preliminary step (unless --reapply-cherry-picks is passed).
+
See also INCOMPATIBLE OPTIONS below.
+--no-keep-empty::
--keep-empty::
- No-op. Rebasing commits that started empty (had no change
- relative to their parent) used to fail and this option would
- override that behavior, allowing commits with empty changes to
- be rebased. Now commits with no changes do not cause rebasing
- to halt.
+ Do not keep commits that start empty before the rebase
+ (i.e. that do not change anything from its parent) in the
+ result. The default is to keep commits which start empty,
+ since creating such commits requires passing the --allow-empty
+ override flag to `git commit`, signifying that a user is very
+ intentionally creating such a commit and thus wants to keep
+ it.
+
-See also BEHAVIORAL DIFFERENCES and INCOMPATIBLE OPTIONS below.
+Usage of this flag will probably be rare, since you can get rid of
+commits that start empty by just firing up an interactive rebase and
+removing the lines corresponding to the commits you don't want. This
+flag exists as a convenient shortcut, such as for cases where external
+tools generate many empty commits and you want them all removed.
++
+For commits which do not start empty but become empty after rebasing,
+see the --empty flag.
++
+See also INCOMPATIBLE OPTIONS below.
+
+--reapply-cherry-picks::
+--no-reapply-cherry-picks::
+ Reapply all clean cherry-picks of any upstream commit instead
+ of preemptively dropping them. (If these commits then become
+ empty after rebasing, because they contain a subset of already
+ upstream changes, the behavior towards them is controlled by
+ the `--empty` flag.)
++
+By default (or if `--no-reapply-cherry-picks` is given), these commits
+will be automatically dropped. Because this necessitates reading all
+upstream commits, this can be expensive in repos with a large number
+of upstream commits that need to be read.
++
+`--reapply-cherry-picks` allows rebase to forgo reading all upstream
+commits, potentially improving performance.
++
+See also INCOMPATIBLE OPTIONS below.
--allow-empty-message::
No-op. Rebasing commits with an empty message used to fail
@@ -354,9 +386,12 @@
-S[<keyid>]::
--gpg-sign[=<keyid>]::
+--no-gpg-sign::
GPG-sign commits. The `keyid` argument is optional and
defaults to the committer identity; if specified, it must be
- stuck to the option without a space.
+ stuck to the option without a space. `--no-gpg-sign` is useful to
+ countermand both `commit.gpgSign` configuration variable, and
+ earlier `--gpg-sign`.
-q::
--quiet::
@@ -414,12 +449,14 @@
<branch>` command (see linkgit:git-merge-base[1]). If 'fork_point'
ends up being empty, the <upstream> will be used as a fallback.
+
-If either <upstream> or --root is given on the command line, then the
-default is `--no-fork-point`, otherwise the default is `--fork-point`.
+If <upstream> is given on the command line, then the default is
+`--no-fork-point`, otherwise the default is `--fork-point`.
+
If your branch was based on <upstream> but <upstream> was rewound and
your branch contains commits which were dropped, this option can be used
with `--keep-base` in order to drop those commits from your branch.
++
+See also INCOMPATIBLE OPTIONS below.
--ignore-whitespace::
--whitespace=<option>::
@@ -587,8 +624,9 @@
* --preserve-merges
* --interactive
* --exec
- * --keep-empty
+ * --no-keep-empty
* --empty=
+ * --reapply-cherry-picks
* --edit-todo
* --root when used in combination with --onto
@@ -600,12 +638,13 @@
* --preserve-merges and --empty=
* --keep-base and --onto
* --keep-base and --root
+ * --fork-point and --root
BEHAVIORAL DIFFERENCES
-----------------------
git rebase has two primary backends: apply and merge. (The apply
-backend used to known as the 'am' backend, but the name led to
+backend used to be known as the 'am' backend, but the name led to
confusion as it looks like a verb instead of a noun. Also, the merge
backend used to be known as the interactive backend, but it is now
used for non-interactive cases as well. Both were renamed based on
@@ -620,12 +659,15 @@
also drops commits that become empty and has no option for controlling
this behavior.
-The merge backend keeps intentionally empty commits. Similar to the
-apply backend, by default the merge backend drops commits that become
-empty unless -i/--interactive is specified (in which case it stops and
-asks the user what to do). The merge backend also has an
---empty={drop,keep,ask} option for changing the behavior of handling
-commits that become empty.
+The merge backend keeps intentionally empty commits by default (though
+with -i they are marked as empty in the todo list editor, or they can
+be dropped automatically with --no-keep-empty).
+
+Similar to the apply backend, by default the merge backend drops
+commits that become empty unless -i/--interactive is specified (in
+which case it stops and asks the user what to do). The merge backend
+also has an --empty={drop,keep,ask} option for changing the behavior
+of handling commits that become empty.
Directory rename detection
~~~~~~~~~~~~~~~~~~~~~~~~~~
@@ -684,9 +726,17 @@
~~~~~
The apply backend has not traditionally called the post-commit hook,
-while the merge backend has. However, this was by accident of
-implementation rather than by design. Both backends should have the
-same behavior, though it is not clear which one is correct.
+while the merge backend has. Both have called the post-checkout hook,
+though the merge backend has squelched its output. Further, both
+backends only call the post-checkout hook with the starting point
+commit of the rebase, not the intermediate commits nor the final
+commit. In each case, the calling of these hooks was by accident of
+implementation rather than by design (both backends were originally
+implemented as shell scripts and happened to invoke other commands
+like 'git checkout' or 'git commit' that would call the hooks). Both
+backends should have the same behavior, though it is not entirely
+clear which, if any, is correct. We will likely make rebase stop
+calling either of these hooks in the future.
Interruptability
~~~~~~~~~~~~~~~~
@@ -1002,7 +1052,8 @@
'subsystem' did.
In that case, the fix is easy because 'git rebase' knows to skip
-changes that are already present in the new upstream. So if you say
+changes that are already present in the new upstream (unless
+`--reapply-cherry-picks` is given). So if you say
(assuming you're on 'topic')
------------
$ git rebase subsystem
diff --git a/Documentation/git-reset.txt b/Documentation/git-reset.txt
index 932080c..252e2d4 100644
--- a/Documentation/git-reset.txt
+++ b/Documentation/git-reset.txt
@@ -87,6 +87,12 @@
different between `<commit>` and `HEAD`.
If a file that is different between `<commit>` and `HEAD` has local
changes, reset is aborted.
+
+--[no-]recurse-submodules::
+ When the working tree is updated, using --recurse-submodules will
+ also recursively reset the working tree of all active submodules
+ according to the commit recorded in the superproject, also setting
+ the submodules' HEAD to be detached at that commit.
--
See "Reset, restore and revert" in linkgit:git[1] for the differences
diff --git a/Documentation/git-restore.txt b/Documentation/git-restore.txt
index 5bf60d4..84c6c40 100644
--- a/Documentation/git-restore.txt
+++ b/Documentation/git-restore.txt
@@ -22,9 +22,8 @@
`--staged`, or restore both the working tree and the index with
`--staged --worktree`.
-By default, the restore sources for working tree and the index are the
-index and `HEAD` respectively. `--source` could be used to specify a
-commit as the restore source.
+By default, if `--staged` is given, the contents are restored from `HEAD`,
+otherwise from the index. Use `--source` to restore from a different commit.
See "Reset, restore and revert" in linkgit:git[1] for the differences
between the three commands.
@@ -39,10 +38,8 @@
tree. It is common to specify the source tree by naming a
commit, branch or tag associated with it.
+
-If not specified, the default restore source for the working tree is
-the index, and the default restore source for the index is
-`HEAD`. When both `--staged` and `--worktree` are specified,
-`--source` must also be specified.
+If not specified, the contents are restored from `HEAD` if `--staged` is
+given, otherwise from the index.
-p::
--patch::
@@ -107,6 +104,17 @@
patterns and unconditionally restores any files in
`<pathspec>`.
+--recurse-submodules::
+--no-recurse-submodules::
+ If `<pathspec>` names an active submodule and the restore location
+ includes the working tree, the submodule will only be updated if
+ this option is given, in which case its working tree will be
+ restored to the commit recorded in the superproject, and any local
+ modifications overwritten. If nothing (or
+ `--no-recurse-submodules`) is used, submodules working trees will
+ not be updated. Just like linkgit:git-checkout[1], this will detach
+ `HEAD` of the submodule.
+
--overlay::
--no-overlay::
In overlay mode, the command never removes files when
diff --git a/Documentation/git-revert.txt b/Documentation/git-revert.txt
index 9d22270..044276e 100644
--- a/Documentation/git-revert.txt
+++ b/Documentation/git-revert.txt
@@ -90,9 +90,12 @@
-S[<keyid>]::
--gpg-sign[=<keyid>]::
+--no-gpg-sign::
GPG-sign commits. The `keyid` argument is optional and
defaults to the committer identity; if specified, it must be
- stuck to the option without a space.
+ stuck to the option without a space. `--no-gpg-sign` is useful to
+ countermand both `commit.gpgSign` configuration variable, and
+ earlier `--gpg-sign`.
-s::
--signoff::
diff --git a/Documentation/git-sparse-checkout.txt b/Documentation/git-sparse-checkout.txt
index c0342e5..7c8943a 100644
--- a/Documentation/git-sparse-checkout.txt
+++ b/Documentation/git-sparse-checkout.txt
@@ -70,6 +70,16 @@
`core.sparseCheckoutCone` is enabled, the given patterns are interpreted
as directory names as in the 'set' subcommand.
+'reapply'::
+ Reapply the sparsity pattern rules to paths in the working tree.
+ Commands like merge or rebase can materialize paths to do their
+ work (e.g. in order to show you a conflict), and other
+ sparse-checkout commands might fail to sparsify an individual file
+ (e.g. because it has unstaged changes or conflicts). In such
+ 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.).
+
'disable'::
Disable the `core.sparseCheckout` config setting, and restore the
working directory to include all files. Leaves the sparse-checkout
diff --git a/Documentation/git-switch.txt b/Documentation/git-switch.txt
index 1979003..3759c3a 100644
--- a/Documentation/git-switch.txt
+++ b/Documentation/git-switch.txt
@@ -181,9 +181,9 @@
--recurse-submodules::
--no-recurse-submodules::
Using `--recurse-submodules` will update the content of all
- initialized submodules according to the commit recorded in the
+ active submodules according to the commit recorded in the
superproject. If nothing (or `--no-recurse-submodules`) is
- used, the work trees of submodules will not be updated. Just
+ used, submodules working trees will not be updated. Just
like linkgit:git-submodule[1], this will detach `HEAD` of the
submodules.
diff --git a/Documentation/git-update-ref.txt b/Documentation/git-update-ref.txt
index 9671423..3e737c2 100644
--- a/Documentation/git-update-ref.txt
+++ b/Documentation/git-update-ref.txt
@@ -66,6 +66,10 @@
delete SP <ref> [SP <oldvalue>] LF
verify SP <ref> [SP <oldvalue>] LF
option SP <opt> LF
+ start LF
+ prepare LF
+ commit LF
+ abort LF
With `--create-reflog`, update-ref will create a reflog for each ref
even if one would not ordinarily be created.
@@ -83,6 +87,10 @@
delete SP <ref> NUL [<oldvalue>] NUL
verify SP <ref> NUL [<oldvalue>] NUL
option SP <opt> NUL
+ start NUL
+ prepare NUL
+ commit NUL
+ abort NUL
In this format, use 40 "0" to specify a zero value, and use the empty
string to specify a missing value.
@@ -107,13 +115,31 @@
verify::
Verify <ref> against <oldvalue> but do not change it. If
- <oldvalue> zero or missing, the ref must not exist.
+ <oldvalue> is zero or missing, the ref must not exist.
option::
Modify behavior of the next command naming a <ref>.
The only valid option is `no-deref` to avoid dereferencing
a symbolic ref.
+start::
+ Start a transaction. In contrast to a non-transactional session, a
+ transaction will automatically abort if the session ends without an
+ explicit commit.
+
+prepare::
+ Prepare to commit the transaction. This will create lock files for all
+ queued reference updates. If one reference could not be locked, the
+ transaction will be aborted.
+
+commit::
+ Commit all reference updates queued for the transaction, ending the
+ transaction.
+
+abort::
+ Abort the transaction, releasing all locks if the transaction is in
+ prepared state.
+
If all <ref>s can be locked with matching <oldvalue>s
simultaneously, all modifications are performed. Otherwise, no
modifications are performed. Note that while each individual
diff --git a/Documentation/git.txt b/Documentation/git.txt
index b0672bd..1289084 100644
--- a/Documentation/git.txt
+++ b/Documentation/git.txt
@@ -493,6 +493,12 @@
details. This variable has lower precedence than other path
variables such as GIT_INDEX_FILE, GIT_OBJECT_DIRECTORY...
+`GIT_DEFAULT_HASH`::
+ If this variable is set, the default hash algorithm for new
+ repositories will be set to this value. This value is currently
+ ignored when cloning; the setting of the remote repository
+ is used instead. The default is "sha1".
+
Git Commits
~~~~~~~~~~~
`GIT_AUTHOR_NAME`::
diff --git a/Documentation/gitattributes.txt b/Documentation/gitattributes.txt
index 508fe71..2d0a037 100644
--- a/Documentation/gitattributes.txt
+++ b/Documentation/gitattributes.txt
@@ -824,6 +824,8 @@
- `java` suitable for source code in the Java language.
+- `markdown` suitable for Markdown documents.
+
- `matlab` suitable for source code in the MATLAB and Octave languages.
- `objc` suitable for source code in the Objective-C language.
diff --git a/Documentation/gitcredentials.txt b/Documentation/gitcredentials.txt
index 1814d2d..9e481ae 100644
--- a/Documentation/gitcredentials.txt
+++ b/Documentation/gitcredentials.txt
@@ -216,20 +216,26 @@
----------------------------------------------------
# run "git credential-foo"
-foo
+[credential]
+ helper = foo
# same as above, but pass an argument to the helper
-foo --bar=baz
+[credential]
+ helper = "foo --bar=baz"
# the arguments are parsed by the shell, so use shell
# quoting if necessary
-foo --bar="whitespace arg"
+[credential]
+ helper = "foo --bar='whitespace arg'"
# you can also use an absolute path, which will not use the git wrapper
-/path/to/my/helper --with-arguments
+[credential]
+ helper = "/path/to/my/helper --with-arguments"
# or you can specify your own shell snippet
-!f() { echo "password=`cat $HOME/.secret`"; }; f
+[credential "https://example.com"]
+ username = your_user
+ helper = "!f() { test \"$1\" = get && echo \"password=$(cat $HOME/.secret)\"; }; f"
----------------------------------------------------
Generally speaking, rule (3) above is the simplest for users to specify.
@@ -262,16 +268,26 @@
stdout in the same format (see linkgit:git-credential[1] for common
attributes). A helper is free to produce a subset, or even no values at
all if it has nothing useful to provide. Any provided attributes will
-overwrite those already known about by Git. If a helper outputs a
-`quit` attribute with a value of `true` or `1`, no further helpers will
-be consulted, nor will the user be prompted (if no credential has been
-provided, the operation will then fail).
+overwrite those already known about by Git's credential subsystem.
+
+While it is possible to override all attributes, well behaving helpers
+should refrain from doing so for any attribute other than username and
+password.
+
+If a helper outputs a `quit` attribute with a value of `true` or `1`,
+no further helpers will be consulted, nor will the user be prompted
+(if no credential has been provided, the operation will then fail).
+
+Similarly, no more helpers will be consulted once both username and
+password had been provided.
For a `store` or `erase` operation, the helper's output is ignored.
-If it fails to perform the requested operation, it may complain to
-stderr to inform the user. If it does not support the requested
-operation (e.g., a read-only store), it should silently ignore the
-request.
+
+If a helper fails to perform the requested operation or needs to notify
+the user of a potential issue, it may write to stderr.
+
+If it does not support the requested operation (e.g., a read-only store),
+it should silently ignore the request.
If a helper receives any other operation, it should silently ignore the
request. This leaves room for future operations to be added (older
diff --git a/Documentation/gitfaq.txt b/Documentation/gitfaq.txt
new file mode 100644
index 0000000..9cd7a59
--- /dev/null
+++ b/Documentation/gitfaq.txt
@@ -0,0 +1,355 @@
+gitfaq(7)
+=========
+
+NAME
+----
+gitfaq - Frequently asked questions about using Git
+
+SYNOPSIS
+--------
+gitfaq
+
+DESCRIPTION
+-----------
+
+The examples in this FAQ assume a standard POSIX shell, like `bash` or `dash`,
+and a user, A U Thor, who has the account `author` on the hosting provider
+`git.example.org`.
+
+Configuration
+-------------
+
+[[user-name]]
+What should I put in `user.name`?::
+ You should put your personal name, generally a form using a given name
+ and family name. For example, the current maintainer of Git uses "Junio
+ C Hamano". This will be the name portion that is stored in every commit
+ you make.
++
+This configuration doesn't have any effect on authenticating to remote services;
+for that, see `credential.username` in linkgit:git-config[1].
+
+[[http-postbuffer]]
+What does `http.postBuffer` really do?::
+ This option changes the size of the buffer that Git uses when pushing
+ data to a remote over HTTP or HTTPS. If the data is larger than this
+ size, libcurl, which handles the HTTP support for Git, will use chunked
+ transfer encoding since it isn't known ahead of time what the size of
+ the pushed data will be.
++
+Leaving this value at the default size is fine unless you know that either the
+remote server or a proxy in the middle doesn't support HTTP/1.1 (which
+introduced the chunked transfer encoding) or is known to be broken with chunked
+data. This is often (erroneously) suggested as a solution for generic push
+problems, but since almost every server and proxy supports at least HTTP/1.1,
+raising this value usually doesn't solve most push problems. A server or proxy
+that didn't correctly support HTTP/1.1 and chunked transfer encoding wouldn't be
+that useful on the Internet today, since it would break lots of traffic.
++
+Note that increasing this value will increase the memory used on every relevant
+push that Git does over HTTP or HTTPS, since the entire buffer is allocated
+regardless of whether or not it is all used. Thus, it's best to leave it at the
+default unless you are sure you need a different value.
+
+[[configure-editor]]
+How do I configure a different editor?::
+ If you haven't specified an editor specifically for Git, it will by default
+ use the editor you've configured using the `VISUAL` or `EDITOR` environment
+ variables, or if neither is specified, the system default (which is usually
+ `vi`). Since some people find `vi` difficult to use or prefer a different
+ editor, it may be desirable to change the editor used.
++
+If you want to configure a general editor for most programs which need one, you
+can edit your shell configuration (e.g., `~/.bashrc` or `~/.zshenv`) to contain
+a line setting the `EDITOR` or `VISUAL` environment variable to an appropriate
+value. For example, if you prefer the editor `nano`, then you could write the
+following:
++
+----
+export VISUAL=nano
+----
++
+If you want to configure an editor specifically for Git, you can either set the
+`core.editor` configuration value or the `GIT_EDITOR` environment variable. You
+can see linkgit:git-var[1] for details on the order in which these options are
+consulted.
++
+Note that in all cases, the editor value will be passed to the shell, so any
+arguments containing spaces should be appropriately quoted. Additionally, if
+your editor normally detaches from the terminal when invoked, you should specify
+it with an argument that makes it not do that, or else Git will not see any
+changes. An example of a configuration addressing both of these issues on
+Windows would be the configuration `"C:\Program Files\Vim\gvim.exe" --nofork`,
+which quotes the filename with spaces and specifies the `--nofork` option to
+avoid backgrounding the process.
+
+Credentials
+-----------
+
+[[http-credentials]]
+How do I specify my credentials when pushing over HTTP?::
+ The easiest way to do this is to use a credential helper via the
+ `credential.helper` configuration. Most systems provide a standard
+ choice to integrate with the system credential manager. For example,
+ Git for Windows provides the `wincred` credential manager, macOS has the
+ `osxkeychain` credential manager, and Unix systems with a standard
+ desktop environment can use the `libsecret` credential manager. All of
+ these store credentials in an encrypted store to keep your passwords or
+ tokens secure.
++
+In addition, you can use the `store` credential manager which stores in a file
+in your home directory, or the `cache` credential manager, which does not
+permanently store your credentials, but does prevent you from being prompted for
+them for a certain period of time.
++
+You can also just enter your password when prompted. While it is possible to
+place the password (which must be percent-encoded) in the URL, this is not
+particularly secure and can lead to accidental exposure of credentials, so it is
+not recommended.
+
+[[http-credentials-environment]]
+How do I read a password or token from an environment variable?::
+ The `credential.helper` configuration option can also take an arbitrary
+ shell command that produces the credential protocol on standard output.
+ This is useful when passing credentials into a container, for example.
++
+Such a shell command can be specified by starting the option value with an
+exclamation point. If your password or token were stored in the `GIT_TOKEN`,
+you could run the following command to set your credential helper:
++
+----
+$ git config credential.helper \
+ '!f() { echo username=author; echo "password=$GIT_TOKEN"; };f'
+----
+
+[[http-reset-credentials]]
+How do I change the password or token I've saved in my credential manager?::
+ Usually, if the password or token is invalid, Git will erase it and
+ prompt for a new one. However, there are times when this doesn't always
+ happen. To change the password or token, you can erase the existing
+ credentials and then Git will prompt for new ones. To erase
+ credentials, use a syntax like the following (substituting your username
+ and the hostname):
++
+----
+$ echo url=https://author@git.example.org | git credential reject
+----
+
+[[multiple-accounts-http]]
+How do I use multiple accounts with the same hosting provider using HTTP?::
+ Usually the easiest way to distinguish between these accounts is to use
+ the username in the URL. For example, if you have the accounts `author`
+ and `committer` on `git.example.org`, you can use the URLs
+ https://author@git.example.org/org1/project1.git and
+ https://committer@git.example.org/org2/project2.git. This way, when you
+ use a credential helper, it will automatically try to look up the
+ correct credentials for your account. If you already have a remote set
+ up, you can change the URL with something like `git remote set-url
+ origin https://author@git.example.org/org1/project1.git` (see
+ linkgit:git-remote[1] for details).
+
+[[multiple-accounts-ssh]]
+How do I use multiple accounts with the same hosting provider using SSH?::
+ With most hosting providers that support SSH, a single key pair uniquely
+ identifies a user. Therefore, to use multiple accounts, it's necessary
+ to create a key pair for each account. If you're using a reasonably
+ modern OpenSSH version, you can create a new key pair with something
+ like `ssh-keygen -t ed25519 -f ~/.ssh/id_committer`. You can then
+ register the public key (in this case, `~/.ssh/id_committer.pub`; note
+ the `.pub`) with the hosting provider.
++
+Most hosting providers use a single SSH account for pushing; that is, all users
+push to the `git` account (e.g., `git@git.example.org`). If that's the case for
+your provider, you can set up multiple aliases in SSH to make it clear which key
+pair to use. For example, you could write something like the following in
+`~/.ssh/config`, substituting the proper private key file:
++
+----
+# This is the account for author on git.example.org.
+Host example_author
+ HostName git.example.org
+ User git
+ # This is the key pair registered for author with git.example.org.
+ IdentityFile ~/.ssh/id_author
+ IdentitiesOnly yes
+# This is the account for committer on git.example.org.
+Host example_committer
+ HostName git.example.org
+ User git
+ # This is the key pair registered for committer with git.example.org.
+ IdentityFile ~/.ssh/id_committer
+ IdentitiesOnly yes
+----
++
+Then, you can adjust your push URL to use `git@example_author` or
+`git@example_committer` instead of `git@example.org` (e.g., `git remote set-url
+git@example_author:org1/project1.git`).
+
+Common Issues
+-------------
+
+[[last-commit-amend]]
+I've made a mistake in the last commit. How do I change it?::
+ You can make the appropriate change to your working tree, run `git add
+ <file>` or `git rm <file>`, as appropriate, to stage it, and then `git
+ commit --amend`. Your change will be included in the commit, and you'll
+ be prompted to edit the commit message again; if you wish to use the
+ original message verbatim, you can use the `--no-edit` option to `git
+ commit` in addition, or just save and quit when your editor opens.
+
+[[undo-previous-change]]
+I've made a change with a bug and it's been included in the main branch. How should I undo it?::
+ The usual way to deal with this is to use `git revert`. This preserves
+ the history that the original change was made and was a valuable
+ contribution, but also introduces a new commit that undoes those changes
+ because the original had a problem. The commit message of the revert
+ indicates the commit which was reverted and is usually edited to include
+ an explanation as to why the revert was made.
+
+[[ignore-tracked-files]]
+How do I ignore changes to a tracked file?::
+ Git doesn't provide a way to do this. The reason is that if Git needs
+ to overwrite this file, such as during a checkout, it doesn't know
+ whether the changes to the file are precious and should be kept, or
+ whether they are irrelevant and can safely be destroyed. Therefore, it
+ has to take the safe route and always preserve them.
++
+It's tempting to try to use certain features of `git update-index`, namely the
+assume-unchanged and skip-worktree bits, but these don't work properly for this
+purpose and shouldn't be used this way.
++
+If your goal is to modify a configuration file, it can often be helpful to have
+a file checked into the repository which is a template or set of defaults which
+can then be copied alongside and modified as appropriate. This second, modified
+file is usually ignored to prevent accidentally committing it.
+
+[[files-in-gitignore-are-tracked]]
+I asked Git to ignore various files, yet they are still tracked::
+ A `gitignore` file ensures that certain file(s) which are not
+ tracked by Git remain untracked. However, sometimes particular
+ file(s) may have been tracked before adding them into the
+ `.gitignore`, hence they still remain tracked. To untrack and
+ ignore files/patterns, use `git rm --cached <file/pattern>`
+ and add a pattern to `.gitignore` that matches the <file>.
+ See linkgit:gitignore[5] for details.
+
+[[fetching-and-pulling]]
+How do I know if I want to do a fetch or a pull?::
+ A fetch stores a copy of the latest changes from the remote
+ repository, without modifying the working tree or current branch.
+ You can then at your leisure inspect, merge, rebase on top of, or
+ ignore the upstream changes. A pull consists of a fetch followed
+ immediately by either a merge or rebase. See linkgit:git-pull[1].
+
+Hooks
+-----
+
+[[restrict-with-hooks]]
+How do I use hooks to prevent users from making certain changes?::
+ The only safe place to make these changes is on the remote repository
+ (i.e., the Git server), usually in the `pre-receive` hook or in a
+ continuous integration (CI) system. These are the locations in which
+ policy can be enforced effectively.
++
+It's common to try to use `pre-commit` hooks (or, for commit messages,
+`commit-msg` hooks) to check these things, which is great if you're working as a
+solo developer and want the tooling to help you. However, using hooks on a
+developer machine is not effective as a policy control because a user can bypass
+these hooks with `--no-verify` without being noticed (among various other ways).
+Git assumes that the user is in control of their local repositories and doesn't
+try to prevent this or tattle on the user.
++
+In addition, some advanced users find `pre-commit` hooks to be an impediment to
+workflows that use temporary commits to stage work in progress or that create
+fixup commits, so it's better to push these kinds of checks to the server
+anyway.
+
+Cross-Platform Issues
+---------------------
+
+[[windows-text-binary]]
+I'm on Windows and my text files are detected as binary.::
+ Git works best when you store text files as UTF-8. Many programs on
+ Windows support UTF-8, but some do not and only use the little-endian
+ UTF-16 format, which Git detects as binary. If you can't use UTF-8 with
+ your programs, you can specify a working tree encoding that indicates
+ which encoding your files should be checked out with, while still
+ storing these files as UTF-8 in the repository. This allows tools like
+ linkgit:git-diff[1] to work as expected, while still allowing your tools
+ to work.
++
+To do so, you can specify a linkgit:gitattributes[5] pattern with the
+`working-tree-encoding` attribute. For example, the following pattern sets all
+C files to use UTF-16LE-BOM, which is a common encoding on Windows:
++
+----
+*.c working-tree-encoding=UTF-16LE-BOM
+----
++
+You will need to run `git add --renormalize` to have this take effect. Note
+that if you are making these changes on a project that is used across platforms,
+you'll probably want to make it in a per-user configuration file or in the one
+in `$GIT_DIR/info/attributes`, since making it in a `.gitattributes` file in the
+repository will apply to all users of the repository.
++
+See the following entry for information about normalizing line endings as well,
+and see linkgit:gitattributes[5] for more information about attribute files.
+
+[[windows-diff-control-m]]
+I'm on Windows and git diff shows my files as having a `^M` at the end.::
+ By default, Git expects files to be stored with Unix line endings. As such,
+ the carriage return (`^M`) that is part of a Windows line ending is shown
+ because it is considered to be trailing whitespace. Git defaults to showing
+ trailing whitespace only on new lines, not existing ones.
++
+You can store the files in the repository with Unix line endings and convert
+them automatically to your platform's line endings. To do that, set the
+configuration option `core.eol` to `native` and see the following entry for
+information about how to configure files as text or binary.
++
+You can also control this behavior with the `core.whitespace` setting if you
+don't wish to remove the carriage returns from your line endings.
+
+[[recommended-storage-settings]]
+What's the recommended way to store files in Git?::
+ While Git can store and handle any file of any type, there are some
+ settings that work better than others. In general, we recommend that
+ text files be stored in UTF-8 without a byte-order mark (BOM) with LF
+ (Unix-style) endings. We also recommend the use of UTF-8 (again,
+ without BOM) in commit messages. These are the settings that work best
+ across platforms and with tools such as `git diff` and `git merge`.
++
+Additionally, if you have a choice between storage formats that are text based
+or non-text based, we recommend storing files in the text format and, if
+necessary, transforming them into the other format. For example, a text-based
+SQL dump with one record per line will work much better for diffing and merging
+than an actual database file. Similarly, text-based formats such as Markdown
+and AsciiDoc will work better than binary formats such as Microsoft Word and
+PDF.
++
+Similarly, storing binary dependencies (e.g., shared libraries or JAR files) or
+build products in the repository is generally not recommended. Dependencies and
+build products are best stored on an artifact or package server with only
+references, URLs, and hashes stored in the repository.
++
+We also recommend setting a linkgit:gitattributes[5] file to explicitly mark
+which files are text and which are binary. If you want Git to guess, you can
+set the attribute `text=auto`. For example, the following might be appropriate
+in some projects:
++
+----
+# By default, guess.
+* text=auto
+# Mark all C files as text.
+*.c text
+# Mark all JPEG files as binary.
+*.jpg binary
+----
++
+These settings help tools pick the right format for output such as patches and
+result in files being checked out in the appropriate line ending for the
+platform.
+
+GIT
+---
+Part of the linkgit:git[1] suite
diff --git a/Documentation/githooks.txt b/Documentation/githooks.txt
index 3dccab5..81f2a87 100644
--- a/Documentation/githooks.txt
+++ b/Documentation/githooks.txt
@@ -522,12 +522,61 @@
hook to limit its search. On error, it will fall back to verifying
all files and folders.
+p4-changelist
+~~~~~~~~~~~~~
+
+This hook is invoked by `git-p4 submit`.
+
+The `p4-changelist` hook is executed after the changelist
+message has been edited by the user. It can be bypassed with the
+`--no-verify` option. It takes a single parameter, the name
+of the file that holds the proposed changelist text. Exiting
+with a non-zero status causes the command to abort.
+
+The hook is allowed to edit the changelist file and can be used
+to normalize the text into some project standard format. It can
+also be used to refuse the Submit after inspect the message file.
+
+Run `git-p4 submit --help` for details.
+
+p4-prepare-changelist
+~~~~~~~~~~~~~~~~~~~~~
+
+This hook is invoked by `git-p4 submit`.
+
+The `p4-prepare-changelist` hook is executed right after preparing
+the default changelist message and before the editor is started.
+It takes one parameter, the name of the file that contains the
+changelist text. Exiting with a non-zero status from the script
+will abort the process.
+
+The purpose of the hook is to edit the message file in place,
+and it is not supressed by the `--no-verify` option. This hook
+is called even if `--prepare-p4-only` is set.
+
+Run `git-p4 submit --help` for details.
+
+p4-post-changelist
+~~~~~~~~~~~~~~~~~~
+
+This hook is invoked by `git-p4 submit`.
+
+The `p4-post-changelist` hook is invoked after the submit has
+successfully occured in P4. It takes no parameters and is meant
+primarily for notification and cannot affect the outcome of the
+git p4 submit action.
+
+Run `git-p4 submit --help` for details.
+
p4-pre-submit
~~~~~~~~~~~~~
This hook is invoked by `git-p4 submit`. It takes no parameters and nothing
from standard input. Exiting with non-zero status from this script prevent
-`git-p4 submit` from launching. Run `git-p4 submit --help` for details.
+`git-p4 submit` from launching. It can be bypassed with the `--no-verify`
+command line option. Run `git-p4 submit --help` for details.
+
+
post-index-change
~~~~~~~~~~~~~~~~~
diff --git a/Documentation/gitsubmodules.txt b/Documentation/gitsubmodules.txt
index c476f89..f9f4e65 100644
--- a/Documentation/gitsubmodules.txt
+++ b/Documentation/gitsubmodules.txt
@@ -271,7 +271,8 @@
into submodules. The 'init' and 'update' subcommands of 'git submodule'
will maintain submodules checked out and at an appropriate revision in
your working tree. Alternatively you can set 'submodule.recurse' to have
-'checkout' recursing into submodules.
+'checkout' recursing into submodules (note that 'submodule.recurse' also
+affects other git commands, see linkgit:git-config[1] for a complete list).
SEE ALSO
diff --git a/Documentation/howto/maintain-git.txt b/Documentation/howto/maintain-git.txt
index ca43787..73be8b4 100644
--- a/Documentation/howto/maintain-git.txt
+++ b/Documentation/howto/maintain-git.txt
@@ -154,15 +154,17 @@
- Anything unobvious that is applicable to 'master' (in other
words, does not depend on anything that is still in 'next'
and not in 'master') is applied to a new topic branch that
- is forked from the tip of 'master'. This includes both
+ is forked from the tip of 'master' (or the last feature release,
+ which is a bit older than 'master'). This includes both
enhancements and unobvious fixes to 'master'. A topic
branch is named as ai/topic where "ai" is two-letter string
named after author's initial and "topic" is a descriptive name
of the topic (in other words, "what's the series is about").
- An unobvious fix meant for 'maint' is applied to a new
- topic branch that is forked from the tip of 'maint'. The
- topic is named as ai/maint-topic.
+ topic branch that is forked from the tip of 'maint' (or the
+ oldest and still relevant maintenance branch). The
+ topic may be named as ai/maint-topic.
- Changes that pertain to an existing topic are applied to
the branch, but:
@@ -174,24 +176,40 @@
- Replacement patches to an existing topic are accepted only
for commits not in 'next'.
- The above except the "replacement" are all done with:
+ The initial round is done with:
$ git checkout ai/topic ;# or "git checkout -b ai/topic master"
$ git am -sc3 mailbox
- while patch replacement is often done by:
+ and replacing an existing topic with subsequent round is done with:
- $ git format-patch ai/topic~$n..ai/topic ;# export existing
+ $ git checkout master...ai/topic ;# try to reapply to the same base
+ $ git am -sc3 mailbox
- then replace some parts with the new patch, and reapplying:
+ to prepare the new round on a detached HEAD, and then
- $ git checkout ai/topic
- $ git reset --hard ai/topic~$n
- $ git am -sc3 -s 000*.txt
+ $ git range-diff @{-1}...
+ $ git diff @{-1}
- The full test suite is always run for 'maint' and 'master'
- after patch application; for topic branches the tests are run
- as time permits.
+ to double check what changed since the last round, and finally
+
+ $ git checkout -B @{-1}
+
+ to conclude (the last step is why a topic already in 'next' is
+ not replaced but updated incrementally).
+
+ Whether it is the initial round or a subsequent round, the topic
+ may not build even in isolation, or may break the build when
+ merged to integration branches due to bugs. There may already
+ be obvious and trivial improvements suggested on the list. The
+ maintainer often adds an extra commit, with "SQUASH???" in its
+ title, to fix things up, before publishing the integration
+ branches to make it usable by other developers for testing.
+ These changes are what the maintainer is not 100% committed to
+ (trivial typofixes etc. are often squashed directly into the
+ patches that need fixing, without being applied as a separate
+ "SQUASH???" commit), so that they can be removed easily as needed.
+
- Merge maint to master as needed:
@@ -371,6 +389,14 @@
be included in the next feature release. Being in the
'master' branch typically is.
+ * Due to the nature of "SQUASH???" fix-ups, if the original author
+ agrees with the suggested changes, it is OK to squash them to
+ appropriate patches in the next round (when the suggested change
+ is small enough, the author should not even bother with
+ "Helped-by"). It is also OK to drop them from the next round
+ when the original author does not agree with the suggestion, but
+ the author is expected to say why somewhere in the discussion.
+
Appendix
--------
diff --git a/Documentation/manpage-1.72.xsl b/Documentation/manpage-1.72.xsl
deleted file mode 100644
index b4d315c..0000000
--- a/Documentation/manpage-1.72.xsl
+++ /dev/null
@@ -1,14 +0,0 @@
-<!-- manpage-1.72.xsl:
- special settings for manpages rendered from asciidoc+docbook
- handles peculiarities in docbook-xsl 1.72.0 -->
-<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
- version="1.0">
-
-<xsl:import href="manpage-base.xsl"/>
-
-<!-- these are the special values for the roff control characters
- needed for docbook-xsl 1.72.0 -->
-<xsl:param name="git.docbook.backslash">▓</xsl:param>
-<xsl:param name="git.docbook.dot" >⌂</xsl:param>
-
-</xsl:stylesheet>
diff --git a/Documentation/manpage-base.xsl b/Documentation/manpage-base.xsl
deleted file mode 100644
index a264fa6..0000000
--- a/Documentation/manpage-base.xsl
+++ /dev/null
@@ -1,35 +0,0 @@
-<!-- manpage-base.xsl:
- special formatting for manpages rendered from asciidoc+docbook -->
-<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
- version="1.0">
-
-<!-- these params silence some output from xmlto -->
-<xsl:param name="man.output.quietly" select="1"/>
-<xsl:param name="refentry.meta.get.quietly" select="1"/>
-
-<!-- convert asciidoc callouts to man page format;
- git.docbook.backslash and git.docbook.dot params
- must be supplied by another XSL file or other means -->
-<xsl:template match="co">
- <xsl:value-of select="concat(
- $git.docbook.backslash,'fB(',
- substring-after(@id,'-'),')',
- $git.docbook.backslash,'fR')"/>
-</xsl:template>
-<xsl:template match="calloutlist">
- <xsl:value-of select="$git.docbook.dot"/>
- <xsl:text>sp </xsl:text>
- <xsl:apply-templates/>
- <xsl:text> </xsl:text>
-</xsl:template>
-<xsl:template match="callout">
- <xsl:value-of select="concat(
- $git.docbook.backslash,'fB',
- substring-after(@arearefs,'-'),
- '. ',$git.docbook.backslash,'fR')"/>
- <xsl:apply-templates/>
- <xsl:value-of select="$git.docbook.dot"/>
- <xsl:text>br </xsl:text>
-</xsl:template>
-
-</xsl:stylesheet>
diff --git a/Documentation/manpage-bold-literal.xsl b/Documentation/manpage-bold-literal.xsl
index 94d6c1b..e13db85 100644
--- a/Documentation/manpage-bold-literal.xsl
+++ b/Documentation/manpage-bold-literal.xsl
@@ -8,11 +8,9 @@
this makes literal text easier to distinguish in manpages
viewed on a tty -->
<xsl:template match="literal|d:literal">
- <xsl:value-of select="$git.docbook.backslash"/>
- <xsl:text>fB</xsl:text>
+ <xsl:text>\fB</xsl:text>
<xsl:apply-templates/>
- <xsl:value-of select="$git.docbook.backslash"/>
- <xsl:text>fR</xsl:text>
+ <xsl:text>\fR</xsl:text>
</xsl:template>
</xsl:stylesheet>
diff --git a/Documentation/manpage-normal.xsl b/Documentation/manpage-normal.xsl
index a48f5b1..a9c7ec6 100644
--- a/Documentation/manpage-normal.xsl
+++ b/Documentation/manpage-normal.xsl
@@ -1,13 +1,26 @@
<!-- manpage-normal.xsl:
- special settings for manpages rendered from asciidoc+docbook
- handles anything we want to keep away from docbook-xsl 1.72.0 -->
+ special settings for manpages rendered from asciidoc+docbook -->
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
version="1.0">
-<xsl:import href="manpage-base.xsl"/>
-<!-- these are the normal values for the roff control characters -->
-<xsl:param name="git.docbook.backslash">\</xsl:param>
-<xsl:param name="git.docbook.dot" >.</xsl:param>
+<!-- these params silence some output from xmlto -->
+<xsl:param name="man.output.quietly" select="1"/>
+<xsl:param name="refentry.meta.get.quietly" select="1"/>
+
+<!-- convert asciidoc callouts to man page format -->
+<xsl:template match="co">
+ <xsl:value-of select="concat('\fB(',substring-after(@id,'-'),')\fR')"/>
+</xsl:template>
+<xsl:template match="calloutlist">
+ <xsl:text>.sp </xsl:text>
+ <xsl:apply-templates/>
+ <xsl:text> </xsl:text>
+</xsl:template>
+<xsl:template match="callout">
+ <xsl:value-of select="concat('\fB',substring-after(@arearefs,'-'),'. \fR')"/>
+ <xsl:apply-templates/>
+ <xsl:text>.br </xsl:text>
+</xsl:template>
</xsl:stylesheet>
diff --git a/Documentation/manpage-suppress-sp.xsl b/Documentation/manpage-suppress-sp.xsl
deleted file mode 100644
index a63c763..0000000
--- a/Documentation/manpage-suppress-sp.xsl
+++ /dev/null
@@ -1,21 +0,0 @@
-<!-- manpage-suppress-sp.xsl:
- special settings for manpages rendered from asciidoc+docbook
- handles erroneous, inline .sp in manpage output of some
- versions of docbook-xsl -->
-<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
- version="1.0">
-
-<!-- attempt to work around spurious .sp at the tail of the line
- that some versions of docbook stylesheets seem to add -->
-<xsl:template match="simpara">
- <xsl:variable name="content">
- <xsl:apply-templates/>
- </xsl:variable>
- <xsl:value-of select="normalize-space($content)"/>
- <xsl:if test="not(ancestor::authorblurb) and
- not(ancestor::personblurb)">
- <xsl:text> </xsl:text>
- </xsl:if>
-</xsl:template>
-
-</xsl:stylesheet>
diff --git a/Documentation/merge-options.txt b/Documentation/merge-options.txt
index 40dc4f5..80d4831 100644
--- a/Documentation/merge-options.txt
+++ b/Documentation/merge-options.txt
@@ -61,9 +61,12 @@
-S[<keyid>]::
--gpg-sign[=<keyid>]::
+--no-gpg-sign::
GPG-sign the resulting merge commit. The `keyid` argument is
optional and defaults to the committer identity; if specified,
- it must be stuck to the option without a space.
+ it must be stuck to the option without a space. `--no-gpg-sign`
+ is useful to countermand both `commit.gpgSign` configuration variable,
+ and earlier `--gpg-sign`.
--log[=<n>]::
--no-log::
@@ -157,6 +160,14 @@
endif::git-pull[]
+--autostash::
+--no-autostash::
+ Automatically create a temporary stash entry before the operation
+ begins, and apply it after the operation ends. This means
+ that you can run the operation on a dirty worktree. However, use
+ with care: the final stash application after a successful
+ merge might result in non-trivial conflicts.
+
--allow-unrelated-histories::
By default, `git merge` command refuses to merge histories
that do not share a common ancestor. This option can be
diff --git a/Documentation/pretty-formats.txt b/Documentation/pretty-formats.txt
index a4b6f49..547a552 100644
--- a/Documentation/pretty-formats.txt
+++ b/Documentation/pretty-formats.txt
@@ -83,6 +83,12 @@
<full commit message>
+* 'mboxrd'
++
+Like 'email', but lines in the commit message starting with "From "
+(preceded by zero or more ">") are quoted with ">" so they aren't
+confused as starting a new commit.
+
* 'raw'
+
The 'raw' format shows the entire commit exactly as
diff --git a/Documentation/pull-fetch-param.txt b/Documentation/pull-fetch-param.txt
index 7d3a60f..95ea849 100644
--- a/Documentation/pull-fetch-param.txt
+++ b/Documentation/pull-fetch-param.txt
@@ -19,7 +19,8 @@
(see <<CRTB,CONFIGURED REMOTE-TRACKING BRANCHES>> below).
endif::git-pull[]
ifdef::git-pull[]
- (see linkgit:git-fetch[1]).
+ (see the section "CONFIGURED REMOTE-TRACKING BRANCHES"
+ in linkgit:git-fetch[1]).
endif::git-pull[]
+
The format of a <refspec> parameter is an optional plus
diff --git a/Documentation/rev-list-options.txt b/Documentation/rev-list-options.txt
index bfd02ad..b01b2b6 100644
--- a/Documentation/rev-list-options.txt
+++ b/Documentation/rev-list-options.txt
@@ -342,6 +342,12 @@
branches if the end result is the same (i.e. merging branches
with the same content)
+--show-pulls::
+ Include all commits from the default mode, but also any merge
+ commits that are not TREESAME to the first parent but are
+ TREESAME to a later parent. This mode is helpful for showing
+ the merge commits that "first introduced" a change to a branch.
+
--full-history::
Same as the default mode, but does not prune some history.
@@ -534,7 +540,7 @@
parent and is TREESAME.
--
-Finally, there is a fifth simplification mode available:
+There is another simplification mode available:
--ancestry-path::
Limit the displayed commits to those directly on the ancestry
@@ -573,6 +579,135 @@
L--M
-----------------------------------------------------------------------
+Before discussing another option, `--show-pulls`, we need to
+create a new example history.
+
+A common problem users face when looking at simplified history is that a
+commit they know changed a file somehow does not appear in the file's
+simplified history. Let's demonstrate a new example and show how options
+such as `--full-history` and `--simplify-merges` works in that case:
+
+-----------------------------------------------------------------------
+ .-A---M-----C--N---O---P
+ / / \ \ \/ / /
+ I B \ R-'`-Z' /
+ \ / \/ /
+ \ / /\ /
+ `---X--' `---Y--'
+-----------------------------------------------------------------------
+
+For this example, suppose `I` created `file.txt` which was modified by
+`A`, `B`, and `X` in different ways. The single-parent commits `C`, `Z`,
+and `Y` do not change `file.txt`. The merge commit `M` was created by
+resolving the merge conflict to include both changes from `A` and `B`
+and hence is not TREESAME to either. The merge commit `R`, however, was
+created by ignoring the contents of `file.txt` at `M` and taking only
+the contents of `file.txt` at `X`. Hence, `R` is TREESAME to `X` but not
+`M`. Finally, the natural merge resolution to create `N` is to take the
+contents of `file.txt` at `R`, so `N` is TREESAME to `R` but not `C`.
+The merge commits `O` and `P` are TREESAME to their first parents, but
+not to their second parents, `Z` and `Y` respectively.
+
+When using the default mode, `N` and `R` both have a TREESAME parent, so
+those edges are walked and the others are ignored. The resulting history
+graph is:
+
+-----------------------------------------------------------------------
+ I---X
+-----------------------------------------------------------------------
+
+When using `--full-history`, Git walks every edge. This will discover
+the commits `A` and `B` and the merge `M`, but also will reveal the
+merge commits `O` and `P`. With parent rewriting, the resulting graph is:
+
+-----------------------------------------------------------------------
+ .-A---M--------N---O---P
+ / / \ \ \/ / /
+ I B \ R-'`--' /
+ \ / \/ /
+ \ / /\ /
+ `---X--' `------'
+-----------------------------------------------------------------------
+
+Here, the merge commits `O` and `P` contribute extra noise, as they did
+not actually contribute a change to `file.txt`. They only merged a topic
+that was based on an older version of `file.txt`. This is a common
+issue in repositories using a workflow where many contributors work in
+parallel and merge their topic branches along a single trunk: manu
+unrelated merges appear in the `--full-history` results.
+
+When using the `--simplify-merges` option, the commits `O` and `P`
+disappear from the results. This is because the rewritten second parents
+of `O` and `P` are reachable from their first parents. Those edges are
+removed and then the commits look like single-parent commits that are
+TREESAME to their parent. This also happens to the commit `N`, resulting
+in a history view as follows:
+
+-----------------------------------------------------------------------
+ .-A---M--.
+ / / \
+ I B R
+ \ / /
+ \ / /
+ `---X--'
+-----------------------------------------------------------------------
+
+In this view, we see all of the important single-parent changes from
+`A`, `B`, and `X`. We also see the carefully-resolved merge `M` and the
+not-so-carefully-resolved merge `R`. This is usually enough information
+to determine why the commits `A` and `B` "disappeared" from history in
+the default view. However, there are a few issues with this approach.
+
+The first issue is performance. Unlike any previous option, the
+`--simplify-merges` option requires walking the entire commit history
+before returning a single result. This can make the option difficult to
+use for very large repositories.
+
+The second issue is one of auditing. When many contributors are working
+on the same repository, it is important which merge commits introduced
+a change into an important branch. The problematic merge `R` above is
+not likely to be the merge commit that was used to merge into an
+important branch. Instead, the merge `N` was used to merge `R` and `X`
+into the important branch. This commit may have information about why
+the change `X` came to override the changes from `A` and `B` in its
+commit message.
+
+--show-pulls::
+ In addition to the commits shown in the default history, show
+ each merge commit that is not TREESAME to its first parent but
+ is TREESAME to a later parent.
++
+When a merge commit is included by `--show-pulls`, the merge is
+treated as if it "pulled" the change from another branch. When using
+`--show-pulls` on this example (and no other options) the resulting
+graph is:
++
+-----------------------------------------------------------------------
+ I---X---R---N
+-----------------------------------------------------------------------
++
+Here, the merge commits `R` and `N` are included because they pulled
+the commits `X` and `R` into the base branch, respectively. These
+merges are the reason the commits `A` and `B` do not appear in the
+default history.
++
+When `--show-pulls` is paired with `--simplify-merges`, the
+graph includes all of the necessary information:
++
+-----------------------------------------------------------------------
+ .-A---M--. N
+ / / \ /
+ I B R
+ \ / /
+ \ / /
+ `---X--'
+-----------------------------------------------------------------------
++
+Notice that since `M` is reachable from `R`, the edge from `N` to `M`
+was simplified away. However, `N` still appears in the history as an
+important commit because it "pulled" the change `R` into the main
+branch.
+
The `--simplify-by-decoration` option allows you to view only the
big picture of the topology of the history, by omitting commits
that are not referenced by tags. Commits are marked as !TREESAME
diff --git a/Documentation/revisions.txt b/Documentation/revisions.txt
index 97f995e..1ad9506 100644
--- a/Documentation/revisions.txt
+++ b/Documentation/revisions.txt
@@ -233,7 +233,7 @@
A = = A^0
B = A^ = A^1 = A~1
- C = A^2 = A^2
+ C = = A^2
D = A^^ = A^1^1 = A~2
E = B^2 = A^^2
F = B^3 = A^^3
diff --git a/Documentation/technical/api-trace2.txt b/Documentation/technical/api-trace2.txt
index 4f07cea..6b60855 100644
--- a/Documentation/technical/api-trace2.txt
+++ b/Documentation/technical/api-trace2.txt
@@ -656,7 +656,8 @@
------------
`"def_param"`::
- This event is generated to log a global parameter.
+ This event is generated to log a global parameter, such as a config
+ setting, command-line flag, or environment variable.
+
------------
{
diff --git a/Documentation/technical/commit-graph-format.txt b/Documentation/technical/commit-graph-format.txt
index a4f1744..1beef17 100644
--- a/Documentation/technical/commit-graph-format.txt
+++ b/Documentation/technical/commit-graph-format.txt
@@ -17,6 +17,9 @@
- The parents of the commit, stored using positional references within
the graph file.
+- The Bloom filter of the commit carrying the paths that were changed between
+ the commit and its first parent, if requested.
+
These positional references are stored as unsigned 32-bit integers
corresponding to the array position within the list of commit OIDs. Due
to some special constants we use to track parents, we can store at most
@@ -93,6 +96,33 @@
positions for the parents until reaching a value with the most-significant
bit on. The other bits correspond to the position of the last parent.
+ Bloom Filter Index (ID: {'B', 'I', 'D', 'X'}) (N * 4 bytes) [Optional]
+ * The ith entry, BIDX[i], stores the number of bytes in all Bloom filters
+ from commit 0 to commit i (inclusive) in lexicographic order. The Bloom
+ filter for the i-th commit spans from BIDX[i-1] to BIDX[i] (plus header
+ length), where BIDX[-1] is 0.
+ * The BIDX chunk is ignored if the BDAT chunk is not present.
+
+ Bloom Filter Data (ID: {'B', 'D', 'A', 'T'}) [Optional]
+ * It starts with header consisting of three unsigned 32-bit integers:
+ - Version of the hash algorithm being used. We currently only support
+ value 1 which corresponds to the 32-bit version of the murmur3 hash
+ implemented exactly as described in
+ https://en.wikipedia.org/wiki/MurmurHash#Algorithm and the double
+ hashing technique using seed values 0x293ae76f and 0x7e646e2 as
+ described in https://doi.org/10.1007/978-3-540-30494-4_26 "Bloom Filters
+ in Probabilistic Verification"
+ - The number of times a path is hashed and hence the number of bit positions
+ that cumulatively determine whether a file is present in the commit.
+ - The minimum number of bits 'b' per entry in the Bloom filter. If the filter
+ contains 'n' entries, then the filter size is the minimum number of 64-bit
+ words that contain n*b bits.
+ * The rest of the chunk is the concatenation of all the computed Bloom
+ filters for the commits in lexicographic order.
+ * Note: Commits with no changes or more than 512 changes have Bloom filters
+ of length zero.
+ * The BDAT chunk is present if and only if BIDX is present.
+
Base Graphs List (ID: {'B', 'A', 'S', 'E'}) [Optional]
This list of H-byte hashes describe a set of B commit-graph files that
form a commit-graph chain. The graph position for the ith commit in this
diff --git a/Documentation/user-manual.conf b/Documentation/user-manual.conf
index d87294d..0148f12 100644
--- a/Documentation/user-manual.conf
+++ b/Documentation/user-manual.conf
@@ -9,13 +9,3 @@
[linkgit-inlinemacro]
<ulink url="{target}.html">{target}{0?({0})}</ulink>
-
-ifdef::backend-docbook[]
-# "unbreak" docbook-xsl v1.68 for manpages. v1.69 works with or without this.
-[listingblock]
-<example><title>{title}</title>
-<literallayout class="monospaced">
-|
-</literallayout>
-{title#}</example>
-endif::backend-docbook[]
diff --git a/GIT-VERSION-GEN b/GIT-VERSION-GEN
index dafe6d0..06a5333 100755
--- a/GIT-VERSION-GEN
+++ b/GIT-VERSION-GEN
@@ -1,7 +1,7 @@
#!/bin/sh
GVF=GIT-VERSION-FILE
-DEF_VER=v2.26.2
+DEF_VER=v2.27.0
LF='
'
diff --git a/INSTALL b/INSTALL
index 22c364f..9ba33e6 100644
--- a/INSTALL
+++ b/INSTALL
@@ -206,9 +206,7 @@
clone two separate git-htmldocs and git-manpages repositories next
to the clone of git itself.
- It has been reported that docbook-xsl version 1.72 and 1.73 are
- buggy; 1.72 misformats manual pages for callouts, and 1.73 needs
- the patch in contrib/patches/docbook-xsl-manpages-charmap.patch
+ The minimum supported version of docbook-xsl is 1.74.
Users attempting to build the documentation on Cygwin may need to ensure
that the /etc/xml/catalog file looks something like this:
diff --git a/Makefile b/Makefile
index 9804a07..90aa329 100644
--- a/Makefile
+++ b/Makefile
@@ -282,12 +282,6 @@
# Define NO_ST_BLOCKS_IN_STRUCT_STAT if your platform does not have st_blocks
# field that counts the on-disk footprint in 512-byte blocks.
#
-# Define DOCBOOK_XSL_172 if you want to format man pages with DocBook XSL v1.72
-# (not v1.73 or v1.71).
-#
-# Define ASCIIDOC_ROFF if your DocBook XSL does not escape raw roff directives
-# (versions 1.68.1 through v1.72).
-#
# Define GNU_ROFF if your target system uses GNU groff. This forces
# apostrophes to be ASCII so that cut&pasting examples to the shell
# will work.
@@ -405,9 +399,6 @@
# with a different indexfile format version. If it isn't set the index
# file format used is index-v[23].
#
-# Define GMTIME_UNRELIABLE_ERRORS if your gmtime() function does not
-# return NULL when it receives a bogus time_t.
-#
# Define HAVE_CLOCK_GETTIME if your platform has clock_gettime.
#
# Define HAVE_CLOCK_MONOTONIC if your platform has CLOCK_MONOTONIC.
@@ -609,7 +600,6 @@
SCRIPT_SH += git-merge-resolve.sh
SCRIPT_SH += git-mergetool.sh
SCRIPT_SH += git-quiltimport.sh
-SCRIPT_SH += git-legacy-stash.sh
SCRIPT_SH += git-request-pull.sh
SCRIPT_SH += git-submodule.sh
SCRIPT_SH += git-web--browse.sh
@@ -617,8 +607,8 @@
SCRIPT_LIB += git-mergetool--lib
SCRIPT_LIB += git-parse-remote
SCRIPT_LIB += git-rebase--preserve-merges
-SCRIPT_LIB += git-sh-setup
SCRIPT_LIB += git-sh-i18n
+SCRIPT_LIB += git-sh-setup
SCRIPT_PERL += git-add--interactive.perl
SCRIPT_PERL += git-archimport.perl
@@ -681,20 +671,23 @@
# ... and all the rest that could be moved out of bindir to gitexecdir
PROGRAMS += $(EXTRA_PROGRAMS)
+PROGRAM_OBJS += bugreport.o
PROGRAM_OBJS += credential-store.o
PROGRAM_OBJS += daemon.o
PROGRAM_OBJS += fast-import.o
PROGRAM_OBJS += http-backend.o
PROGRAM_OBJS += imap-send.o
+PROGRAM_OBJS += remote-testsvn.o
PROGRAM_OBJS += sh-i18n--envsubst.o
PROGRAM_OBJS += shell.o
-PROGRAM_OBJS += remote-testsvn.o
# Binary suffix, set to .exe for Windows builds
X =
PROGRAMS += $(patsubst %.o,git-%$X,$(PROGRAM_OBJS))
+TEST_BUILTINS_OBJS += test-advise.o
+TEST_BUILTINS_OBJS += test-bloom.o
TEST_BUILTINS_OBJS += test-chmtime.o
TEST_BUILTINS_OBJS += test-config.o
TEST_BUILTINS_OBJS += test-ctype.o
@@ -709,15 +702,16 @@
TEST_BUILTINS_OBJS += test-example-decorate.o
TEST_BUILTINS_OBJS += test-genrandom.o
TEST_BUILTINS_OBJS += test-genzeros.o
+TEST_BUILTINS_OBJS += test-hash-speed.o
TEST_BUILTINS_OBJS += test-hash.o
TEST_BUILTINS_OBJS += test-hashmap.o
-TEST_BUILTINS_OBJS += test-hash-speed.o
TEST_BUILTINS_OBJS += test-index-version.o
TEST_BUILTINS_OBJS += test-json-writer.o
TEST_BUILTINS_OBJS += test-lazy-init-name-hash.o
TEST_BUILTINS_OBJS += test-match-trees.o
TEST_BUILTINS_OBJS += test-mergesort.o
TEST_BUILTINS_OBJS += test-mktemp.o
+TEST_BUILTINS_OBJS += test-oid-array.o
TEST_BUILTINS_OBJS += test-oidmap.o
TEST_BUILTINS_OBJS += test-online-cpus.o
TEST_BUILTINS_OBJS += test-parse-options.o
@@ -738,7 +732,6 @@
TEST_BUILTINS_OBJS += test-scrap-cache-tree.o
TEST_BUILTINS_OBJS += test-serve-v2.o
TEST_BUILTINS_OBJS += test-sha1.o
-TEST_BUILTINS_OBJS += test-sha1-array.o
TEST_BUILTINS_OBJS += test-sha256.o
TEST_BUILTINS_OBJS += test-sigchain.o
TEST_BUILTINS_OBJS += test-strcmp-offset.o
@@ -748,10 +741,10 @@
TEST_BUILTINS_OBJS += test-subprocess.o
TEST_BUILTINS_OBJS += test-trace2.o
TEST_BUILTINS_OBJS += test-urlmatch-normalization.o
-TEST_BUILTINS_OBJS += test-xml-encode.o
TEST_BUILTINS_OBJS += test-wildmatch.o
TEST_BUILTINS_OBJS += test-windows-named-pipe.o
TEST_BUILTINS_OBJS += test-write-cache.o
+TEST_BUILTINS_OBJS += test-xml-encode.o
# Do not add more tests here unless they have extra dependencies. Add
# them in TEST_BUILTINS_OBJS above.
@@ -788,10 +781,10 @@
# what test wrappers are needed and 'install' will install, in bindir
BINDIR_PROGRAMS_NEED_X += git
-BINDIR_PROGRAMS_NEED_X += git-upload-pack
BINDIR_PROGRAMS_NEED_X += git-receive-pack
-BINDIR_PROGRAMS_NEED_X += git-upload-archive
BINDIR_PROGRAMS_NEED_X += git-shell
+BINDIR_PROGRAMS_NEED_X += git-upload-archive
+BINDIR_PROGRAMS_NEED_X += git-upload-pack
BINDIR_PROGRAMS_NO_X += git-cvsserver
@@ -815,6 +808,7 @@
XDIFF_LIB = xdiff/lib.a
VCSSVN_LIB = vcs-svn/lib.a
+GENERATED_H += config-list.h
GENERATED_H += command-list.h
LIB_H := $(sort $(patsubst ./%,%,$(shell git ls-files '*.h' ':!t/' ':!Documentation/' 2>/dev/null || \
@@ -831,15 +825,16 @@
LIB_OBJS += alias.o
LIB_OBJS += alloc.o
LIB_OBJS += apply.o
-LIB_OBJS += archive.o
LIB_OBJS += archive-tar.o
LIB_OBJS += archive-zip.o
+LIB_OBJS += archive.o
LIB_OBJS += argv-array.o
LIB_OBJS += attr.o
LIB_OBJS += base85.o
LIB_OBJS += bisect.o
LIB_OBJS += blame.o
LIB_OBJS += blob.o
+LIB_OBJS += bloom.o
LIB_OBJS += branch.o
LIB_OBJS += bulk-checkin.o
LIB_OBJS += bundle.o
@@ -849,9 +844,9 @@
LIB_OBJS += color.o
LIB_OBJS += column.o
LIB_OBJS += combine-diff.o
-LIB_OBJS += commit.o
LIB_OBJS += commit-graph.o
LIB_OBJS += commit-reach.o
+LIB_OBJS += commit.o
LIB_OBJS += compat/obstack.o
LIB_OBJS += compat/terminal.o
LIB_OBJS += config.o
@@ -865,17 +860,17 @@
LIB_OBJS += date.o
LIB_OBJS += decorate.o
LIB_OBJS += delta-islands.o
+LIB_OBJS += diff-delta.o
+LIB_OBJS += diff-lib.o
+LIB_OBJS += diff-no-index.o
+LIB_OBJS += diff.o
LIB_OBJS += diffcore-break.o
LIB_OBJS += diffcore-delta.o
LIB_OBJS += diffcore-order.o
LIB_OBJS += diffcore-pickaxe.o
LIB_OBJS += diffcore-rename.o
-LIB_OBJS += diff-delta.o
-LIB_OBJS += diff-lib.o
-LIB_OBJS += diff-no-index.o
-LIB_OBJS += diff.o
-LIB_OBJS += dir.o
LIB_OBJS += dir-iterator.o
+LIB_OBJS += dir.o
LIB_OBJS += editor.o
LIB_OBJS += entry.o
LIB_OBJS += environment.o
@@ -886,6 +881,7 @@
LIB_OBJS += exec-cmd.o
LIB_OBJS += fetch-negotiator.o
LIB_OBJS += fetch-pack.o
+LIB_OBJS += fmt-merge-msg.o
LIB_OBJS += fsck.o
LIB_OBJS += fsmonitor.o
LIB_OBJS += gettext.o
@@ -893,7 +889,6 @@
LIB_OBJS += graph.o
LIB_OBJS += grep.o
LIB_OBJS += hashmap.o
-LIB_OBJS += linear-assignment.o
LIB_OBJS += help.o
LIB_OBJS += hex.o
LIB_OBJS += ident.o
@@ -903,9 +898,10 @@
LIB_OBJS += levenshtein.o
LIB_OBJS += line-log.o
LIB_OBJS += line-range.o
-LIB_OBJS += list-objects.o
-LIB_OBJS += list-objects-filter.o
+LIB_OBJS += linear-assignment.o
LIB_OBJS += list-objects-filter-options.o
+LIB_OBJS += list-objects-filter.o
+LIB_OBJS += list-objects.o
LIB_OBJS += ll-merge.o
LIB_OBJS += lockfile.o
LIB_OBJS += log-tree.o
@@ -914,31 +910,32 @@
LIB_OBJS += mailmap.o
LIB_OBJS += match-trees.o
LIB_OBJS += mem-pool.o
-LIB_OBJS += merge.o
LIB_OBJS += merge-blobs.o
LIB_OBJS += merge-recursive.o
+LIB_OBJS += merge.o
LIB_OBJS += mergesort.o
LIB_OBJS += midx.o
LIB_OBJS += name-hash.o
LIB_OBJS += negotiator/default.o
LIB_OBJS += negotiator/skipping.o
-LIB_OBJS += notes.o
LIB_OBJS += notes-cache.o
LIB_OBJS += notes-merge.o
LIB_OBJS += notes-utils.o
+LIB_OBJS += notes.o
LIB_OBJS += object.o
+LIB_OBJS += oid-array.o
LIB_OBJS += oidmap.o
LIB_OBJS += oidset.o
-LIB_OBJS += packfile.o
-LIB_OBJS += pack-bitmap.o
LIB_OBJS += pack-bitmap-write.o
+LIB_OBJS += pack-bitmap.o
LIB_OBJS += pack-check.o
LIB_OBJS += pack-objects.o
LIB_OBJS += pack-revindex.o
LIB_OBJS += pack-write.o
+LIB_OBJS += packfile.o
LIB_OBJS += pager.o
-LIB_OBJS += parse-options.o
LIB_OBJS += parse-options-cb.o
+LIB_OBJS += parse-options.o
LIB_OBJS += patch-delta.o
LIB_OBJS += patch-ids.o
LIB_OBJS += path.o
@@ -951,12 +948,14 @@
LIB_OBJS += promisor-remote.o
LIB_OBJS += prompt.o
LIB_OBJS += protocol.o
+LIB_OBJS += prune-packed.o
LIB_OBJS += quote.o
LIB_OBJS += range-diff.o
LIB_OBJS += reachable.o
LIB_OBJS += read-cache.o
-LIB_OBJS += rebase.o
LIB_OBJS += rebase-interactive.o
+LIB_OBJS += rebase.o
+LIB_OBJS += ref-filter.o
LIB_OBJS += reflog-walk.o
LIB_OBJS += refs.o
LIB_OBJS += refs/files-backend.o
@@ -964,12 +963,12 @@
LIB_OBJS += refs/packed-backend.o
LIB_OBJS += refs/ref-cache.o
LIB_OBJS += refspec.o
-LIB_OBJS += ref-filter.o
LIB_OBJS += remote.o
LIB_OBJS += replace-object.o
LIB_OBJS += repo-settings.o
LIB_OBJS += repository.o
LIB_OBJS += rerere.o
+LIB_OBJS += reset.o
LIB_OBJS += resolve-undo.o
LIB_OBJS += revision.o
LIB_OBJS += run-command.o
@@ -978,9 +977,8 @@
LIB_OBJS += serve.o
LIB_OBJS += server-info.o
LIB_OBJS += setup.o
-LIB_OBJS += sha1-array.o
-LIB_OBJS += sha1-lookup.o
LIB_OBJS += sha1-file.o
+LIB_OBJS += sha1-lookup.o
LIB_OBJS += sha1-name.o
LIB_OBJS += shallow.o
LIB_OBJS += sideband.o
@@ -990,9 +988,9 @@
LIB_OBJS += strbuf.o
LIB_OBJS += streaming.o
LIB_OBJS += string-list.o
-LIB_OBJS += submodule.o
-LIB_OBJS += submodule-config.o
LIB_OBJS += sub-process.o
+LIB_OBJS += submodule-config.o
+LIB_OBJS += submodule.o
LIB_OBJS += symlinks.o
LIB_OBJS += tag.o
LIB_OBJS += tempfile.o
@@ -1011,11 +1009,11 @@
LIB_OBJS += trace2/tr2_tgt_perf.o
LIB_OBJS += trace2/tr2_tls.o
LIB_OBJS += trailer.o
-LIB_OBJS += transport.o
LIB_OBJS += transport-helper.o
+LIB_OBJS += transport.o
LIB_OBJS += tree-diff.o
-LIB_OBJS += tree.o
LIB_OBJS += tree-walk.o
+LIB_OBJS += tree.o
LIB_OBJS += unpack-trees.o
LIB_OBJS += upload-pack.o
LIB_OBJS += url.o
@@ -1055,9 +1053,9 @@
BUILTIN_OBJS += builtin/clean.o
BUILTIN_OBJS += builtin/clone.o
BUILTIN_OBJS += builtin/column.o
+BUILTIN_OBJS += builtin/commit-graph.o
BUILTIN_OBJS += builtin/commit-tree.o
BUILTIN_OBJS += builtin/commit.o
-BUILTIN_OBJS += builtin/commit-graph.o
BUILTIN_OBJS += builtin/config.o
BUILTIN_OBJS += builtin/count-objects.o
BUILTIN_OBJS += builtin/credential.o
@@ -1088,13 +1086,13 @@
BUILTIN_OBJS += builtin/ls-tree.o
BUILTIN_OBJS += builtin/mailinfo.o
BUILTIN_OBJS += builtin/mailsplit.o
-BUILTIN_OBJS += builtin/merge.o
BUILTIN_OBJS += builtin/merge-base.o
BUILTIN_OBJS += builtin/merge-file.o
BUILTIN_OBJS += builtin/merge-index.o
BUILTIN_OBJS += builtin/merge-ours.o
BUILTIN_OBJS += builtin/merge-recursive.o
BUILTIN_OBJS += builtin/merge-tree.o
+BUILTIN_OBJS += builtin/merge.o
BUILTIN_OBJS += builtin/mktag.o
BUILTIN_OBJS += builtin/mktree.o
BUILTIN_OBJS += builtin/multi-pack-index.o
@@ -1114,9 +1112,9 @@
BUILTIN_OBJS += builtin/rebase.o
BUILTIN_OBJS += builtin/receive-pack.o
BUILTIN_OBJS += builtin/reflog.o
-BUILTIN_OBJS += builtin/remote.o
BUILTIN_OBJS += builtin/remote-ext.o
BUILTIN_OBJS += builtin/remote-fd.o
+BUILTIN_OBJS += builtin/remote.o
BUILTIN_OBJS += builtin/repack.o
BUILTIN_OBJS += builtin/replace.o
BUILTIN_OBJS += builtin/rerere.o
@@ -1358,17 +1356,22 @@
else
ifdef CURLDIR
# Try "-Wl,-rpath=$(CURLDIR)/$(lib)" in such a case.
- BASIC_CFLAGS += -I$(CURLDIR)/include
+ CURL_CFLAGS = -I$(CURLDIR)/include
CURL_LIBCURL = -L$(CURLDIR)/$(lib) $(CC_LD_DYNPATH)$(CURLDIR)/$(lib)
else
+ CURL_CFLAGS =
CURL_LIBCURL =
endif
-ifdef CURL_LDFLAGS
+ ifndef CURL_LDFLAGS
+ CURL_LDFLAGS = $(eval CURL_LDFLAGS := $$(shell $$(CURL_CONFIG) --libs))$(CURL_LDFLAGS)
+ endif
CURL_LIBCURL += $(CURL_LDFLAGS)
-else
- CURL_LIBCURL += $(shell $(CURL_CONFIG) --libs)
-endif
+
+ ifndef CURL_CFLAGS
+ CURL_CFLAGS = $(eval CURL_CFLAGS := $$(shell $$(CURL_CONFIG) --cflags))$(CURL_CFLAGS)
+ endif
+ BASIC_CFLAGS += $(CURL_CFLAGS)
REMOTE_CURL_PRIMARY = git-remote-http$X
REMOTE_CURL_ALIASES = git-remote-https$X git-remote-ftp$X git-remote-ftps$X
@@ -1803,11 +1806,6 @@
MSGFMT += --check --statistics
endif
-ifdef GMTIME_UNRELIABLE_ERRORS
- COMPAT_OBJS += compat/gmtime.o
- BASIC_CFLAGS += -DGMTIME_UNRELIABLE_ERRORS
-endif
-
ifdef HAVE_CLOCK_GETTIME
BASIC_CFLAGS += -DHAVE_CLOCK_GETTIME
endif
@@ -2133,7 +2131,7 @@
help.sp help.s help.o: command-list.h
-builtin/help.sp builtin/help.s builtin/help.o: command-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)"' \
@@ -2153,6 +2151,12 @@
ln -s $< $@ 2>/dev/null || \
cp $< $@
+config-list.h: generate-configlist.sh
+
+config-list.h:
+ $(QUIET_GEN)$(SHELL_PATH) ./generate-configlist.sh \
+ >$@+ && mv $@+ $@
+
command-list.h: generate-cmdlist.sh command-list.txt
command-list.h: $(wildcard Documentation/git*.txt) Documentation/*config.txt Documentation/config/*.txt
@@ -2335,16 +2339,16 @@
endif
XDIFF_OBJS += xdiff/xdiffi.o
-XDIFF_OBJS += xdiff/xprepare.o
-XDIFF_OBJS += xdiff/xutils.o
XDIFF_OBJS += xdiff/xemit.o
+XDIFF_OBJS += xdiff/xhistogram.o
XDIFF_OBJS += xdiff/xmerge.o
XDIFF_OBJS += xdiff/xpatience.o
-XDIFF_OBJS += xdiff/xhistogram.o
+XDIFF_OBJS += xdiff/xprepare.o
+XDIFF_OBJS += xdiff/xutils.o
+VCSSVN_OBJS += vcs-svn/fast_export.o
VCSSVN_OBJS += vcs-svn/line_buffer.o
VCSSVN_OBJS += vcs-svn/sliding_window.o
-VCSSVN_OBJS += vcs-svn/fast_export.o
VCSSVN_OBJS += vcs-svn/svndiff.o
VCSSVN_OBJS += vcs-svn/svndump.o
@@ -2455,6 +2459,10 @@
git-%$X: %.o GIT-LDFLAGS $(GITLIBS)
$(QUIET_LINK)$(CC) $(ALL_CFLAGS) -o $@ $(ALL_LDFLAGS) $(filter %.o,$^) $(LIBS)
+git-bugreport$X: bugreport.o GIT-LDFLAGS $(GITLIBS)
+ $(QUIET_LINK)$(CC) $(ALL_CFLAGS) -o $@ $(ALL_LDFLAGS) $(filter %.o,$^) \
+ $(LIBS)
+
git-imap-send$X: imap-send.o $(IMAP_SEND_BUILDDEPS) GIT-LDFLAGS $(GITLIBS)
$(QUIET_LINK)$(CC) $(ALL_CFLAGS) -o $@ $(ALL_LDFLAGS) $(filter %.o,$^) \
$(IMAP_SEND_LDFLAGS) $(LIBS)
@@ -2786,7 +2794,7 @@
.PHONY: sparse $(SP_OBJ)
sparse: $(SP_OBJ)
-EXCEPT_HDRS := command-list.h unicode-width.h compat/% xdiff/%
+EXCEPT_HDRS := command-list.h config-list.h unicode-width.h compat/% xdiff/%
ifndef GCRYPT_SHA256
EXCEPT_HDRS += sha256/gcrypt.h
endif
@@ -2808,7 +2816,7 @@
style:
git clang-format --style file --diff --extensions c,h
-check: command-list.h
+check: config-list.h command-list.h
@if sparse; \
then \
echo >&2 "Use 'make sparse' instead"; \
@@ -3152,9 +3160,10 @@
#
ALL_COMMANDS = $(ALL_PROGRAMS) $(SCRIPT_LIB) $(BUILT_INS)
ALL_COMMANDS += git
+ALL_COMMANDS += git-citool
+ALL_COMMANDS += git-gui
ALL_COMMANDS += gitk
ALL_COMMANDS += gitweb
-ALL_COMMANDS += git-gui git-citool
.PHONY: check-docs
check-docs::
diff --git a/README.md b/README.md
index 9d4564c..eb8115e 100644
--- a/README.md
+++ b/README.md
@@ -1,4 +1,4 @@
-[![Build Status](https://dev.azure.com/git/git/_apis/build/status/git.git)](https://dev.azure.com/git/git/_build/latest?definitionId=11)
+[![Build status](https://github.com/git/git/workflows/CI/PR/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 3427be6..f3d8527 120000
--- a/RelNotes
+++ b/RelNotes
@@ -1 +1 @@
-Documentation/RelNotes/2.26.2.txt
\ No newline at end of file
+Documentation/RelNotes/2.27.0.txt
\ No newline at end of file
diff --git a/abspath.c b/abspath.c
index 9857985..6f15a41 100644
--- a/abspath.c
+++ b/abspath.c
@@ -202,22 +202,6 @@
return retval;
}
-/*
- * Resolve `path` into an absolute, cleaned-up path. The return value
- * comes from a shared buffer.
- */
-const char *real_path(const char *path)
-{
- static struct strbuf realpath = STRBUF_INIT;
- return strbuf_realpath(&realpath, path, 1);
-}
-
-const char *real_path_if_valid(const char *path)
-{
- static struct strbuf realpath = STRBUF_INIT;
- return strbuf_realpath(&realpath, path, 0);
-}
-
char *real_pathdup(const char *path, int die_on_error)
{
struct strbuf realpath = STRBUF_INIT;
@@ -233,7 +217,7 @@
/*
* Use this to get an absolute path from a relative one. If you want
- * to resolve links, you should use real_path.
+ * to resolve links, you should use strbuf_realpath.
*/
const char *absolute_path(const char *path)
{
diff --git a/add-interactive.c b/add-interactive.c
index 4a9bf85..29cd2fe 100644
--- a/add-interactive.c
+++ b/add-interactive.c
@@ -9,6 +9,7 @@
#include "lockfile.h"
#include "dir.h"
#include "run-command.h"
+#include "prompt.h"
static void init_color(struct repository *r, struct add_i_state *s,
const char *slot_name, char *dst,
@@ -289,13 +290,12 @@
fputs(singleton ? "> " : ">> ", stdout);
fflush(stdout);
- if (strbuf_getline(&input, stdin) == EOF) {
+ if (git_read_line_interactively(&input) == EOF) {
putchar('\n');
if (immediate)
res = LIST_AND_CHOOSE_QUIT;
break;
}
- strbuf_trim(&input);
if (!input.len)
break;
diff --git a/add-patch.c b/add-patch.c
index d8dafa8..d8bfe37 100644
--- a/add-patch.c
+++ b/add-patch.c
@@ -7,6 +7,7 @@
#include "color.h"
#include "diff.h"
#include "compat/terminal.h"
+#include "prompt.h"
enum prompt_mode_type {
PROMPT_MODE_CHANGE = 0, PROMPT_DELETION, PROMPT_HUNK,
@@ -1158,9 +1159,8 @@
return res;
}
- if (strbuf_getline(&s->answer, stdin) == EOF)
+ if (git_read_line_interactively(&s->answer) == EOF)
return EOF;
- strbuf_trim_trailing_newline(&s->answer);
return 0;
}
diff --git a/advice.c b/advice.c
index 97f3f98..f0a3d32 100644
--- a/advice.c
+++ b/advice.c
@@ -29,7 +29,6 @@
int advice_waiting_for_editor = 1;
int advice_graft_file_deprecated = 1;
int advice_checkout_ambiguous_remote_branch_name = 1;
-int advice_nested_tag = 1;
int advice_submodule_alternate_error_strategy_die = 1;
int advice_add_ignored_file = 1;
int advice_add_empty_pathspec = 1;
@@ -82,7 +81,7 @@
{ "sequencerInUse", &advice_sequencer_in_use },
{ "implicitIdentity", &advice_implicit_identity },
{ "detachedHead", &advice_detached_head },
- { "setupStreamFailure", &advice_set_upstream_failure },
+ { "setUpstreamFailure", &advice_set_upstream_failure },
{ "objectNameWarning", &advice_object_name_warning },
{ "amWorkDir", &advice_amworkdir },
{ "rmHints", &advice_rm_hints },
@@ -91,7 +90,6 @@
{ "waitingForEditor", &advice_waiting_for_editor },
{ "graftFileDeprecated", &advice_graft_file_deprecated },
{ "checkoutAmbiguousRemoteBranchName", &advice_checkout_ambiguous_remote_branch_name },
- { "nestedTag", &advice_nested_tag },
{ "submoduleAlternateErrorStrategyDie", &advice_submodule_alternate_error_strategy_die },
{ "addIgnoredFile", &advice_add_ignored_file },
{ "addEmptyPathspec", &advice_add_empty_pathspec },
@@ -100,15 +98,58 @@
{ "pushNonFastForward", &advice_push_update_rejected }
};
-void advise(const char *advice, ...)
+static struct {
+ const char *key;
+ int enabled;
+} advice_setting[] = {
+ [ADVICE_ADD_EMBEDDED_REPO] = { "addEmbeddedRepo", 1 },
+ [ADVICE_AM_WORK_DIR] = { "amWorkDir", 1 },
+ [ADVICE_CHECKOUT_AMBIGUOUS_REMOTE_BRANCH_NAME] = { "checkoutAmbiguousRemoteBranchName", 1 },
+ [ADVICE_COMMIT_BEFORE_MERGE] = { "commitBeforeMerge", 1 },
+ [ADVICE_DETACHED_HEAD] = { "detachedHead", 1 },
+ [ADVICE_FETCH_SHOW_FORCED_UPDATES] = { "fetchShowForcedUpdates", 1 },
+ [ADVICE_GRAFT_FILE_DEPRECATED] = { "graftFileDeprecated", 1 },
+ [ADVICE_IGNORED_HOOK] = { "ignoredHook", 1 },
+ [ADVICE_IMPLICIT_IDENTITY] = { "implicitIdentity", 1 },
+ [ADVICE_NESTED_TAG] = { "nestedTag", 1 },
+ [ADVICE_OBJECT_NAME_WARNING] = { "objectNameWarning", 1 },
+ [ADVICE_PUSH_ALREADY_EXISTS] = { "pushAlreadyExists", 1 },
+ [ADVICE_PUSH_FETCH_FIRST] = { "pushFetchFirst", 1 },
+ [ADVICE_PUSH_NEEDS_FORCE] = { "pushNeedsForce", 1 },
+
+ /* make this an alias for backward compatibility */
+ [ADVICE_PUSH_UPDATE_REJECTED_ALIAS] = { "pushNonFastForward", 1 },
+
+ [ADVICE_PUSH_NON_FF_CURRENT] = { "pushNonFFCurrent", 1 },
+ [ADVICE_PUSH_NON_FF_MATCHING] = { "pushNonFFMatching", 1 },
+ [ADVICE_PUSH_UNQUALIFIED_REF_NAME] = { "pushUnqualifiedRefName", 1 },
+ [ADVICE_PUSH_UPDATE_REJECTED] = { "pushUpdateRejected", 1 },
+ [ADVICE_RESET_QUIET_WARNING] = { "resetQuiet", 1 },
+ [ADVICE_RESOLVE_CONFLICT] = { "resolveConflict", 1 },
+ [ADVICE_RM_HINTS] = { "rmHints", 1 },
+ [ADVICE_SEQUENCER_IN_USE] = { "sequencerInUse", 1 },
+ [ADVICE_SET_UPSTREAM_FAILURE] = { "setUpstreamFailure", 1 },
+ [ADVICE_STATUS_AHEAD_BEHIND_WARNING] = { "statusAheadBehindWarning", 1 },
+ [ADVICE_STATUS_HINTS] = { "statusHints", 1 },
+ [ADVICE_STATUS_U_OPTION] = { "statusUoption", 1 },
+ [ADVICE_SUBMODULE_ALTERNATE_ERROR_STRATEGY_DIE] = { "submoduleAlternateErrorStrategyDie", 1 },
+ [ADVICE_WAITING_FOR_EDITOR] = { "waitingForEditor", 1 },
+};
+
+static const char turn_off_instructions[] =
+N_("\n"
+ "Disable this message with \"git config advice.%s false\"");
+
+static void vadvise(const char *advice, int display_instructions,
+ const char *key, va_list params)
{
struct strbuf buf = STRBUF_INIT;
- va_list params;
const char *cp, *np;
- va_start(params, advice);
strbuf_vaddf(&buf, advice, params);
- va_end(params);
+
+ if (display_instructions)
+ strbuf_addf(&buf, turn_off_instructions, key);
for (cp = buf.buf; *cp; cp = np) {
np = strchrnul(cp, '\n');
@@ -122,6 +163,37 @@
strbuf_release(&buf);
}
+void advise(const char *advice, ...)
+{
+ va_list params;
+ va_start(params, advice);
+ vadvise(advice, 0, "", params);
+ va_end(params);
+}
+
+int advice_enabled(enum advice_type type)
+{
+ switch(type) {
+ case ADVICE_PUSH_UPDATE_REJECTED:
+ return advice_setting[ADVICE_PUSH_UPDATE_REJECTED].enabled &&
+ advice_setting[ADVICE_PUSH_UPDATE_REJECTED_ALIAS].enabled;
+ default:
+ return advice_setting[type].enabled;
+ }
+}
+
+void advise_if_enabled(enum advice_type type, const char *advice, ...)
+{
+ va_list params;
+
+ if (!advice_enabled(type))
+ return;
+
+ va_start(params, advice);
+ vadvise(advice, 1, advice_setting[type].key, params);
+ va_end(params);
+}
+
int git_default_advice_config(const char *var, const char *value)
{
const char *k, *slot_name;
@@ -148,6 +220,13 @@
if (strcasecmp(k, advice_config[i].name))
continue;
*advice_config[i].preference = git_config_bool(var, value);
+ break;
+ }
+
+ for (i = 0; i < ARRAY_SIZE(advice_setting); i++) {
+ if (strcasecmp(k, advice_setting[i].key))
+ continue;
+ advice_setting[i].enabled = git_config_bool(var, value);
return 0;
}
@@ -158,8 +237,8 @@
{
int i;
- for (i = 0; i < ARRAY_SIZE(advice_config); i++)
- list_config_item(list, prefix, advice_config[i].name);
+ for (i = 0; i < ARRAY_SIZE(advice_setting); i++)
+ list_config_item(list, prefix, advice_setting[i].key);
}
int error_resolve_conflict(const char *me)
diff --git a/advice.h b/advice.h
index 0e6e58d..16f2c11 100644
--- a/advice.h
+++ b/advice.h
@@ -29,14 +29,64 @@
extern int advice_waiting_for_editor;
extern int advice_graft_file_deprecated;
extern int advice_checkout_ambiguous_remote_branch_name;
-extern int advice_nested_tag;
extern int advice_submodule_alternate_error_strategy_die;
extern int advice_add_ignored_file;
extern int advice_add_empty_pathspec;
+/*
+ * To add a new advice, you need to:
+ * Define a new advice_type.
+ * Add a new entry to advice_setting array.
+ * Add the new config variable to Documentation/config/advice.txt.
+ * Call advise_if_enabled to print your advice.
+ */
+ enum advice_type {
+ ADVICE_ADD_EMBEDDED_REPO,
+ ADVICE_AM_WORK_DIR,
+ ADVICE_CHECKOUT_AMBIGUOUS_REMOTE_BRANCH_NAME,
+ ADVICE_COMMIT_BEFORE_MERGE,
+ ADVICE_DETACHED_HEAD,
+ ADVICE_FETCH_SHOW_FORCED_UPDATES,
+ ADVICE_GRAFT_FILE_DEPRECATED,
+ ADVICE_IGNORED_HOOK,
+ ADVICE_IMPLICIT_IDENTITY,
+ ADVICE_NESTED_TAG,
+ ADVICE_OBJECT_NAME_WARNING,
+ ADVICE_PUSH_ALREADY_EXISTS,
+ ADVICE_PUSH_FETCH_FIRST,
+ ADVICE_PUSH_NEEDS_FORCE,
+ ADVICE_PUSH_NON_FF_CURRENT,
+ ADVICE_PUSH_NON_FF_MATCHING,
+ ADVICE_PUSH_UNQUALIFIED_REF_NAME,
+ ADVICE_PUSH_UPDATE_REJECTED_ALIAS,
+ ADVICE_PUSH_UPDATE_REJECTED,
+ ADVICE_RESET_QUIET_WARNING,
+ ADVICE_RESOLVE_CONFLICT,
+ ADVICE_RM_HINTS,
+ ADVICE_SEQUENCER_IN_USE,
+ ADVICE_SET_UPSTREAM_FAILURE,
+ ADVICE_STATUS_AHEAD_BEHIND_WARNING,
+ ADVICE_STATUS_HINTS,
+ ADVICE_STATUS_U_OPTION,
+ ADVICE_SUBMODULE_ALTERNATE_ERROR_STRATEGY_DIE,
+ ADVICE_WAITING_FOR_EDITOR,
+};
+
int git_default_advice_config(const char *var, const char *value);
__attribute__((format (printf, 1, 2)))
void advise(const char *advice, ...);
+
+/**
+ * Checks if advice type is enabled (can be printed to the user).
+ * Should be called before advise().
+ */
+int advice_enabled(enum advice_type type);
+
+/**
+ * Checks the visibility of the advice before printing.
+ */
+void advise_if_enabled(enum advice_type type, const char *advice, ...);
+
int error_resolve_conflict(const char *me);
void NORETURN die_resolve_conflict(const char *me);
void NORETURN die_conclude_merge(void);
diff --git a/apply.c b/apply.c
index bdc008f..8bff604 100644
--- a/apply.c
+++ b/apply.c
@@ -4349,7 +4349,7 @@
if (fd < 0)
return 1;
- if (convert_to_working_tree(state->repo->index, path, buf, size, &nbuf)) {
+ if (convert_to_working_tree(state->repo->index, path, buf, size, &nbuf, NULL)) {
size = nbuf.len;
buf = nbuf.buf;
}
@@ -4964,15 +4964,15 @@
const char * const *apply_usage)
{
struct option builtin_apply_options[] = {
- { OPTION_CALLBACK, 0, "exclude", state, N_("path"),
+ OPT_CALLBACK_F(0, "exclude", state, N_("path"),
N_("don't apply changes matching the given path"),
- PARSE_OPT_NONEG, apply_option_parse_exclude },
- { OPTION_CALLBACK, 0, "include", state, N_("path"),
+ PARSE_OPT_NONEG, apply_option_parse_exclude),
+ OPT_CALLBACK_F(0, "include", state, N_("path"),
N_("apply changes matching the given path"),
- PARSE_OPT_NONEG, apply_option_parse_include },
- { OPTION_CALLBACK, 'p', NULL, state, N_("num"),
+ PARSE_OPT_NONEG, apply_option_parse_include),
+ OPT_CALLBACK('p', NULL, state, N_("num"),
N_("remove <num> leading slashes from traditional diff paths"),
- 0, apply_option_parse_p },
+ apply_option_parse_p),
OPT_BOOL(0, "no-add", &state->no_add,
N_("ignore additions made by the patch")),
OPT_BOOL(0, "stat", &state->diffstat,
@@ -5005,15 +5005,15 @@
N_("paths are separated with NUL character"), '\0'),
OPT_INTEGER('C', NULL, &state->p_context,
N_("ensure at least <n> lines of context match")),
- { OPTION_CALLBACK, 0, "whitespace", state, N_("action"),
+ OPT_CALLBACK(0, "whitespace", state, N_("action"),
N_("detect new or modified lines that have whitespace errors"),
- 0, apply_option_parse_whitespace },
- { OPTION_CALLBACK, 0, "ignore-space-change", state, NULL,
+ apply_option_parse_whitespace),
+ OPT_CALLBACK_F(0, "ignore-space-change", state, NULL,
N_("ignore changes in whitespace when finding context"),
- PARSE_OPT_NOARG, apply_option_parse_space_change },
- { OPTION_CALLBACK, 0, "ignore-whitespace", state, NULL,
+ PARSE_OPT_NOARG, apply_option_parse_space_change),
+ OPT_CALLBACK_F(0, "ignore-whitespace", state, NULL,
N_("ignore changes in whitespace when finding context"),
- PARSE_OPT_NOARG, apply_option_parse_space_change },
+ PARSE_OPT_NOARG, apply_option_parse_space_change),
OPT_BOOL('R', "reverse", &state->apply_in_reverse,
N_("apply the patch in reverse")),
OPT_BOOL(0, "unidiff-zero", &state->unidiff_zero,
@@ -5029,9 +5029,9 @@
OPT_BIT(0, "recount", options,
N_("do not trust the line counts in the hunk headers"),
APPLY_OPT_RECOUNT),
- { OPTION_CALLBACK, 0, "directory", state, N_("root"),
+ OPT_CALLBACK(0, "directory", state, N_("root"),
N_("prepend <root> to all filenames"),
- 0, apply_option_parse_directory },
+ apply_option_parse_directory),
OPT_END()
};
diff --git a/archive-tar.c b/archive-tar.c
index 5a77701..5ceec36 100644
--- a/archive-tar.c
+++ b/archive-tar.c
@@ -364,7 +364,7 @@
static int nr_tar_filters;
static int alloc_tar_filters;
-static struct archiver *find_tar_filter(const char *name, int len)
+static struct archiver *find_tar_filter(const char *name, size_t len)
{
int i;
for (i = 0; i < nr_tar_filters; i++) {
@@ -380,7 +380,7 @@
struct archiver *ar;
const char *name;
const char *type;
- int namelen;
+ size_t namelen;
if (parse_config_key(var, "tar", &name, &namelen, &type) < 0 || !name)
return 0;
diff --git a/archive.c b/archive.c
index a8da0fc..fb39706 100644
--- a/archive.c
+++ b/archive.c
@@ -77,6 +77,11 @@
{
void *buffer;
const struct commit *commit = args->convert ? args->commit : NULL;
+ struct checkout_metadata meta;
+
+ init_checkout_metadata(&meta, args->refname,
+ args->commit_oid ? args->commit_oid :
+ (args->tree ? &args->tree->object.oid : NULL), oid);
path += args->baselen;
buffer = read_object_file(oid, type, sizep);
@@ -85,7 +90,7 @@
size_t size = 0;
strbuf_attach(&buf, buffer, *sizep, *sizep + 1);
- convert_to_working_tree(args->repo->index, path, buf.buf, buf.len, &buf);
+ convert_to_working_tree(args->repo->index, path, buf.buf, buf.len, &buf, &meta);
if (commit)
format_subst(commit, buf.buf, buf.len, &buf);
buffer = strbuf_detach(&buf, &size);
@@ -385,16 +390,17 @@
struct tree *tree;
const struct commit *commit;
struct object_id oid;
+ char *ref = NULL;
/* Remotes are only allowed to fetch actual refs */
if (remote && !remote_allow_unreachable) {
- char *ref = NULL;
const char *colon = strchrnul(name, ':');
int refnamelen = colon - name;
if (!dwim_ref(name, refnamelen, &oid, &ref))
die(_("no such ref: %.*s"), refnamelen, name);
- free(ref);
+ } else {
+ dwim_ref(name, strlen(name), &oid, &ref);
}
if (get_oid(name, &oid))
@@ -427,6 +433,7 @@
tree = parse_tree_indirect(&tree_oid);
}
+ ar_args->refname = ref;
ar_args->tree = tree;
ar_args->commit_oid = commit_oid;
ar_args->commit = commit;
diff --git a/archive.h b/archive.h
index e60e3dd..3bd96bf 100644
--- a/archive.h
+++ b/archive.h
@@ -8,6 +8,7 @@
struct archiver_args {
struct repository *repo;
+ const char *refname;
const char *base;
size_t baselen;
struct tree *tree;
diff --git a/azure-pipelines.yml b/azure-pipelines.yml
deleted file mode 100644
index 675c3a4..0000000
--- a/azure-pipelines.yml
+++ /dev/null
@@ -1,558 +0,0 @@
-variables:
- Agent.Source.Git.ShallowFetchDepth: 1
-
-jobs:
-- job: windows_build
- displayName: Windows Build
- condition: succeeded()
- pool:
- vmImage: windows-latest
- timeoutInMinutes: 240
- steps:
- - powershell: |
- if ("$GITFILESHAREPWD" -ne "" -and "$GITFILESHAREPWD" -ne "`$`(gitfileshare.pwd)") {
- net use s: \\gitfileshare.file.core.windows.net\test-cache "$GITFILESHAREPWD" /user:AZURE\gitfileshare /persistent:no
- cmd /c mklink /d "$(Build.SourcesDirectory)\test-cache" S:\
- }
- displayName: 'Mount test-cache'
- env:
- GITFILESHAREPWD: $(gitfileshare.pwd)
- - powershell: |
- $urlbase = "https://dev.azure.com/git-for-windows/git/_apis/build/builds"
- $id = ((Invoke-WebRequest -UseBasicParsing "${urlbase}?definitions=22&statusFilter=completed&resultFilter=succeeded&`$top=1").content | ConvertFrom-JSON).value[0].id
- $downloadUrl = ((Invoke-WebRequest -UseBasicParsing "${urlbase}/$id/artifacts").content | ConvertFrom-JSON).value[1].resource.downloadUrl
- (New-Object Net.WebClient).DownloadFile($downloadUrl,"git-sdk-64-minimal.zip")
- Expand-Archive git-sdk-64-minimal.zip -DestinationPath . -Force
- Remove-Item git-sdk-64-minimal.zip
-
- # Let Git ignore the SDK and the test-cache
- "/git-sdk-64-minimal/`n/test-cache/`n" | Out-File -NoNewLine -Encoding ascii -Append "$(Build.SourcesDirectory)\.git\info\exclude"
- displayName: 'Download git-sdk-64-minimal'
- - powershell: |
- & git-sdk-64-minimal\usr\bin\bash.exe -lc @"
- ci/make-test-artifacts.sh artifacts
- "@
- if (!$?) { exit(1) }
- displayName: Build
- env:
- HOME: $(Build.SourcesDirectory)
- MSYSTEM: MINGW64
- DEVELOPER: 1
- NO_PERL: 1
- - task: PublishPipelineArtifact@0
- displayName: 'Publish Pipeline Artifact: test artifacts'
- inputs:
- artifactName: 'windows-artifacts'
- targetPath: '$(Build.SourcesDirectory)\artifacts'
- - task: PublishPipelineArtifact@0
- displayName: 'Publish Pipeline Artifact: git-sdk-64-minimal'
- inputs:
- artifactName: 'git-sdk-64-minimal'
- targetPath: '$(Build.SourcesDirectory)\git-sdk-64-minimal'
- - powershell: |
- if ("$GITFILESHAREPWD" -ne "" -and "$GITFILESHAREPWD" -ne "`$`(gitfileshare.pwd)") {
- cmd /c rmdir "$(Build.SourcesDirectory)\test-cache"
- }
- displayName: 'Unmount test-cache'
- condition: true
- env:
- GITFILESHAREPWD: $(gitfileshare.pwd)
-
-- job: windows_test
- displayName: Windows Test
- dependsOn: windows_build
- condition: succeeded()
- pool:
- vmImage: windows-latest
- timeoutInMinutes: 240
- strategy:
- parallel: 10
- steps:
- - powershell: |
- if ("$GITFILESHAREPWD" -ne "" -and "$GITFILESHAREPWD" -ne "`$`(gitfileshare.pwd)") {
- net use s: \\gitfileshare.file.core.windows.net\test-cache "$GITFILESHAREPWD" /user:AZURE\gitfileshare /persistent:no
- cmd /c mklink /d "$(Build.SourcesDirectory)\test-cache" S:\
- }
- displayName: 'Mount test-cache'
- env:
- GITFILESHAREPWD: $(gitfileshare.pwd)
- - task: DownloadPipelineArtifact@0
- displayName: 'Download Pipeline Artifact: test artifacts'
- inputs:
- artifactName: 'windows-artifacts'
- targetPath: '$(Build.SourcesDirectory)'
- - task: DownloadPipelineArtifact@0
- displayName: 'Download Pipeline Artifact: git-sdk-64-minimal'
- inputs:
- artifactName: 'git-sdk-64-minimal'
- targetPath: '$(Build.SourcesDirectory)\git-sdk-64-minimal'
- - powershell: |
- & git-sdk-64-minimal\usr\bin\bash.exe -lc @"
- test -f artifacts.tar.gz || {
- echo No test artifacts found\; skipping >&2
- exit 0
- }
- tar xf artifacts.tar.gz || exit 1
-
- # Let Git ignore the SDK and the test-cache
- printf '%s\n' /git-sdk-64-minimal/ /test-cache/ >>.git/info/exclude
-
- ci/run-test-slice.sh `$SYSTEM_JOBPOSITIONINPHASE `$SYSTEM_TOTALJOBSINPHASE || {
- ci/print-test-failures.sh
- exit 1
- }
- "@
- if (!$?) { exit(1) }
- displayName: 'Test (parallel)'
- env:
- HOME: $(Build.SourcesDirectory)
- MSYSTEM: MINGW64
- NO_SVN_TESTS: 1
- GIT_TEST_SKIP_REBASE_P: 1
- - powershell: |
- if ("$GITFILESHAREPWD" -ne "" -and "$GITFILESHAREPWD" -ne "`$`(gitfileshare.pwd)") {
- cmd /c rmdir "$(Build.SourcesDirectory)\test-cache"
- }
- displayName: 'Unmount test-cache'
- condition: true
- env:
- GITFILESHAREPWD: $(gitfileshare.pwd)
- - task: PublishTestResults@2
- displayName: 'Publish Test Results **/TEST-*.xml'
- inputs:
- mergeTestResults: true
- testRunTitle: 'windows'
- platform: Windows
- publishRunAttachments: false
- condition: succeededOrFailed()
- - task: PublishBuildArtifacts@1
- displayName: 'Publish trash directories of failed tests'
- condition: failed()
- inputs:
- PathtoPublish: t/failed-test-artifacts
- ArtifactName: failed-test-artifacts
-
-- job: vs_build
- displayName: Visual Studio Build
- condition: succeeded()
- pool:
- vmImage: windows-latest
- timeoutInMinutes: 240
- steps:
- - powershell: |
- if ("$GITFILESHAREPWD" -ne "" -and "$GITFILESHAREPWD" -ne "`$`(gitfileshare.pwd)") {
- net use s: \\gitfileshare.file.core.windows.net\test-cache "$GITFILESHAREPWD" /user:AZURE\gitfileshare /persistent:no
- cmd /c mklink /d "$(Build.SourcesDirectory)\test-cache" S:\
- }
- displayName: 'Mount test-cache'
- env:
- GITFILESHAREPWD: $(gitfileshare.pwd)
- - powershell: |
- $urlbase = "https://dev.azure.com/git-for-windows/git/_apis/build/builds"
- $id = ((Invoke-WebRequest -UseBasicParsing "${urlbase}?definitions=22&statusFilter=completed&resultFilter=succeeded&`$top=1").content | ConvertFrom-JSON).value[0].id
- $downloadUrl = ((Invoke-WebRequest -UseBasicParsing "${urlbase}/$id/artifacts").content | ConvertFrom-JSON).value[1].resource.downloadUrl
- (New-Object Net.WebClient).DownloadFile($downloadUrl,"git-sdk-64-minimal.zip")
- Expand-Archive git-sdk-64-minimal.zip -DestinationPath . -Force
- Remove-Item git-sdk-64-minimal.zip
-
- # Let Git ignore the SDK and the test-cache
- "/git-sdk-64-minimal/`n/test-cache/`n" | Out-File -NoNewLine -Encoding ascii -Append "$(Build.SourcesDirectory)\.git\info\exclude"
- displayName: 'Download git-sdk-64-minimal'
- - powershell: |
- & git-sdk-64-minimal\usr\bin\bash.exe -lc @"
- make NDEBUG=1 DEVELOPER=1 vcxproj
- "@
- if (!$?) { exit(1) }
- displayName: Generate Visual Studio Solution
- env:
- HOME: $(Build.SourcesDirectory)
- MSYSTEM: MINGW64
- DEVELOPER: 1
- NO_PERL: 1
- GIT_CONFIG_PARAMETERS: "'user.name=CI' 'user.email=ci@git'"
- - powershell: |
- $urlbase = "https://dev.azure.com/git/git/_apis/build/builds"
- $id = ((Invoke-WebRequest -UseBasicParsing "${urlbase}?definitions=9&statusFilter=completed&resultFilter=succeeded&`$top=1").content | ConvertFrom-JSON).value[0].id
- $downloadUrl = ((Invoke-WebRequest -UseBasicParsing "${urlbase}/$id/artifacts").content | ConvertFrom-JSON).value[0].resource.downloadUrl
- (New-Object Net.WebClient).DownloadFile($downloadUrl, "compat.zip")
- Expand-Archive compat.zip -DestinationPath . -Force
- Remove-Item compat.zip
- displayName: 'Download vcpkg artifacts'
- - task: MSBuild@1
- inputs:
- solution: git.sln
- platform: x64
- configuration: Release
- maximumCpuCount: 4
- msbuildArguments: /p:PlatformToolset=v142
- - powershell: |
- & compat\vcbuild\vcpkg_copy_dlls.bat release
- if (!$?) { exit(1) }
- & git-sdk-64-minimal\usr\bin\bash.exe -lc @"
- mkdir -p artifacts &&
- eval \"`$(make -n artifacts-tar INCLUDE_DLLS_IN_ARTIFACTS=YesPlease ARTIFACTS_DIRECTORY=artifacts | grep ^tar)\"
- "@
- if (!$?) { exit(1) }
- displayName: Bundle artifact tar
- env:
- HOME: $(Build.SourcesDirectory)
- MSYSTEM: MINGW64
- DEVELOPER: 1
- NO_PERL: 1
- MSVC: 1
- VCPKG_ROOT: $(Build.SourcesDirectory)\compat\vcbuild\vcpkg
- - powershell: |
- $tag = (Invoke-WebRequest -UseBasicParsing "https://gitforwindows.org/latest-tag.txt").content
- $version = (Invoke-WebRequest -UseBasicParsing "https://gitforwindows.org/latest-version.txt").content
- $url = "https://github.com/git-for-windows/git/releases/download/${tag}/PortableGit-${version}-64-bit.7z.exe"
- (New-Object Net.WebClient).DownloadFile($url,"PortableGit.exe")
- & .\PortableGit.exe -y -oartifacts\PortableGit
- # Wait until it is unpacked
- while (-not @(Remove-Item -ErrorAction SilentlyContinue PortableGit.exe; $?)) { sleep 1 }
- displayName: Download & extract portable Git
- - task: PublishPipelineArtifact@0
- displayName: 'Publish Pipeline Artifact: MSVC test artifacts'
- inputs:
- artifactName: 'vs-artifacts'
- targetPath: '$(Build.SourcesDirectory)\artifacts'
- - powershell: |
- if ("$GITFILESHAREPWD" -ne "" -and "$GITFILESHAREPWD" -ne "`$`(gitfileshare.pwd)") {
- cmd /c rmdir "$(Build.SourcesDirectory)\test-cache"
- }
- displayName: 'Unmount test-cache'
- condition: true
- env:
- GITFILESHAREPWD: $(gitfileshare.pwd)
-
-- job: vs_test
- displayName: Visual Studio Test
- dependsOn: vs_build
- condition: succeeded()
- pool:
- vmImage: windows-latest
- timeoutInMinutes: 240
- strategy:
- parallel: 10
- steps:
- - powershell: |
- if ("$GITFILESHAREPWD" -ne "" -and "$GITFILESHAREPWD" -ne "`$`(gitfileshare.pwd)") {
- net use s: \\gitfileshare.file.core.windows.net\test-cache "$GITFILESHAREPWD" /user:AZURE\gitfileshare /persistent:no
- cmd /c mklink /d "$(Build.SourcesDirectory)\test-cache" S:\
- }
- displayName: 'Mount test-cache'
- env:
- GITFILESHAREPWD: $(gitfileshare.pwd)
- - task: DownloadPipelineArtifact@0
- displayName: 'Download Pipeline Artifact: VS test artifacts'
- inputs:
- artifactName: 'vs-artifacts'
- targetPath: '$(Build.SourcesDirectory)'
- - powershell: |
- & PortableGit\git-cmd.exe --command=usr\bin\bash.exe -lc @"
- test -f artifacts.tar.gz || {
- echo No test artifacts found\; skipping >&2
- exit 0
- }
- tar xf artifacts.tar.gz || exit 1
-
- # Let Git ignore the SDK and the test-cache
- printf '%s\n' /PortableGit/ /test-cache/ >>.git/info/exclude
-
- cd t &&
- PATH=\"`$PWD/helper:`$PATH\" &&
- test-tool.exe run-command testsuite --jobs=10 -V -x --write-junit-xml \
- `$(test-tool.exe path-utils slice-tests \
- `$SYSTEM_JOBPOSITIONINPHASE `$SYSTEM_TOTALJOBSINPHASE t[0-9]*.sh)
- "@
- if (!$?) { exit(1) }
- displayName: 'Test (parallel)'
- env:
- HOME: $(Build.SourcesDirectory)
- MSYSTEM: MINGW64
- NO_SVN_TESTS: 1
- GIT_TEST_SKIP_REBASE_P: 1
- - powershell: |
- if ("$GITFILESHAREPWD" -ne "" -and "$GITFILESHAREPWD" -ne "`$`(gitfileshare.pwd)") {
- cmd /c rmdir "$(Build.SourcesDirectory)\test-cache"
- }
- displayName: 'Unmount test-cache'
- condition: true
- env:
- GITFILESHAREPWD: $(gitfileshare.pwd)
- - task: PublishTestResults@2
- displayName: 'Publish Test Results **/TEST-*.xml'
- inputs:
- mergeTestResults: true
- testRunTitle: 'vs'
- platform: Windows
- publishRunAttachments: false
- condition: succeededOrFailed()
- - task: PublishBuildArtifacts@1
- displayName: 'Publish trash directories of failed tests'
- condition: failed()
- inputs:
- PathtoPublish: t/failed-test-artifacts
- ArtifactName: failed-vs-test-artifacts
-
-- job: linux_clang
- displayName: linux-clang
- condition: succeeded()
- pool:
- vmImage: ubuntu-latest
- steps:
- - bash: |
- test "$GITFILESHAREPWD" = '$(gitfileshare.pwd)' || ci/mount-fileshare.sh //gitfileshare.file.core.windows.net/test-cache gitfileshare "$GITFILESHAREPWD" "$HOME/test-cache" || exit 1
-
- sudo apt-get update &&
- sudo apt-get -y install git gcc make libssl-dev libcurl4-openssl-dev libexpat-dev tcl tk gettext git-email zlib1g-dev apache2-bin &&
-
- export CC=clang || exit 1
-
- ci/install-dependencies.sh || exit 1
- ci/run-build-and-tests.sh || {
- ci/print-test-failures.sh
- exit 1
- }
-
- test "$GITFILESHAREPWD" = '$(gitfileshare.pwd)' || sudo umount "$HOME/test-cache" || exit 1
- displayName: 'ci/run-build-and-tests.sh'
- env:
- GITFILESHAREPWD: $(gitfileshare.pwd)
- - task: PublishTestResults@2
- displayName: 'Publish Test Results **/TEST-*.xml'
- inputs:
- mergeTestResults: true
- testRunTitle: 'linux-clang'
- platform: Linux
- publishRunAttachments: false
- condition: succeededOrFailed()
- - task: PublishBuildArtifacts@1
- displayName: 'Publish trash directories of failed tests'
- condition: failed()
- inputs:
- PathtoPublish: t/failed-test-artifacts
- ArtifactName: failed-test-artifacts
-
-- job: linux_gcc
- displayName: linux-gcc
- condition: succeeded()
- pool:
- vmImage: ubuntu-latest
- steps:
- - bash: |
- test "$GITFILESHAREPWD" = '$(gitfileshare.pwd)' || ci/mount-fileshare.sh //gitfileshare.file.core.windows.net/test-cache gitfileshare "$GITFILESHAREPWD" "$HOME/test-cache" || exit 1
-
- sudo add-apt-repository ppa:ubuntu-toolchain-r/test &&
- sudo apt-get update &&
- sudo apt-get -y install git gcc make libssl-dev libcurl4-openssl-dev libexpat-dev tcl tk gettext git-email zlib1g-dev apache2 language-pack-is git-svn gcc-8 || exit 1
-
- ci/install-dependencies.sh || exit 1
- ci/run-build-and-tests.sh || {
- ci/print-test-failures.sh
- exit 1
- }
-
- test "$GITFILESHAREPWD" = '$(gitfileshare.pwd)' || sudo umount "$HOME/test-cache" || exit 1
- displayName: 'ci/run-build-and-tests.sh'
- env:
- GITFILESHAREPWD: $(gitfileshare.pwd)
- - task: PublishTestResults@2
- displayName: 'Publish Test Results **/TEST-*.xml'
- inputs:
- mergeTestResults: true
- testRunTitle: 'linux-gcc'
- platform: Linux
- publishRunAttachments: false
- condition: succeededOrFailed()
- - task: PublishBuildArtifacts@1
- displayName: 'Publish trash directories of failed tests'
- condition: failed()
- inputs:
- PathtoPublish: t/failed-test-artifacts
- ArtifactName: failed-test-artifacts
-
-- job: osx_clang
- displayName: osx-clang
- condition: succeeded()
- pool:
- vmImage: macOS-latest
- steps:
- - bash: |
- test "$GITFILESHAREPWD" = '$(gitfileshare.pwd)' || ci/mount-fileshare.sh //gitfileshare.file.core.windows.net/test-cache gitfileshare "$GITFILESHAREPWD" "$HOME/test-cache" || exit 1
-
- export CC=clang
-
- ci/install-dependencies.sh || exit 1
- ci/run-build-and-tests.sh || {
- ci/print-test-failures.sh
- exit 1
- }
-
- test "$GITFILESHAREPWD" = '$(gitfileshare.pwd)' || umount "$HOME/test-cache" || exit 1
- displayName: 'ci/run-build-and-tests.sh'
- env:
- GITFILESHAREPWD: $(gitfileshare.pwd)
- - task: PublishTestResults@2
- displayName: 'Publish Test Results **/TEST-*.xml'
- inputs:
- mergeTestResults: true
- testRunTitle: 'osx-clang'
- platform: macOS
- publishRunAttachments: false
- condition: succeededOrFailed()
- - task: PublishBuildArtifacts@1
- displayName: 'Publish trash directories of failed tests'
- condition: failed()
- inputs:
- PathtoPublish: t/failed-test-artifacts
- ArtifactName: failed-test-artifacts
-
-- job: osx_gcc
- displayName: osx-gcc
- condition: succeeded()
- pool:
- vmImage: macOS-latest
- steps:
- - bash: |
- test "$GITFILESHAREPWD" = '$(gitfileshare.pwd)' || ci/mount-fileshare.sh //gitfileshare.file.core.windows.net/test-cache gitfileshare "$GITFILESHAREPWD" "$HOME/test-cache" || exit 1
-
- ci/install-dependencies.sh || exit 1
- ci/run-build-and-tests.sh || {
- ci/print-test-failures.sh
- exit 1
- }
-
- test "$GITFILESHAREPWD" = '$(gitfileshare.pwd)' || umount "$HOME/test-cache" || exit 1
- displayName: 'ci/run-build-and-tests.sh'
- env:
- GITFILESHAREPWD: $(gitfileshare.pwd)
- - task: PublishTestResults@2
- displayName: 'Publish Test Results **/TEST-*.xml'
- inputs:
- mergeTestResults: true
- testRunTitle: 'osx-gcc'
- platform: macOS
- publishRunAttachments: false
- condition: succeededOrFailed()
- - task: PublishBuildArtifacts@1
- displayName: 'Publish trash directories of failed tests'
- condition: failed()
- inputs:
- PathtoPublish: t/failed-test-artifacts
- ArtifactName: failed-test-artifacts
-
-- job: gettext_poison
- displayName: GETTEXT_POISON
- condition: succeeded()
- pool:
- vmImage: ubuntu-latest
- steps:
- - bash: |
- test "$GITFILESHAREPWD" = '$(gitfileshare.pwd)' || ci/mount-fileshare.sh //gitfileshare.file.core.windows.net/test-cache gitfileshare "$GITFILESHAREPWD" "$HOME/test-cache" || exit 1
-
- sudo apt-get update &&
- sudo apt-get -y install git gcc make libssl-dev libcurl4-openssl-dev libexpat-dev tcl tk gettext git-email zlib1g-dev &&
-
- export jobname=GETTEXT_POISON || exit 1
-
- ci/run-build-and-tests.sh || {
- ci/print-test-failures.sh
- exit 1
- }
-
- test "$GITFILESHAREPWD" = '$(gitfileshare.pwd)' || sudo umount "$HOME/test-cache" || exit 1
- displayName: 'ci/run-build-and-tests.sh'
- env:
- GITFILESHAREPWD: $(gitfileshare.pwd)
- - task: PublishTestResults@2
- displayName: 'Publish Test Results **/TEST-*.xml'
- inputs:
- mergeTestResults: true
- testRunTitle: 'gettext-poison'
- platform: Linux
- publishRunAttachments: false
- condition: succeededOrFailed()
- - task: PublishBuildArtifacts@1
- displayName: 'Publish trash directories of failed tests'
- condition: failed()
- inputs:
- PathtoPublish: t/failed-test-artifacts
- ArtifactName: failed-test-artifacts
-
-- job: linux32
- displayName: Linux32
- condition: succeeded()
- pool:
- vmImage: ubuntu-latest
- steps:
- - bash: |
- test "$GITFILESHAREPWD" = '$(gitfileshare.pwd)' || ci/mount-fileshare.sh //gitfileshare.file.core.windows.net/test-cache gitfileshare "$GITFILESHAREPWD" "$HOME/test-cache" || exit 1
-
- res=0
- sudo AGENT_OS="$AGENT_OS" BUILD_BUILDNUMBER="$BUILD_BUILDNUMBER" BUILD_REPOSITORY_URI="$BUILD_REPOSITORY_URI" BUILD_SOURCEBRANCH="$BUILD_SOURCEBRANCH" BUILD_SOURCEVERSION="$BUILD_SOURCEVERSION" SYSTEM_PHASENAME="$SYSTEM_PHASENAME" SYSTEM_TASKDEFINITIONSURI="$SYSTEM_TASKDEFINITIONSURI" SYSTEM_TEAMPROJECT="$SYSTEM_TEAMPROJECT" CC=$CC MAKEFLAGS="$MAKEFLAGS" bash -lxc ci/run-linux32-docker.sh || res=1
-
- sudo chmod a+r t/out/TEST-*.xml
- test ! -d t/failed-test-artifacts || sudo chmod a+r t/failed-test-artifacts
-
- test "$GITFILESHAREPWD" = '$(gitfileshare.pwd)' || sudo umount "$HOME/test-cache" || res=1
- exit $res
- displayName: 'ci/run-linux32-docker.sh'
- env:
- GITFILESHAREPWD: $(gitfileshare.pwd)
- - task: PublishTestResults@2
- displayName: 'Publish Test Results **/TEST-*.xml'
- inputs:
- mergeTestResults: true
- testRunTitle: 'linux32'
- platform: Linux
- publishRunAttachments: false
- condition: succeededOrFailed()
- - task: PublishBuildArtifacts@1
- displayName: 'Publish trash directories of failed tests'
- condition: failed()
- inputs:
- PathtoPublish: t/failed-test-artifacts
- ArtifactName: failed-test-artifacts
-
-- job: static_analysis
- displayName: StaticAnalysis
- condition: succeeded()
- pool:
- vmImage: ubuntu-latest
- steps:
- - bash: |
- test "$GITFILESHAREPWD" = '$(gitfileshare.pwd)' || ci/mount-fileshare.sh //gitfileshare.file.core.windows.net/test-cache gitfileshare "$GITFILESHAREPWD" "$HOME/test-cache" || exit 1
-
- sudo apt-get update &&
- sudo apt-get install -y coccinelle libcurl4-openssl-dev libssl-dev libexpat-dev gettext &&
-
- export jobname=StaticAnalysis &&
-
- ci/run-static-analysis.sh || exit 1
-
- test "$GITFILESHAREPWD" = '$(gitfileshare.pwd)' || sudo umount "$HOME/test-cache" || exit 1
- displayName: 'ci/run-static-analysis.sh'
- env:
- GITFILESHAREPWD: $(gitfileshare.pwd)
-
-- job: documentation
- displayName: Documentation
- condition: succeeded()
- pool:
- vmImage: ubuntu-latest
- steps:
- - bash: |
- test "$GITFILESHAREPWD" = '$(gitfileshare.pwd)' || ci/mount-fileshare.sh //gitfileshare.file.core.windows.net/test-cache gitfileshare "$GITFILESHAREPWD" "$HOME/test-cache" || exit 1
-
- sudo apt-get update &&
- sudo apt-get install -y asciidoc xmlto asciidoctor docbook-xsl-ns &&
-
- export ALREADY_HAVE_ASCIIDOCTOR=yes. &&
- export jobname=Documentation &&
-
- ci/test-documentation.sh || exit 1
-
- test "$GITFILESHAREPWD" = '$(gitfileshare.pwd)' || sudo umount "$HOME/test-cache" || exit 1
- displayName: 'ci/test-documentation.sh'
- env:
- GITFILESHAREPWD: $(gitfileshare.pwd)
diff --git a/bisect.c b/bisect.c
index 9154f81..d5e8304 100644
--- a/bisect.c
+++ b/bisect.c
@@ -10,7 +10,7 @@
#include "run-command.h"
#include "log-tree.h"
#include "bisect.h"
-#include "sha1-array.h"
+#include "oid-array.h"
#include "argv-array.h"
#include "commit-slab.h"
#include "commit-reach.h"
@@ -473,7 +473,7 @@
fclose(fp);
}
-static char *join_sha1_array_hex(struct oid_array *array, char delim)
+static char *join_oid_array_hex(struct oid_array *array, char delim)
{
struct strbuf joined_hexs = STRBUF_INIT;
int i;
@@ -765,7 +765,7 @@
{
if (is_expected_rev(current_bad_oid)) {
char *bad_hex = oid_to_hex(current_bad_oid);
- char *good_hex = join_sha1_array_hex(&good_revs, ' ');
+ char *good_hex = join_oid_array_hex(&good_revs, ' ');
if (!strcmp(term_bad, "bad") && !strcmp(term_good, "good")) {
fprintf(stderr, _("The merge base %s is bad.\n"
"This means the bug has been fixed "
@@ -796,7 +796,7 @@
{
char *mb_hex = oid_to_hex(mb);
char *bad_hex = oid_to_hex(current_bad_oid);
- char *good_hex = join_sha1_array_hex(&good_revs, ' ');
+ char *good_hex = join_oid_array_hex(&good_revs, ' ');
warning(_("the merge base between %s and [%s] "
"must be skipped.\n"
diff --git a/blame.c b/blame.c
index 29770e5..da7e288 100644
--- a/blame.c
+++ b/blame.c
@@ -9,6 +9,8 @@
#include "blame.h"
#include "alloc.h"
#include "commit-slab.h"
+#include "bloom.h"
+#include "commit-graph.h"
define_commit_slab(blame_suspects, struct blame_origin *);
static struct blame_suspects blame_suspects;
@@ -1246,13 +1248,74 @@
return -1;
}
+struct blame_bloom_data {
+ /*
+ * Changed-path Bloom filter keys. These can help prevent
+ * computing diffs against first parents, but we need to
+ * expand the list as code is moved or files are renamed.
+ */
+ struct bloom_filter_settings *settings;
+ struct bloom_key **keys;
+ int nr;
+ int alloc;
+};
+
+static int bloom_count_queries = 0;
+static int bloom_count_no = 0;
+static int maybe_changed_path(struct repository *r,
+ struct blame_origin *origin,
+ struct blame_bloom_data *bd)
+{
+ int i;
+ struct bloom_filter *filter;
+
+ if (!bd)
+ return 1;
+
+ if (origin->commit->generation == GENERATION_NUMBER_INFINITY)
+ return 1;
+
+ filter = get_bloom_filter(r, origin->commit, 0);
+
+ if (!filter)
+ return 1;
+
+ bloom_count_queries++;
+ for (i = 0; i < bd->nr; i++) {
+ if (bloom_filter_contains(filter,
+ bd->keys[i],
+ bd->settings))
+ return 1;
+ }
+
+ bloom_count_no++;
+ return 0;
+}
+
+static void add_bloom_key(struct blame_bloom_data *bd,
+ const char *path)
+{
+ if (!bd)
+ return;
+
+ if (bd->nr >= bd->alloc) {
+ bd->alloc *= 2;
+ REALLOC_ARRAY(bd->keys, bd->alloc);
+ }
+
+ bd->keys[bd->nr] = xmalloc(sizeof(struct bloom_key));
+ fill_bloom_key(path, strlen(path), bd->keys[bd->nr], bd->settings);
+ bd->nr++;
+}
+
/*
* We have an origin -- check if the same path exists in the
* parent and return an origin structure to represent it.
*/
static struct blame_origin *find_origin(struct repository *r,
struct commit *parent,
- struct blame_origin *origin)
+ struct blame_origin *origin,
+ struct blame_bloom_data *bd)
{
struct blame_origin *porigin;
struct diff_options diff_opts;
@@ -1286,10 +1349,18 @@
if (is_null_oid(&origin->commit->object.oid))
do_diff_cache(get_commit_tree_oid(parent), &diff_opts);
- else
- diff_tree_oid(get_commit_tree_oid(parent),
- get_commit_tree_oid(origin->commit),
- "", &diff_opts);
+ else {
+ int compute_diff = 1;
+ if (origin->commit->parents &&
+ !oidcmp(&parent->object.oid,
+ &origin->commit->parents->item->object.oid))
+ compute_diff = maybe_changed_path(r, origin, bd);
+
+ if (compute_diff)
+ diff_tree_oid(get_commit_tree_oid(parent),
+ get_commit_tree_oid(origin->commit),
+ "", &diff_opts);
+ }
diffcore_std(&diff_opts);
if (!diff_queued_diff.nr) {
@@ -1341,7 +1412,8 @@
*/
static struct blame_origin *find_rename(struct repository *r,
struct commit *parent,
- struct blame_origin *origin)
+ struct blame_origin *origin,
+ struct blame_bloom_data *bd)
{
struct blame_origin *porigin = NULL;
struct diff_options diff_opts;
@@ -1366,6 +1438,7 @@
struct diff_filepair *p = diff_queued_diff.queue[i];
if ((p->status == 'R' || p->status == 'C') &&
!strcmp(p->two->path, origin->path)) {
+ add_bloom_key(bd, p->one->path);
porigin = get_origin(parent, p->one->path);
oidcpy(&porigin->blob_oid, &p->one->oid);
porigin->mode = p->one->mode;
@@ -2332,6 +2405,11 @@
#define MAXSG 16
+typedef struct blame_origin *(*blame_find_alg)(struct repository *,
+ struct commit *,
+ struct blame_origin *,
+ struct blame_bloom_data *);
+
static void pass_blame(struct blame_scoreboard *sb, struct blame_origin *origin, int opt)
{
struct rev_info *revs = sb->revs;
@@ -2356,8 +2434,7 @@
* common cases, then we look for renames in the second pass.
*/
for (pass = 0; pass < 2 - sb->no_whole_file_rename; pass++) {
- struct blame_origin *(*find)(struct repository *, struct commit *, struct blame_origin *);
- find = pass ? find_rename : find_origin;
+ blame_find_alg find = pass ? find_rename : find_origin;
for (i = 0, sg = first_scapegoat(revs, commit, sb->reverse);
i < num_sg && sg;
@@ -2369,7 +2446,7 @@
continue;
if (parse_commit(p))
continue;
- porigin = find(sb->repo, p, origin);
+ porigin = find(sb->repo, p, origin, sb->bloom_data);
if (!porigin)
continue;
if (oideq(&porigin->blob_oid, &origin->blob_oid)) {
@@ -2809,3 +2886,45 @@
blame_origin_incref(o);
return new_head;
}
+
+void setup_blame_bloom_data(struct blame_scoreboard *sb,
+ const char *path)
+{
+ struct blame_bloom_data *bd;
+
+ if (!sb->repo->objects->commit_graph)
+ return;
+
+ if (!sb->repo->objects->commit_graph->bloom_filter_settings)
+ return;
+
+ bd = xmalloc(sizeof(struct blame_bloom_data));
+
+ bd->settings = sb->repo->objects->commit_graph->bloom_filter_settings;
+
+ bd->alloc = 4;
+ bd->nr = 0;
+ ALLOC_ARRAY(bd->keys, bd->alloc);
+
+ add_bloom_key(bd, path);
+
+ sb->bloom_data = bd;
+}
+
+void cleanup_scoreboard(struct blame_scoreboard *sb)
+{
+ if (sb->bloom_data) {
+ int i;
+ for (i = 0; i < sb->bloom_data->nr; i++) {
+ free(sb->bloom_data->keys[i]->hashes);
+ free(sb->bloom_data->keys[i]);
+ }
+ free(sb->bloom_data->keys);
+ FREE_AND_NULL(sb->bloom_data);
+
+ trace2_data_intmax("blame", sb->repo,
+ "bloom/queries", bloom_count_queries);
+ trace2_data_intmax("blame", sb->repo,
+ "bloom/response-no", bloom_count_no);
+ }
+}
diff --git a/blame.h b/blame.h
index 089b181..b6bbee4 100644
--- a/blame.h
+++ b/blame.h
@@ -100,6 +100,8 @@
int unblamable;
};
+struct blame_bloom_data;
+
/*
* The current state of the blame assignment.
*/
@@ -156,6 +158,7 @@
void(*found_guilty_entry)(struct blame_entry *, void *);
void *found_guilty_entry_data;
+ struct blame_bloom_data *bloom_data;
};
/*
@@ -180,6 +183,9 @@
void setup_scoreboard(struct blame_scoreboard *sb,
const char *path,
struct blame_origin **orig);
+void setup_blame_bloom_data(struct blame_scoreboard *sb,
+ const char *path);
+void cleanup_scoreboard(struct blame_scoreboard *sb);
struct blame_entry *blame_entry_prepend(struct blame_entry *head,
long start, long end,
diff --git a/bloom.c b/bloom.c
new file mode 100644
index 0000000..9b86aa3
--- /dev/null
+++ b/bloom.c
@@ -0,0 +1,296 @@
+#include "git-compat-util.h"
+#include "bloom.h"
+#include "diff.h"
+#include "diffcore.h"
+#include "revision.h"
+#include "hashmap.h"
+#include "commit-graph.h"
+#include "commit.h"
+
+define_commit_slab(bloom_filter_slab, struct bloom_filter);
+
+static struct bloom_filter_slab bloom_filters;
+
+struct pathmap_hash_entry {
+ struct hashmap_entry entry;
+ const char path[FLEX_ARRAY];
+};
+
+static uint32_t rotate_left(uint32_t value, int32_t count)
+{
+ uint32_t mask = 8 * sizeof(uint32_t) - 1;
+ count &= mask;
+ return ((value << count) | (value >> ((-count) & mask)));
+}
+
+static inline unsigned char get_bitmask(uint32_t pos)
+{
+ return ((unsigned char)1) << (pos & (BITS_PER_WORD - 1));
+}
+
+static int load_bloom_filter_from_graph(struct commit_graph *g,
+ struct bloom_filter *filter,
+ struct commit *c)
+{
+ uint32_t lex_pos, start_index, end_index;
+
+ while (c->graph_pos < g->num_commits_in_base)
+ g = g->base_graph;
+
+ /* The commit graph commit 'c' lives in doesn't carry bloom filters. */
+ if (!g->chunk_bloom_indexes)
+ return 0;
+
+ lex_pos = c->graph_pos - g->num_commits_in_base;
+
+ end_index = get_be32(g->chunk_bloom_indexes + 4 * lex_pos);
+
+ if (lex_pos > 0)
+ start_index = get_be32(g->chunk_bloom_indexes + 4 * (lex_pos - 1));
+ else
+ start_index = 0;
+
+ filter->len = end_index - start_index;
+ filter->data = (unsigned char *)(g->chunk_bloom_data +
+ sizeof(unsigned char) * start_index +
+ BLOOMDATA_CHUNK_HEADER_SIZE);
+
+ return 1;
+}
+
+/*
+ * Calculate the murmur3 32-bit hash value for the given data
+ * using the given seed.
+ * Produces a uniformly distributed hash value.
+ * Not considered to be cryptographically secure.
+ * Implemented as described in https://en.wikipedia.org/wiki/MurmurHash#Algorithm
+ */
+uint32_t murmur3_seeded(uint32_t seed, const char *data, size_t len)
+{
+ const uint32_t c1 = 0xcc9e2d51;
+ const uint32_t c2 = 0x1b873593;
+ const uint32_t r1 = 15;
+ const uint32_t r2 = 13;
+ const uint32_t m = 5;
+ const uint32_t n = 0xe6546b64;
+ int i;
+ uint32_t k1 = 0;
+ const char *tail;
+
+ int len4 = len / sizeof(uint32_t);
+
+ uint32_t k;
+ for (i = 0; i < len4; i++) {
+ uint32_t byte1 = (uint32_t)data[4*i];
+ uint32_t byte2 = ((uint32_t)data[4*i + 1]) << 8;
+ uint32_t byte3 = ((uint32_t)data[4*i + 2]) << 16;
+ uint32_t byte4 = ((uint32_t)data[4*i + 3]) << 24;
+ k = byte1 | byte2 | byte3 | byte4;
+ k *= c1;
+ k = rotate_left(k, r1);
+ k *= c2;
+
+ seed ^= k;
+ seed = rotate_left(seed, r2) * m + n;
+ }
+
+ tail = (data + len4 * sizeof(uint32_t));
+
+ switch (len & (sizeof(uint32_t) - 1)) {
+ case 3:
+ k1 ^= ((uint32_t)tail[2]) << 16;
+ /*-fallthrough*/
+ case 2:
+ k1 ^= ((uint32_t)tail[1]) << 8;
+ /*-fallthrough*/
+ case 1:
+ k1 ^= ((uint32_t)tail[0]) << 0;
+ k1 *= c1;
+ k1 = rotate_left(k1, r1);
+ k1 *= c2;
+ seed ^= k1;
+ break;
+ }
+
+ seed ^= (uint32_t)len;
+ seed ^= (seed >> 16);
+ seed *= 0x85ebca6b;
+ seed ^= (seed >> 13);
+ seed *= 0xc2b2ae35;
+ seed ^= (seed >> 16);
+
+ return seed;
+}
+
+void fill_bloom_key(const char *data,
+ size_t len,
+ struct bloom_key *key,
+ const struct bloom_filter_settings *settings)
+{
+ int i;
+ const uint32_t seed0 = 0x293ae76f;
+ const uint32_t seed1 = 0x7e646e2c;
+ const uint32_t hash0 = murmur3_seeded(seed0, data, len);
+ const uint32_t hash1 = murmur3_seeded(seed1, data, len);
+
+ key->hashes = (uint32_t *)xcalloc(settings->num_hashes, sizeof(uint32_t));
+ for (i = 0; i < settings->num_hashes; i++)
+ key->hashes[i] = hash0 + i * hash1;
+}
+
+void add_key_to_filter(const struct bloom_key *key,
+ struct bloom_filter *filter,
+ const struct bloom_filter_settings *settings)
+{
+ int i;
+ uint64_t mod = filter->len * BITS_PER_WORD;
+
+ for (i = 0; i < settings->num_hashes; i++) {
+ uint64_t hash_mod = key->hashes[i] % mod;
+ uint64_t block_pos = hash_mod / BITS_PER_WORD;
+
+ filter->data[block_pos] |= get_bitmask(hash_mod);
+ }
+}
+
+void init_bloom_filters(void)
+{
+ init_bloom_filter_slab(&bloom_filters);
+}
+
+static int pathmap_cmp(const void *hashmap_cmp_fn_data,
+ const struct hashmap_entry *eptr,
+ const struct hashmap_entry *entry_or_key,
+ const void *keydata)
+{
+ const struct pathmap_hash_entry *e1, *e2;
+
+ e1 = container_of(eptr, const struct pathmap_hash_entry, entry);
+ e2 = container_of(entry_or_key, const struct pathmap_hash_entry, entry);
+
+ return strcmp(e1->path, e2->path);
+}
+
+struct bloom_filter *get_bloom_filter(struct repository *r,
+ struct commit *c,
+ int compute_if_not_present)
+{
+ struct bloom_filter *filter;
+ struct bloom_filter_settings settings = DEFAULT_BLOOM_FILTER_SETTINGS;
+ int i;
+ struct diff_options diffopt;
+ int max_changes = 512;
+
+ if (bloom_filters.slab_size == 0)
+ return NULL;
+
+ filter = bloom_filter_slab_at(&bloom_filters, c);
+
+ if (!filter->data) {
+ load_commit_graph_info(r, c);
+ if (c->graph_pos != COMMIT_NOT_FROM_GRAPH &&
+ r->objects->commit_graph->chunk_bloom_indexes) {
+ if (load_bloom_filter_from_graph(r->objects->commit_graph, filter, c))
+ return filter;
+ else
+ return NULL;
+ }
+ }
+
+ if (filter->data || !compute_if_not_present)
+ return filter;
+
+ repo_diff_setup(r, &diffopt);
+ diffopt.flags.recursive = 1;
+ diffopt.detect_rename = 0;
+ diffopt.max_changes = max_changes;
+ diff_setup_done(&diffopt);
+
+ /* ensure commit is parsed so we have parent information */
+ repo_parse_commit(r, c);
+
+ if (c->parents)
+ diff_tree_oid(&c->parents->item->object.oid, &c->object.oid, "", &diffopt);
+ else
+ diff_tree_oid(NULL, &c->object.oid, "", &diffopt);
+ diffcore_std(&diffopt);
+
+ if (diffopt.num_changes <= max_changes) {
+ struct hashmap pathmap;
+ struct pathmap_hash_entry *e;
+ struct hashmap_iter iter;
+ hashmap_init(&pathmap, pathmap_cmp, NULL, 0);
+
+ for (i = 0; i < diff_queued_diff.nr; i++) {
+ const char *path = diff_queued_diff.queue[i]->two->path;
+
+ /*
+ * Add each leading directory of the changed file, i.e. for
+ * 'dir/subdir/file' add 'dir' and 'dir/subdir' as well, so
+ * the Bloom filter could be used to speed up commands like
+ * 'git log dir/subdir', too.
+ *
+ * Note that directories are added without the trailing '/'.
+ */
+ do {
+ char *last_slash = strrchr(path, '/');
+
+ FLEX_ALLOC_STR(e, path, path);
+ hashmap_entry_init(&e->entry, strhash(path));
+
+ if (!hashmap_get(&pathmap, &e->entry, NULL))
+ hashmap_add(&pathmap, &e->entry);
+ else
+ free(e);
+
+ if (!last_slash)
+ last_slash = (char*)path;
+ *last_slash = '\0';
+
+ } while (*path);
+
+ diff_free_filepair(diff_queued_diff.queue[i]);
+ }
+
+ filter->len = (hashmap_get_size(&pathmap) * settings.bits_per_entry + BITS_PER_WORD - 1) / BITS_PER_WORD;
+ filter->data = xcalloc(filter->len, sizeof(unsigned char));
+
+ hashmap_for_each_entry(&pathmap, &iter, e, entry) {
+ struct bloom_key key;
+ fill_bloom_key(e->path, strlen(e->path), &key, &settings);
+ add_key_to_filter(&key, filter, &settings);
+ }
+
+ hashmap_free_entries(&pathmap, struct pathmap_hash_entry, entry);
+ } else {
+ for (i = 0; i < diff_queued_diff.nr; i++)
+ diff_free_filepair(diff_queued_diff.queue[i]);
+ filter->data = NULL;
+ filter->len = 0;
+ }
+
+ free(diff_queued_diff.queue);
+ DIFF_QUEUE_CLEAR(&diff_queued_diff);
+
+ return filter;
+}
+
+int bloom_filter_contains(const struct bloom_filter *filter,
+ const struct bloom_key *key,
+ const struct bloom_filter_settings *settings)
+{
+ int i;
+ uint64_t mod = filter->len * BITS_PER_WORD;
+
+ if (!mod)
+ return -1;
+
+ for (i = 0; i < settings->num_hashes; i++) {
+ uint64_t hash_mod = key->hashes[i] % mod;
+ uint64_t block_pos = hash_mod / BITS_PER_WORD;
+ if (!(filter->data[block_pos] & get_bitmask(hash_mod)))
+ return 0;
+ }
+
+ return 1;
+}
diff --git a/bloom.h b/bloom.h
new file mode 100644
index 0000000..b2a8379
--- /dev/null
+++ b/bloom.h
@@ -0,0 +1,90 @@
+#ifndef BLOOM_H
+#define BLOOM_H
+
+struct commit;
+struct repository;
+
+struct bloom_filter_settings {
+ /*
+ * The version of the hashing technique being used.
+ * We currently only support version = 1 which is
+ * the seeded murmur3 hashing technique implemented
+ * in bloom.c.
+ */
+ uint32_t hash_version;
+
+ /*
+ * The number of times a path is hashed, i.e. the
+ * number of bit positions tht cumulatively
+ * determine whether a path is present in the
+ * Bloom filter.
+ */
+ uint32_t num_hashes;
+
+ /*
+ * The minimum number of bits per entry in the Bloom
+ * filter. If the filter contains 'n' entries, then
+ * filter size is the minimum number of 8-bit words
+ * that contain n*b bits.
+ */
+ uint32_t bits_per_entry;
+};
+
+#define DEFAULT_BLOOM_FILTER_SETTINGS { 1, 7, 10 }
+#define BITS_PER_WORD 8
+#define BLOOMDATA_CHUNK_HEADER_SIZE 3 * sizeof(uint32_t)
+
+/*
+ * A bloom_filter struct represents a data segment to
+ * use when testing hash values. The 'len' member
+ * dictates how many entries are stored in
+ * 'data'.
+ */
+struct bloom_filter {
+ unsigned char *data;
+ size_t len;
+};
+
+/*
+ * A bloom_key represents the k hash values for a
+ * given string. These can be precomputed and
+ * stored in a bloom_key for re-use when testing
+ * against a bloom_filter. The number of hashes is
+ * given by the Bloom filter settings and is the same
+ * for all Bloom filters and keys interacting with
+ * the loaded version of the commit graph file and
+ * the Bloom data chunks.
+ */
+struct bloom_key {
+ uint32_t *hashes;
+};
+
+/*
+ * Calculate the murmur3 32-bit hash value for the given data
+ * using the given seed.
+ * Produces a uniformly distributed hash value.
+ * Not considered to be cryptographically secure.
+ * Implemented as described in https://en.wikipedia.org/wiki/MurmurHash#Algorithm
+ */
+uint32_t murmur3_seeded(uint32_t seed, const char *data, size_t len);
+
+void fill_bloom_key(const char *data,
+ size_t len,
+ struct bloom_key *key,
+ const struct bloom_filter_settings *settings);
+
+void add_key_to_filter(const struct bloom_key *key,
+ struct bloom_filter *filter,
+ const struct bloom_filter_settings *settings);
+
+void init_bloom_filters(void);
+
+struct bloom_filter *get_bloom_filter(struct repository *r,
+ struct commit *c,
+ int compute_if_not_present);
+
+int bloom_filter_contains(const struct bloom_filter *filter,
+ const struct bloom_key *key,
+ const struct bloom_filter_settings *settings);
+
+#endif
diff --git a/branch.c b/branch.c
index 5794947..2d9e767 100644
--- a/branch.c
+++ b/branch.c
@@ -344,6 +344,7 @@
unlink(git_path_merge_rr(r));
unlink(git_path_merge_msg(r));
unlink(git_path_merge_mode(r));
+ save_autostash(git_path_merge_autostash(r));
}
void remove_branch_state(struct repository *r, int verbose)
diff --git a/bugreport.c b/bugreport.c
new file mode 100644
index 0000000..aa8a489
--- /dev/null
+++ b/bugreport.c
@@ -0,0 +1,192 @@
+#include "cache.h"
+#include "parse-options.h"
+#include "strbuf.h"
+#include "help.h"
+#include "compat/compiler.h"
+#include "run-command.h"
+
+
+static void get_system_info(struct strbuf *sys_info)
+{
+ struct utsname uname_info;
+
+ /* get git version from native cmd */
+ strbuf_addstr(sys_info, _("git version:\n"));
+ get_version_info(sys_info, 1);
+
+ /* system call for other version info */
+ strbuf_addstr(sys_info, "uname: ");
+ if (uname(&uname_info))
+ strbuf_addf(sys_info, _("uname() failed with error '%s' (%d)\n"),
+ strerror(errno),
+ errno);
+ else
+ strbuf_addf(sys_info, "%s %s %s %s\n",
+ uname_info.sysname,
+ uname_info.release,
+ uname_info.version,
+ uname_info.machine);
+
+ strbuf_addstr(sys_info, _("compiler info: "));
+ get_compiler_info(sys_info);
+ strbuf_addstr(sys_info, _("libc info: "));
+ get_libc_info(sys_info);
+}
+
+static void get_populated_hooks(struct strbuf *hook_info, int nongit)
+{
+ /*
+ * NEEDSWORK: Doesn't look like there is a list of all possible hooks;
+ * so below is a transcription of `git help hooks`. Later, this should
+ * be replaced with some programmatically generated list (generated from
+ * doc or else taken from some library which tells us about all the
+ * hooks)
+ */
+ static const char *hook[] = {
+ "applypatch-msg",
+ "pre-applypatch",
+ "post-applypatch",
+ "pre-commit",
+ "pre-merge-commit",
+ "prepare-commit-msg",
+ "commit-msg",
+ "post-commit",
+ "pre-rebase",
+ "post-checkout",
+ "post-merge",
+ "pre-push",
+ "pre-receive",
+ "update",
+ "post-receive",
+ "post-update",
+ "push-to-checkout",
+ "pre-auto-gc",
+ "post-rewrite",
+ "sendemail-validate",
+ "fsmonitor-watchman",
+ "p4-pre-submit",
+ "post-index-change",
+ };
+ int i;
+
+ if (nongit) {
+ strbuf_addstr(hook_info,
+ _("not run from a git repository - no hooks to show\n"));
+ return;
+ }
+
+ for (i = 0; i < ARRAY_SIZE(hook); i++)
+ if (find_hook(hook[i]))
+ strbuf_addf(hook_info, "%s\n", hook[i]);
+}
+
+static const char * const bugreport_usage[] = {
+ N_("git bugreport [-o|--output-directory <file>] [-s|--suffix <format>]"),
+ NULL
+};
+
+static int get_bug_template(struct strbuf *template)
+{
+ const char template_text[] = N_(
+"Thank you for filling out a Git bug report!\n"
+"Please answer the following questions to help us understand your issue.\n"
+"\n"
+"What did you do before the bug happened? (Steps to reproduce your issue)\n"
+"\n"
+"What did you expect to happen? (Expected behavior)\n"
+"\n"
+"What happened instead? (Actual behavior)\n"
+"\n"
+"What's different between what you expected and what actually happened?\n"
+"\n"
+"Anything else you want to add:\n"
+"\n"
+"Please review the rest of the bug report below.\n"
+"You can delete any lines you don't wish to share.\n");
+
+ strbuf_addstr(template, _(template_text));
+ return 0;
+}
+
+static void get_header(struct strbuf *buf, const char *title)
+{
+ strbuf_addf(buf, "\n\n[%s]\n", title);
+}
+
+int cmd_main(int argc, const char **argv)
+{
+ struct strbuf buffer = STRBUF_INIT;
+ struct strbuf report_path = STRBUF_INIT;
+ int report = -1;
+ time_t now = time(NULL);
+ char *option_output = NULL;
+ char *option_suffix = "%Y-%m-%d-%H%M";
+ int nongit_ok = 0;
+ const char *prefix = NULL;
+ const char *user_relative_path = NULL;
+
+ const struct option bugreport_options[] = {
+ OPT_STRING('o', "output-directory", &option_output, N_("path"),
+ N_("specify a destination for the bugreport file")),
+ OPT_STRING('s', "suffix", &option_suffix, N_("format"),
+ N_("specify a strftime format suffix for the filename")),
+ OPT_END()
+ };
+
+ prefix = setup_git_directory_gently(&nongit_ok);
+
+ argc = parse_options(argc, argv, prefix, bugreport_options,
+ bugreport_usage, 0);
+
+ /* Prepare the path to put the result */
+ strbuf_addstr(&report_path,
+ prefix_filename(prefix,
+ option_output ? option_output : ""));
+ strbuf_complete(&report_path, '/');
+
+ strbuf_addstr(&report_path, "git-bugreport-");
+ strbuf_addftime(&report_path, option_suffix, localtime(&now), 0, 0);
+ strbuf_addstr(&report_path, ".txt");
+
+ switch (safe_create_leading_directories(report_path.buf)) {
+ case SCLD_OK:
+ case SCLD_EXISTS:
+ break;
+ default:
+ die(_("could not create leading directories for '%s'"),
+ report_path.buf);
+ }
+
+ /* Prepare the report contents */
+ get_bug_template(&buffer);
+
+ get_header(&buffer, _("System Info"));
+ get_system_info(&buffer);
+
+ get_header(&buffer, _("Enabled Hooks"));
+ get_populated_hooks(&buffer, nongit_ok);
+
+ /* fopen doesn't offer us an O_EXCL alternative, except with glibc. */
+ report = open(report_path.buf, O_CREAT | O_EXCL | O_WRONLY, 0666);
+
+ if (report < 0) {
+ UNLEAK(report_path);
+ die(_("couldn't create a new file at '%s'"), report_path.buf);
+ }
+
+ strbuf_write_fd(&buffer, report);
+ close(report);
+
+ /*
+ * We want to print the path relative to the user, but we still need the
+ * path relative to us to give to the editor.
+ */
+ if (!(prefix && skip_prefix(report_path.buf, prefix, &user_relative_path)))
+ user_relative_path = report_path.buf;
+ fprintf(stderr, _("Created new report at '%s'.\n"),
+ user_relative_path);
+
+ UNLEAK(buffer);
+ UNLEAK(report_path);
+ return !!launch_editor(report_path.buf, NULL, NULL);
+}
diff --git a/builtin.h b/builtin.h
index 2b25a80..a5ae15b 100644
--- a/builtin.h
+++ b/builtin.h
@@ -94,25 +94,9 @@
* command.
*/
-#define DEFAULT_MERGE_LOG_LEN 20
-
extern const char git_usage_string[];
extern const char git_more_info_string[];
-#define PRUNE_PACKED_DRY_RUN 01
-#define PRUNE_PACKED_VERBOSE 02
-
-void prune_packed_objects(int);
-
-struct fmt_merge_msg_opts {
- unsigned add_title:1,
- credit_people:1;
- int shortlog_len;
-};
-
-int fmt_merge_msg(struct strbuf *in, struct strbuf *out,
- struct fmt_merge_msg_opts *);
-
/**
* If a built-in has DELAY_PAGER_CONFIG set, the built-in should call this early
* when it wishes to respect the `pager.foo`-config. The `cmd` is the name of
diff --git a/builtin/add.c b/builtin/add.c
index 18a0881..298e011 100644
--- a/builtin/add.c
+++ b/builtin/add.c
@@ -330,10 +330,10 @@
OPT_BOOL(0, "renormalize", &add_renormalize, N_("renormalize EOL of tracked files (implies -u)")),
OPT_BOOL('N', "intent-to-add", &intent_to_add, N_("record only the fact that the path will be added later")),
OPT_BOOL('A', "all", &addremove_explicit, N_("add changes from all tracked and untracked files")),
- { OPTION_CALLBACK, 0, "ignore-removal", &addremove_explicit,
+ OPT_CALLBACK_F(0, "ignore-removal", &addremove_explicit,
NULL /* takes no arguments */,
N_("ignore paths removed in the working tree (same as --no-all)"),
- PARSE_OPT_NOARG, ignore_removal_cb },
+ PARSE_OPT_NOARG, ignore_removal_cb),
OPT_BOOL( 0 , "refresh", &refresh_only, N_("don't add, only refresh the index")),
OPT_BOOL( 0 , "ignore-errors", &ignore_add_errors, N_("just skip files which cannot be added because of errors")),
OPT_BOOL( 0 , "ignore-missing", &ignore_missing, N_("check if - even missing - files are ignored in dry run")),
diff --git a/builtin/am.c b/builtin/am.c
index e3dfd93..69e50de 100644
--- a/builtin/am.c
+++ b/builtin/am.c
@@ -1691,7 +1691,6 @@
*/
static void am_run(struct am_state *state, int resume)
{
- const char *argv_gc_auto[] = {"gc", "--auto", NULL};
struct strbuf sb = STRBUF_INIT;
unlink(am_path(state, "dirtyindex"));
@@ -1796,7 +1795,7 @@
if (!state->rebasing) {
am_destroy(state);
close_object_store(the_repository->objects);
- run_command_v_opt(argv_gc_auto, RUN_GIT_CMD);
+ run_auto_gc(state->quiet);
}
}
diff --git a/builtin/blame.c b/builtin/blame.c
index bf1cecd..94ef57c 100644
--- a/builtin/blame.c
+++ b/builtin/blame.c
@@ -864,8 +864,8 @@
OPT_BIT(0, "minimal", &xdl_opts, N_("Spend extra cycles to find better match"), XDF_NEED_MINIMAL),
OPT_STRING('S', NULL, &revs_file, N_("file"), N_("Use revisions from <file> instead of calling git-rev-list")),
OPT_STRING(0, "contents", &contents_from, N_("file"), N_("Use <file>'s contents as the final image")),
- { OPTION_CALLBACK, 'C', NULL, &opt, N_("score"), N_("Find line copies within and across files"), PARSE_OPT_OPTARG, blame_copy_callback },
- { OPTION_CALLBACK, 'M', NULL, &opt, N_("score"), N_("Find line movements within and across files"), PARSE_OPT_OPTARG, blame_move_callback },
+ OPT_CALLBACK_F('C', NULL, &opt, N_("score"), N_("Find line copies within and across files"), PARSE_OPT_OPTARG, blame_copy_callback),
+ OPT_CALLBACK_F('M', NULL, &opt, N_("score"), N_("Find line movements within and across files"), PARSE_OPT_OPTARG, blame_move_callback),
OPT_STRING_LIST('L', NULL, &range_list, N_("n,m"), N_("Process only line range n,m, counting from 1")),
OPT__ABBREV(&abbrev),
OPT_END()
@@ -1061,6 +1061,14 @@
string_list_clear(&ignore_revs_file_list, 0);
string_list_clear(&ignore_rev_list, 0);
setup_scoreboard(&sb, path, &o);
+
+ /*
+ * Changed-path Bloom filters are disabled when looking
+ * for copies.
+ */
+ if (!(opt & PICKAXE_BLAME_COPY))
+ setup_blame_bloom_data(&sb, path);
+
lno = sb.num_lines;
if (lno && !range_list.nr)
@@ -1164,5 +1172,7 @@
printf("num get patch: %d\n", sb.num_get_patch);
printf("num commits: %d\n", sb.num_commits);
}
+
+ cleanup_scoreboard(&sb);
return 0;
}
diff --git a/builtin/branch.c b/builtin/branch.c
index d8297f8..accb61b 100644
--- a/builtin/branch.c
+++ b/builtin/branch.c
@@ -653,10 +653,8 @@
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),
- {
- OPTION_CALLBACK, 0, "points-at", &filter.points_at, N_("object"),
- N_("print only branches of the object"), 0, parse_opt_object_name
- },
+ 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")),
OPT_STRING( 0 , "format", &format.format, N_("format"), N_("format to use for the output")),
OPT_END(),
@@ -739,7 +737,7 @@
*/
if (!sorting)
sorting = ref_default_sorting();
- sorting->ignore_case = icase;
+ ref_sorting_icase_all(sorting, icase);
print_ref_list(&filter, sorting, &format);
print_columns(&output, colopts, NULL);
string_list_clear(&output, 0);
diff --git a/builtin/cat-file.c b/builtin/cat-file.c
index 272f9fc..ae18e20 100644
--- a/builtin/cat-file.c
+++ b/builtin/cat-file.c
@@ -12,7 +12,7 @@
#include "userdiff.h"
#include "streaming.h"
#include "tree-walk.h"
-#include "sha1-array.h"
+#include "oid-array.h"
#include "packfile.h"
#include "object-store.h"
#include "promisor-remote.h"
@@ -42,7 +42,10 @@
oid_to_hex(oid), path);
if ((type == OBJ_BLOB) && S_ISREG(mode)) {
struct strbuf strbuf = STRBUF_INIT;
- if (convert_to_working_tree(&the_index, path, *buf, *size, &strbuf)) {
+ struct checkout_metadata meta;
+
+ init_checkout_metadata(&meta, NULL, NULL, oid);
+ if (convert_to_working_tree(&the_index, path, *buf, *size, &strbuf, &meta)) {
free(*buf);
*size = strbuf.len;
*buf = strbuf_detach(&strbuf, NULL);
@@ -647,14 +650,14 @@
OPT_BOOL(0, "allow-unknown-type", &unknown_type,
N_("allow -s and -t to work with broken/corrupt objects")),
OPT_BOOL(0, "buffer", &batch.buffer_output, N_("buffer --batch output")),
- { OPTION_CALLBACK, 0, "batch", &batch, "format",
+ OPT_CALLBACK_F(0, "batch", &batch, "format",
N_("show info and content of objects fed from the standard input"),
PARSE_OPT_OPTARG | PARSE_OPT_NONEG,
- batch_option_callback },
- { OPTION_CALLBACK, 0, "batch-check", &batch, "format",
+ batch_option_callback),
+ OPT_CALLBACK_F(0, "batch-check", &batch, "format",
N_("show info about objects fed from the standard input"),
PARSE_OPT_OPTARG | PARSE_OPT_NONEG,
- batch_option_callback },
+ batch_option_callback),
OPT_BOOL(0, "follow-symlinks", &batch.follow_symlinks,
N_("follow in-tree symlinks (used with --batch or --batch-check)")),
OPT_BOOL(0, "batch-all-objects", &batch.all_objects,
diff --git a/builtin/checkout-index.c b/builtin/checkout-index.c
index 1ac1cc2..a854fd1 100644
--- a/builtin/checkout-index.c
+++ b/builtin/checkout-index.c
@@ -177,9 +177,9 @@
N_("write the content to temporary files")),
OPT_STRING(0, "prefix", &state.base_dir, N_("string"),
N_("when creating files, prepend <string>")),
- { OPTION_CALLBACK, 0, "stage", NULL, "(1|2|3|all)",
+ OPT_CALLBACK_F(0, "stage", NULL, "(1|2|3|all)",
N_("copy out the files from named stage"),
- PARSE_OPT_NONEG, option_parse_stage },
+ PARSE_OPT_NONEG, option_parse_stage),
OPT_END()
};
diff --git a/builtin/checkout.c b/builtin/checkout.c
index d677381..e9d111b 100644
--- a/builtin/checkout.c
+++ b/builtin/checkout.c
@@ -88,6 +88,19 @@
struct tree *source_tree;
};
+struct branch_info {
+ const char *name; /* The short name used */
+ const 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. */
+ /*
+ * if not null the branch is detached because it's already
+ * checked out in this checkout
+ */
+ char *checkout;
+};
+
static int post_checkout_hook(struct commit *old_commit, struct commit *new_commit,
int changed)
{
@@ -337,7 +350,8 @@
}
}
-static int checkout_worktree(const struct checkout_opts *opts)
+static int checkout_worktree(const struct checkout_opts *opts,
+ const struct branch_info *info)
{
struct checkout state = CHECKOUT_INIT;
int nr_checkouts = 0, nr_unmerged = 0;
@@ -348,6 +362,10 @@
state.refresh_cache = 1;
state.istate = &the_index;
+ init_checkout_metadata(&state.meta, info->refname,
+ info->commit ? &info->commit->object.oid : &info->oid,
+ NULL);
+
enable_delayed_checkout(&state);
for (pos = 0; pos < active_nr; pos++) {
struct cache_entry *ce = active_cache[pos];
@@ -396,7 +414,7 @@
}
static int checkout_paths(const struct checkout_opts *opts,
- const char *revision)
+ const struct branch_info *new_branch_info)
{
int pos;
static char *ps_matched;
@@ -462,7 +480,7 @@
else
BUG("either flag must have been set, worktree=%d, index=%d",
opts->checkout_worktree, opts->checkout_index);
- return run_add_interactive(revision, patch_mode, &opts->pathspec);
+ return run_add_interactive(new_branch_info->name, patch_mode, &opts->pathspec);
}
repo_hold_locked_index(the_repository, &lock_file, LOCK_DIE_ON_ERROR);
@@ -523,7 +541,7 @@
/* Now we are committed to check them out */
if (opts->checkout_worktree)
- errs |= checkout_worktree(opts);
+ errs |= checkout_worktree(opts, new_branch_info);
else
remove_marked_cache_entries(&the_index, 1);
@@ -586,7 +604,8 @@
}
static int reset_tree(struct tree *tree, const struct checkout_opts *o,
- int worktree, int *writeout_error)
+ int worktree, int *writeout_error,
+ struct branch_info *info)
{
struct unpack_trees_options opts;
struct tree_desc tree_desc;
@@ -601,6 +620,11 @@
opts.verbose_update = o->show_progress;
opts.src_index = &the_index;
opts.dst_index = &the_index;
+ init_checkout_metadata(&opts.meta, info->refname,
+ info->commit ? &info->commit->object.oid :
+ is_null_oid(&info->oid) ? &tree->object.oid :
+ &info->oid,
+ NULL);
parse_tree(tree);
init_tree_desc(&tree_desc, tree->buffer, tree->size);
switch (unpack_trees(1, &tree_desc, &opts)) {
@@ -620,21 +644,17 @@
}
}
-struct branch_info {
- const char *name; /* The short name used */
- const char *path; /* The full name of a real branch */
- struct commit *commit; /* The named commit */
- /*
- * if not null the branch is detached because it's already
- * checked out in this checkout
- */
- char *checkout;
-};
-
static void setup_branch_path(struct branch_info *branch)
{
struct strbuf buf = STRBUF_INIT;
+ /*
+ * If this is a ref, resolve it; otherwise, look up the OID for our
+ * expression. Failure here is okay.
+ */
+ if (!dwim_ref(branch->name, strlen(branch->name), &branch->oid, &branch->refname))
+ repo_get_oid_committish(the_repository, branch->name, &branch->oid);
+
strbuf_branchname(&buf, branch->name, INTERPRET_BRANCH_LOCAL);
if (strcmp(buf.buf, branch->name))
branch->name = xstrdup(buf.buf);
@@ -663,7 +683,7 @@
} else
new_tree = get_commit_tree(new_branch_info->commit);
if (opts->discard_changes) {
- ret = reset_tree(new_tree, opts, 1, writeout_error);
+ ret = reset_tree(new_tree, opts, 1, writeout_error, new_branch_info);
if (ret)
return ret;
} else {
@@ -692,6 +712,10 @@
topts.quiet = opts->merge && old_branch_info->commit;
topts.verbose_update = opts->show_progress;
topts.fn = twoway_merge;
+ init_checkout_metadata(&topts.meta, new_branch_info->refname,
+ new_branch_info->commit ?
+ &new_branch_info->commit->object.oid :
+ &new_branch_info->oid, NULL);
if (opts->overwrite_ignore) {
topts.dir = xcalloc(1, sizeof(*topts.dir));
topts.dir->flags |= DIR_SHOW_IGNORED;
@@ -762,7 +786,7 @@
ret = reset_tree(new_tree,
opts, 1,
- writeout_error);
+ writeout_error, new_branch_info);
if (ret)
return ret;
o.ancestor = old_branch_info->name;
@@ -782,7 +806,7 @@
exit(128);
ret = reset_tree(new_tree,
opts, 0,
- writeout_error);
+ writeout_error, new_branch_info);
strbuf_release(&o.obuf);
strbuf_release(&old_commit_shortname);
if (ret)
@@ -1462,9 +1486,9 @@
{
struct option options[] = {
OPT__QUIET(&opts->quiet, N_("suppress progress reporting")),
- { OPTION_CALLBACK, 0, "recurse-submodules", NULL,
+ OPT_CALLBACK_F(0, "recurse-submodules", NULL,
"checkout", "control recursive updating of submodules",
- PARSE_OPT_OPTARG, option_parse_recurse_submodules_worktree_updater },
+ PARSE_OPT_OPTARG, option_parse_recurse_submodules_worktree_updater),
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"),
@@ -1520,6 +1544,9 @@
return newopts;
}
+/* create-branch option (either b or c) */
+static char cb_option = 'b';
+
static int checkout_main(int argc, const char **argv, const char *prefix,
struct checkout_opts *opts, struct option *options,
const char * const usagestr[])
@@ -1562,7 +1589,8 @@
}
if ((!!opts->new_branch + !!opts->new_branch_force + !!opts->new_orphan_branch) > 1)
- die(_("-b, -B and --orphan are mutually exclusive"));
+ die(_("-%c, -%c and --orphan are mutually exclusive"),
+ cb_option, toupper(cb_option));
if (opts->overlay_mode == 1 && opts->patch_mode)
die(_("-p and --overlay are mutually exclusive"));
@@ -1581,16 +1609,16 @@
if (opts->checkout_index < 0 || opts->checkout_worktree < 0)
BUG("these flags should be non-negative by now");
/*
- * convenient shortcut: "git restore --staged" equals
- * "git restore --staged --source HEAD"
+ * convenient shortcut: "git restore --staged [--worktree]" equals
+ * "git restore --staged [--worktree] --source HEAD"
*/
- if (!opts->from_treeish && opts->checkout_index && !opts->checkout_worktree)
+ if (!opts->from_treeish && opts->checkout_index)
opts->from_treeish = "HEAD";
/*
* From here on, new_branch will contain the branch to be checked out,
* and new_branch_force and new_orphan_branch will tell us which one of
- * -b/-B/--orphan is being used.
+ * -b/-B/-c/-C/--orphan is being used.
*/
if (opts->new_branch_force)
opts->new_branch = opts->new_branch_force;
@@ -1598,7 +1626,7 @@
if (opts->new_orphan_branch)
opts->new_branch = opts->new_orphan_branch;
- /* --track without -b/-B/--orphan should DWIM */
+ /* --track without -c/-C/-b/-B/--orphan should DWIM */
if (opts->track != BRANCH_TRACK_UNSPECIFIED && !opts->new_branch) {
const char *argv0 = argv[0];
if (!argc || !strcmp(argv0, "--"))
@@ -1607,7 +1635,7 @@
skip_prefix(argv0, "remotes/", &argv0);
argv0 = strchr(argv0, '/');
if (!argv0 || !argv0[1])
- die(_("missing branch name; try -b"));
+ die(_("missing branch name; try -%c"), cb_option);
opts->new_branch = argv0 + 1;
}
@@ -1710,7 +1738,7 @@
UNLEAK(opts);
if (opts->patch_mode || opts->pathspec.nr)
- return checkout_paths(opts, new_branch_info.name);
+ return checkout_paths(opts, &new_branch_info);
else
return checkout_branch(opts, &new_branch_info);
}
@@ -1798,6 +1826,8 @@
options = add_common_options(&opts, options);
options = add_common_switch_branch_options(&opts, options);
+ cb_option = 'c';
+
ret = checkout_main(argc, argv, prefix, &opts,
options, switch_branch_usage);
FREE_AND_NULL(options);
diff --git a/builtin/clean.c b/builtin/clean.c
index 5abf087..4ca12bc 100644
--- a/builtin/clean.c
+++ b/builtin/clean.c
@@ -18,6 +18,7 @@
#include "color.h"
#include "pathspec.h"
#include "help.h"
+#include "prompt.h"
static int force = -1; /* unset */
static int interactive;
@@ -420,7 +421,6 @@
return found;
}
-
/*
* Parse user input, and return choice(s) for menu (menu_stuff).
*
@@ -580,9 +580,7 @@
clean_get_color(CLEAN_COLOR_RESET));
}
- if (strbuf_getline_lf(&choice, stdin) != EOF) {
- strbuf_trim(&choice);
- } else {
+ if (git_read_line_interactively(&choice) == EOF) {
eof = 1;
break;
}
@@ -662,9 +660,7 @@
clean_print_color(CLEAN_COLOR_PROMPT);
printf(_("Input ignore patterns>> "));
clean_print_color(CLEAN_COLOR_RESET);
- if (strbuf_getline_lf(&confirm, stdin) != EOF)
- strbuf_trim(&confirm);
- else
+ if (git_read_line_interactively(&confirm) == EOF)
putchar('\n');
/* quit filter_by_pattern mode if press ENTER or Ctrl-D */
@@ -760,9 +756,7 @@
qname = quote_path_relative(item->string, NULL, &buf);
/* TRANSLATORS: Make sure to keep [y/N] as is */
printf(_("Remove %s [y/N]? "), qname);
- if (strbuf_getline_lf(&confirm, stdin) != EOF) {
- strbuf_trim(&confirm);
- } else {
+ if (git_read_line_interactively(&confirm) == EOF) {
putchar('\n');
eof = 1;
}
@@ -912,8 +906,8 @@
OPT_BOOL('i', "interactive", &interactive, N_("interactive cleaning")),
OPT_BOOL('d', NULL, &remove_directories,
N_("remove whole directories")),
- { OPTION_CALLBACK, 'e', "exclude", &exclude_list, N_("pattern"),
- N_("add <pattern> to ignore rules"), PARSE_OPT_NONEG, exclude_cb },
+ OPT_CALLBACK_F('e', "exclude", &exclude_list, N_("pattern"),
+ N_("add <pattern> to ignore rules"), PARSE_OPT_NONEG, exclude_cb),
OPT_BOOL('x', NULL, &ignored, N_("remove ignored files, too")),
OPT_BOOL('X', NULL, &ignored_only,
N_("remove only ignored files")),
@@ -989,12 +983,6 @@
if (!cache_name_is_other(ent->name, ent->len))
continue;
- if (pathspec.nr)
- matches = dir_path_match(&the_index, ent, &pathspec, 0, NULL);
-
- if (pathspec.nr && !matches)
- continue;
-
if (lstat(ent->name, &st))
die_errno("Cannot lstat '%s'", ent->name);
diff --git a/builtin/clone.c b/builtin/clone.c
index 1ad26f4..cb48a29 100644
--- a/builtin/clone.c
+++ b/builtin/clone.c
@@ -102,10 +102,10 @@
N_("don't use local hardlinks, always copy")),
OPT_BOOL('s', "shared", &option_shared,
N_("setup as shared repository")),
- OPT_ALIAS(0, "recursive", "recurse-submodules"),
{ OPTION_CALLBACK, 0, "recurse-submodules", &option_recurse_submodules,
N_("pathspec"), N_("initialize submodules in the clone"),
PARSE_OPT_OPTARG, recurse_submodules_cb, (intptr_t)"." },
+ OPT_ALIAS(0, "recursive", "recurse-submodules"),
OPT_INTEGER('j', "jobs", &max_jobs,
N_("number of submodules cloned in parallel")),
OPT_STRING(0, "template", &option_template, N_("template-directory"),
@@ -420,6 +420,7 @@
struct dir_iterator *iter;
int iter_status;
unsigned int flags;
+ struct strbuf realpath = STRBUF_INIT;
mkdir_if_missing(dest->buf, 0777);
@@ -454,7 +455,8 @@
if (unlink(dest->buf) && errno != ENOENT)
die_errno(_("failed to unlink '%s'"), dest->buf);
if (!option_no_hardlinks) {
- if (!link(real_path(src->buf), dest->buf))
+ strbuf_realpath(&realpath, src->buf, 1);
+ if (!link(realpath.buf, dest->buf))
continue;
if (option_local > 0)
die_errno(_("failed to create link '%s'"), dest->buf);
@@ -468,6 +470,8 @@
strbuf_setlen(src, src_len);
die(_("failed to iterate over '%s'"), src->buf);
}
+
+ strbuf_release(&realpath);
}
static void clone_local(const char *src_repo, const char *dest_repo)
@@ -639,7 +643,9 @@
continue;
if (ends_with(ref->name, "^{}"))
continue;
- if (!has_object_file(&ref->old_oid))
+ if (!has_object_file_with_flags(&ref->old_oid,
+ OBJECT_INFO_QUICK |
+ OBJECT_INFO_SKIP_FETCH_OBJECT))
continue;
update_ref(msg, ref->name, &ref->old_oid, NULL, 0,
UPDATE_REFS_DIE_ON_ERR);
@@ -672,8 +678,7 @@
const char *branch_top,
const char *msg,
struct transport *transport,
- int check_connectivity,
- int check_refs_are_promisor_objects_only)
+ int check_connectivity)
{
const struct ref *rm = mapped_refs;
@@ -682,8 +687,6 @@
opt.transport = transport;
opt.progress = transport->progress;
- opt.check_refs_are_promisor_objects_only =
- !!check_refs_are_promisor_objects_only;
if (check_connected(iterate_ref_map, &rm, &opt))
die(_("remote did not send all necessary objects"));
@@ -780,11 +783,11 @@
if (!strcmp(head, "HEAD")) {
if (advice_detached_head)
detach_advice(oid_to_hex(&oid));
+ FREE_AND_NULL(head);
} else {
if (!starts_with(head, "refs/heads/"))
die(_("HEAD not found below refs/heads!"));
}
- free(head);
/* We need to be in the new work tree for the checkout */
setup_work_tree();
@@ -799,6 +802,7 @@
opts.verbose_update = (option_verbosity >= 0);
opts.src_index = &the_index;
opts.dst_index = &the_index;
+ init_checkout_metadata(&opts.meta, head, &oid, NULL);
tree = parse_tree_indirect(&oid);
parse_tree(tree);
@@ -806,6 +810,8 @@
if (unpack_trees(1, &t, &opts) < 0)
die(_("unable to checkout working tree"));
+ free(head);
+
if (write_locked_index(&the_index, &lock_file, COMMIT_LOCK))
die(_("unable to write new index file"));
@@ -1102,7 +1108,7 @@
}
}
- init_db(git_dir, real_git_dir, option_template, INIT_DB_QUIET);
+ init_db(git_dir, real_git_dir, option_template, GIT_HASH_UNKNOWN, INIT_DB_QUIET);
if (real_git_dir)
git_dir = real_git_dir;
@@ -1275,7 +1281,7 @@
update_remote_refs(refs, mapped_refs, remote_head_points_at,
branch_top.buf, reflog_msg.buf, transport,
- !is_local, filter_options.choice);
+ !is_local);
update_head(our_head_points_at, remote_head, reflog_msg.buf);
diff --git a/builtin/commit-graph.c b/builtin/commit-graph.c
index 4a70b33..15fe603 100644
--- a/builtin/commit-graph.c
+++ b/builtin/commit-graph.c
@@ -9,7 +9,9 @@
static char const * const builtin_commit_graph_usage[] = {
N_("git commit-graph verify [--object-dir <objdir>] [--shallow] [--[no-]progress]"),
- N_("git commit-graph write [--object-dir <objdir>] [--append|--split] [--reachable|--stdin-packs|--stdin-commits] [--[no-]progress] <split options>"),
+ N_("git commit-graph write [--object-dir <objdir>] [--append] "
+ "[--split[=<strategy>]] [--reachable|--stdin-packs|--stdin-commits] "
+ "[--changed-paths] [--[no-]progress] <split options>"),
NULL
};
@@ -19,7 +21,9 @@
};
static const char * const builtin_commit_graph_write_usage[] = {
- N_("git commit-graph write [--object-dir <objdir>] [--append|--split] [--reachable|--stdin-packs|--stdin-commits] [--[no-]progress] <split options>"),
+ N_("git commit-graph write [--object-dir <objdir>] [--append] "
+ "[--split[=<strategy>]] [--reachable|--stdin-packs|--stdin-commits] "
+ "[--changed-paths] [--[no-]progress] <split options>"),
NULL
};
@@ -32,6 +36,7 @@
int split;
int shallow;
int progress;
+ int enable_changed_paths;
} opts;
static struct object_directory *find_odb(struct repository *r,
@@ -39,14 +44,17 @@
{
struct object_directory *odb;
char *obj_dir_real = real_pathdup(obj_dir, 1);
+ struct strbuf odb_path_real = STRBUF_INIT;
prepare_alt_odb(r);
for (odb = r->objects->odb; odb; odb = odb->next) {
- if (!strcmp(obj_dir_real, real_path(odb->path)))
+ strbuf_realpath(&odb_path_real, odb->path, 1);
+ if (!strcmp(obj_dir_real, odb_path_real.buf))
break;
}
free(obj_dir_real);
+ strbuf_release(&odb_path_real);
if (!odb)
die(_("could not find object directory matching %s"), obj_dir);
@@ -111,10 +119,29 @@
extern int read_replace_refs;
static struct split_commit_graph_opts split_opts;
+static int write_option_parse_split(const struct option *opt, const char *arg,
+ int unset)
+{
+ enum commit_graph_split_flags *flags = opt->value;
+
+ opts.split = 1;
+ if (!arg)
+ return 0;
+
+ if (!strcmp(arg, "no-merge"))
+ *flags = COMMIT_GRAPH_SPLIT_MERGE_PROHIBITED;
+ else if (!strcmp(arg, "replace"))
+ *flags = COMMIT_GRAPH_SPLIT_REPLACE;
+ else
+ die(_("unrecognized --split argument, %s"), arg);
+
+ return 0;
+}
+
static int graph_write(int argc, const char **argv)
{
struct string_list *pack_indexes = NULL;
- struct string_list *commit_hex = NULL;
+ struct oidset commits = OIDSET_INIT;
struct object_directory *odb = NULL;
struct string_list lines;
int result = 0;
@@ -132,15 +159,19 @@
N_("start walk at commits listed by stdin")),
OPT_BOOL(0, "append", &opts.append,
N_("include all commits already in the commit-graph file")),
+ OPT_BOOL(0, "changed-paths", &opts.enable_changed_paths,
+ N_("enable computation for changed paths")),
OPT_BOOL(0, "progress", &opts.progress, N_("force progress reporting")),
- OPT_BOOL(0, "split", &opts.split,
- N_("allow writing an incremental commit-graph file")),
+ OPT_CALLBACK_F(0, "split", &split_opts.flags, NULL,
+ N_("allow writing an incremental commit-graph file"),
+ PARSE_OPT_OPTARG | PARSE_OPT_NONEG,
+ write_option_parse_split),
OPT_INTEGER(0, "max-commits", &split_opts.max_commits,
N_("maximum number of commits in a non-base split commit-graph")),
OPT_INTEGER(0, "size-multiple", &split_opts.size_multiple,
N_("maximum ratio between two levels of a split commit-graph")),
OPT_EXPIRY_DATE(0, "expire-time", &split_opts.expire_time,
- N_("maximum number of commits in a non-base split commit-graph")),
+ N_("only expire files older than a given date-time")),
OPT_END(),
};
@@ -165,6 +196,9 @@
flags |= COMMIT_GRAPH_WRITE_SPLIT;
if (opts.progress)
flags |= COMMIT_GRAPH_WRITE_PROGRESS;
+ if (opts.enable_changed_paths ||
+ git_env_bool(GIT_TEST_COMMIT_GRAPH_CHANGED_PATHS, 0))
+ flags |= COMMIT_GRAPH_WRITE_BLOOM_FILTERS;
read_replace_refs = 0;
odb = find_odb(the_repository, opts.obj_dir);
@@ -185,7 +219,20 @@
if (opts.stdin_packs)
pack_indexes = &lines;
if (opts.stdin_commits) {
- commit_hex = &lines;
+ struct string_list_item *item;
+ oidset_init(&commits, lines.nr);
+ for_each_string_list_item(item, &lines) {
+ struct object_id oid;
+ const char *end;
+
+ if (parse_oid_hex(item->string, &oid, &end)) {
+ error(_("unexpected non-hex object ID: "
+ "%s"), item->string);
+ return 1;
+ }
+
+ oidset_insert(&commits, &oid);
+ }
flags |= COMMIT_GRAPH_WRITE_CHECK_OIDS;
}
@@ -194,7 +241,7 @@
if (write_commit_graph(odb,
pack_indexes,
- commit_hex,
+ opts.stdin_commits ? &commits : NULL,
flags,
&split_opts))
result = 1;
diff --git a/builtin/commit-tree.c b/builtin/commit-tree.c
index b866d83..1031b9a 100644
--- a/builtin/commit-tree.c
+++ b/builtin/commit-tree.c
@@ -108,15 +108,15 @@
struct object_id commit_oid;
struct option options[] = {
- { OPTION_CALLBACK, 'p', NULL, &parents, N_("parent"),
+ OPT_CALLBACK_F('p', NULL, &parents, N_("parent"),
N_("id of a parent commit object"), PARSE_OPT_NONEG,
- parse_parent_arg_callback },
- { OPTION_CALLBACK, 'm', NULL, &buffer, N_("message"),
+ parse_parent_arg_callback),
+ OPT_CALLBACK_F('m', NULL, &buffer, N_("message"),
N_("commit message"), PARSE_OPT_NONEG,
- parse_message_arg_callback },
- { OPTION_CALLBACK, 'F', NULL, &buffer, N_("file"),
+ parse_message_arg_callback),
+ OPT_CALLBACK_F('F', NULL, &buffer, N_("file"),
N_("read commit log message from file"), PARSE_OPT_NONEG,
- parse_file_arg_callback },
+ parse_file_arg_callback),
{ OPTION_STRING, 'S', "gpg-sign", &sign_commit, N_("key-id"),
N_("GPG sign commit"), PARSE_OPT_OPTARG, NULL, (intptr_t) "" },
OPT_END()
diff --git a/builtin/commit.c b/builtin/commit.c
index 7ba33a3..d1b7396 100644
--- a/builtin/commit.c
+++ b/builtin/commit.c
@@ -59,6 +59,9 @@
" git commit --allow-empty\n"
"\n");
+static const char empty_rebase_pick_advice[] =
+N_("Otherwise, please use 'git rebase --skip'\n");
+
static const char empty_cherry_pick_advice_single[] =
N_("Otherwise, please use 'git cherry-pick --skip'\n");
@@ -122,7 +125,6 @@
static const char *cleanup_arg;
static enum commit_whence whence;
-static int sequencer_in_use;
static int use_editor = 1, include_status = 1;
static int have_option_m;
static struct strbuf message = STRBUF_INIT;
@@ -179,12 +181,7 @@
{
if (file_exists(git_path_merge_head(the_repository)))
whence = FROM_MERGE;
- else if (file_exists(git_path_cherry_pick_head(the_repository))) {
- whence = FROM_CHERRY_PICK;
- if (file_exists(git_path_seq_dir()))
- sequencer_in_use = 1;
- }
- else
+ else if (!sequencer_determine_whence(the_repository, &whence))
whence = FROM_COMMIT;
if (s)
s->whence = whence;
@@ -477,8 +474,10 @@
if (whence != FROM_COMMIT) {
if (whence == FROM_MERGE)
die(_("cannot do a partial commit during a merge."));
- else if (whence == FROM_CHERRY_PICK)
+ else if (is_from_cherry_pick(whence))
die(_("cannot do a partial commit during a cherry-pick."));
+ else if (is_from_rebase(whence))
+ die(_("cannot do a partial commit during a rebase."));
}
if (list_paths(&partial, !current_head ? NULL : "HEAD", &pathspec))
@@ -795,7 +794,7 @@
*/
else if (whence == FROM_MERGE)
hook_arg1 = "merge";
- else if (whence == FROM_CHERRY_PICK) {
+ else if (is_from_cherry_pick(whence) || whence == FROM_REBASE_PICK) {
hook_arg1 = "commit";
hook_arg2 = "CHERRY_PICK_HEAD";
}
@@ -973,12 +972,15 @@
run_status(stdout, index_file, prefix, 0, s);
if (amend)
fputs(_(empty_amend_advice), stderr);
- else if (whence == FROM_CHERRY_PICK) {
+ else if (is_from_cherry_pick(whence) ||
+ whence == FROM_REBASE_PICK) {
fputs(_(empty_cherry_pick_advice), stderr);
- if (!sequencer_in_use)
+ if (whence == FROM_CHERRY_PICK_SINGLE)
fputs(_(empty_cherry_pick_advice_single), stderr);
- else
+ else if (whence == FROM_CHERRY_PICK_MULTI)
fputs(_(empty_cherry_pick_advice_multi), stderr);
+ else
+ fputs(_(empty_rebase_pick_advice), stderr);
}
return 0;
}
@@ -1181,8 +1183,10 @@
if (amend && whence != FROM_COMMIT) {
if (whence == FROM_MERGE)
die(_("You are in the middle of a merge -- cannot amend."));
- else if (whence == FROM_CHERRY_PICK)
+ else if (is_from_cherry_pick(whence))
die(_("You are in the middle of a cherry-pick -- cannot amend."));
+ else if (whence == FROM_REBASE_PICK)
+ die(_("You are in the middle of a rebase -- cannot amend."));
}
if (fixup_message && squash_message)
die(_("Options --squash and --fixup cannot be used together"));
@@ -1204,7 +1208,8 @@
use_message = edit_message;
if (amend && !use_message && !fixup_message)
use_message = "HEAD";
- if (!use_message && whence != FROM_CHERRY_PICK && renew_authorship)
+ if (!use_message && !is_from_cherry_pick(whence) &&
+ !is_from_rebase(whence) && renew_authorship)
die(_("--reset-author can be used only with -C, -c or --amend."));
if (use_message) {
use_message_buffer = read_commit_message(use_message);
@@ -1213,7 +1218,8 @@
author_message_buffer = use_message_buffer;
}
}
- if (whence == FROM_CHERRY_PICK && !renew_authorship) {
+ if ((is_from_cherry_pick(whence) || whence == FROM_REBASE_PICK) &&
+ !renew_authorship) {
author_message = "CHERRY_PICK_HEAD";
author_message_buffer = read_commit_message(author_message);
}
@@ -1366,9 +1372,9 @@
N_("show stash information")),
OPT_BOOL(0, "ahead-behind", &s.ahead_behind_flags,
N_("compute full ahead/behind values")),
- { OPTION_CALLBACK, 0, "porcelain", &status_format,
+ OPT_CALLBACK_F(0, "porcelain", &status_format,
N_("version"), N_("machine-readable output"),
- PARSE_OPT_OPTARG, opt_parse_porcelain },
+ PARSE_OPT_OPTARG, opt_parse_porcelain),
OPT_SET_INT(0, "long", &status_format,
N_("show status in long format (default)"),
STATUS_FORMAT_LONG),
@@ -1387,9 +1393,9 @@
PARSE_OPT_OPTARG, NULL, (intptr_t)"all" },
OPT_COLUMN(0, "column", &s.colopts, N_("list untracked files in columns")),
OPT_BOOL(0, "no-renames", &no_renames, N_("do not detect renames")),
- { OPTION_CALLBACK, 'M', "find-renames", &rename_score_arg,
+ OPT_CALLBACK_F('M', "find-renames", &rename_score_arg,
N_("n"), N_("detect renames, optionally set similarity index"),
- PARSE_OPT_OPTARG | PARSE_OPT_NONEG, opt_parse_rename_score },
+ PARSE_OPT_OPTARG | PARSE_OPT_NONEG, opt_parse_rename_score),
OPT_END(),
};
@@ -1488,7 +1494,6 @@
int cmd_commit(int argc, const char **argv, const char *prefix)
{
- const char *argv_gc_auto[] = {"gc", "--auto", NULL};
static struct wt_status s;
static struct option builtin_commit_options[] = {
OPT__QUIET(&quiet, N_("suppress summary after successful commit")),
@@ -1631,8 +1636,10 @@
reduce_heads_replace(&parents);
} else {
if (!reflog_msg)
- reflog_msg = (whence == FROM_CHERRY_PICK)
+ reflog_msg = is_from_cherry_pick(whence)
? "commit (cherry-pick)"
+ : is_from_rebase(whence)
+ ? "commit (rebase)"
: "commit";
commit_list_insert(current_head, &parents);
}
@@ -1659,7 +1666,7 @@
}
if (amend) {
- const char *exclude_gpgsig[2] = { "gpgsig", NULL };
+ const char *exclude_gpgsig[3] = { "gpgsig", "gpgsig-sha256", NULL };
extra = read_commit_extra_headers(current_head, exclude_gpgsig);
} else {
struct commit_extra_header **tail = &extra;
@@ -1692,12 +1699,10 @@
"new_index file. Check that disk is not full and quota is\n"
"not exceeded, and then \"git restore --staged :/\" to recover."));
- if (git_env_bool(GIT_TEST_COMMIT_GRAPH, 0) &&
- write_commit_graph_reachable(the_repository->objects->odb, 0, NULL))
- return 1;
+ git_test_write_commit_graph_or_die();
repo_rerere(the_repository, 0);
- run_command_v_opt(argv_gc_auto, RUN_GIT_CMD);
+ run_auto_gc(quiet);
run_commit_hook(use_editor, get_index_file(), "post-commit", NULL);
if (amend && !no_post_rewrite) {
commit_post_rewrite(the_repository, current_head, &oid);
@@ -1713,6 +1718,8 @@
&oid, flags);
}
+ apply_autostash(git_path_merge_autostash(the_repository));
+
UNLEAK(err);
UNLEAK(sb);
return 0;
diff --git a/builtin/describe.c b/builtin/describe.c
index 420f4c6..21d2cb9 100644
--- a/builtin/describe.c
+++ b/builtin/describe.c
@@ -54,6 +54,7 @@
struct tag *tag;
unsigned prio:2; /* annotated tag = 2, tag = 1, head = 0 */
unsigned name_checked:1;
+ unsigned misnamed:1;
struct object_id oid;
char *path;
};
@@ -132,6 +133,7 @@
e->tag = tag;
e->prio = prio;
e->name_checked = 0;
+ e->misnamed = 0;
oidcpy(&e->oid, oid);
free(e->path);
e->path = xstrdup(path);
@@ -275,10 +277,11 @@
die(_("annotated tag %s not available"), n->path);
}
if (n->tag && !n->name_checked) {
- if (!n->tag->tag)
- die(_("annotated tag %s has no embedded name"), n->path);
- if (strcmp(n->tag->tag, all ? n->path + 5 : n->path))
- warning(_("tag '%s' is really '%s' here"), n->tag->tag, n->path);
+ if (strcmp(n->tag->tag, all ? n->path + 5 : n->path)) {
+ warning(_("tag '%s' is externally known as '%s'"),
+ n->path, n->tag->tag);
+ n->misnamed = 1;
+ }
n->name_checked = 1;
}
@@ -314,7 +317,7 @@
* Exact match to an existing ref.
*/
append_name(n, dst);
- if (longformat)
+ if (n->misnamed || longformat)
append_suffix(0, n->tag ? get_tagged_oid(n->tag) : oid, dst);
if (suffix)
strbuf_addstr(dst, suffix);
@@ -463,7 +466,7 @@
}
append_name(all_matches[0].name, dst);
- if (abbrev)
+ if (all_matches[0].name->misnamed || abbrev)
append_suffix(all_matches[0].depth, &cmit->object.oid, dst);
if (suffix)
strbuf_addstr(dst, suffix);
diff --git a/builtin/diff-tree.c b/builtin/diff-tree.c
index cb9ea79..802363d 100644
--- a/builtin/diff-tree.c
+++ b/builtin/diff-tree.c
@@ -109,6 +109,7 @@
struct object *tree1, *tree2;
static struct rev_info *opt = &log_tree_opt;
struct setup_revision_opt s_r_opt;
+ struct userformat_want w;
int read_stdin = 0;
if (argc == 2 && !strcmp(argv[1], "-h"))
@@ -127,6 +128,14 @@
precompose_argv(argc, argv);
argc = setup_revisions(argc, argv, opt, &s_r_opt);
+ memset(&w, 0, sizeof(w));
+ userformat_find_requirements(NULL, &w);
+
+ if (!opt->show_notes_given && w.notes)
+ opt->show_notes = 1;
+ if (opt->show_notes)
+ load_display_notes(&opt->notes_opt);
+
while (--argc > 0) {
const char *arg = *++argv;
diff --git a/builtin/diff.c b/builtin/diff.c
index 42ac803..8537b17 100644
--- a/builtin/diff.c
+++ b/builtin/diff.c
@@ -17,7 +17,7 @@
#include "log-tree.h"
#include "builtin.h"
#include "submodule.h"
-#include "sha1-array.h"
+#include "oid-array.h"
#define DIFF_NO_INDEX_EXPLICIT 1
#define DIFF_NO_INDEX_IMPLICIT 2
diff --git a/builtin/fetch-pack.c b/builtin/fetch-pack.c
index dc1485c..4771100 100644
--- a/builtin/fetch-pack.c
+++ b/builtin/fetch-pack.c
@@ -3,7 +3,7 @@
#include "fetch-pack.h"
#include "remote.h"
#include "connect.h"
-#include "sha1-array.h"
+#include "oid-array.h"
#include "protocol.h"
static const char fetch_pack_usage[] =
diff --git a/builtin/fetch.c b/builtin/fetch.c
index bf6bab8..b5788c1 100644
--- a/builtin/fetch.c
+++ b/builtin/fetch.c
@@ -27,6 +27,7 @@
#include "branch.h"
#include "promisor-remote.h"
#include "commit-graph.h"
+#include "shallow.h"
#define FORCED_UPDATES_DELAY_WARNING_IN_MS (10 * 1000)
@@ -156,9 +157,9 @@
N_("prune remote-tracking branches no longer on remote")),
OPT_BOOL('P', "prune-tags", &prune_tags,
N_("prune local tags no longer on remote and clobber changed tags")),
- { OPTION_CALLBACK, 0, "recurse-submodules", &recurse_submodules, N_("on-demand"),
+ OPT_CALLBACK_F(0, "recurse-submodules", &recurse_submodules, N_("on-demand"),
N_("control recursive fetching of submodules"),
- PARSE_OPT_OPTARG, option_fetch_parse_recurse_submodules },
+ PARSE_OPT_OPTARG, option_fetch_parse_recurse_submodules),
OPT_BOOL(0, "dry-run", &dry_run,
N_("dry run")),
OPT_BOOL('k', "keep", &keep, N_("keep downloaded pack")),
@@ -178,15 +179,15 @@
1, PARSE_OPT_NONEG),
{ OPTION_STRING, 0, "submodule-prefix", &submodule_prefix, N_("dir"),
N_("prepend this to submodule path output"), PARSE_OPT_HIDDEN },
- { OPTION_CALLBACK, 0, "recurse-submodules-default",
+ OPT_CALLBACK_F(0, "recurse-submodules-default",
&recurse_submodules_default, N_("on-demand"),
N_("default for recursive fetching of submodules "
"(lower priority than config files)"),
- PARSE_OPT_HIDDEN, option_fetch_parse_recurse_submodules },
+ PARSE_OPT_HIDDEN, option_fetch_parse_recurse_submodules),
OPT_BOOL(0, "update-shallow", &update_shallow,
N_("accept refs that update .git/shallow")),
- { OPTION_CALLBACK, 0, "refmap", NULL, N_("refmap"),
- N_("specify fetch refmap"), PARSE_OPT_NONEG, parse_refmap_arg },
+ OPT_CALLBACK_F(0, "refmap", NULL, N_("refmap"),
+ N_("specify fetch refmap"), PARSE_OPT_NONEG, parse_refmap_arg),
OPT_STRING_LIST('o', "server-option", &server_options, N_("server-specific"), N_("option to transmit")),
OPT_SET_INT('4', "ipv4", &family, N_("use IPv4 addresses only"),
TRANSPORT_FAMILY_IPV4),
@@ -908,13 +909,6 @@
if (!connectivity_checked) {
struct check_connected_options opt = CHECK_CONNECTED_INIT;
- if (filter_options.choice)
- /*
- * Since a filter is specified, objects indirectly
- * referenced by refs are allowed to be absent.
- */
- opt.check_refs_are_promisor_objects_only = 1;
-
rm = ref_map;
if (check_connected(iterate_ref_map, &rm, &opt)) {
rc = error(_("%s did not send all necessary objects\n"), url);
@@ -1759,7 +1753,6 @@
struct remote *remote = NULL;
int result = 0;
int prune_tags_ok = 1;
- struct argv_array argv_gc_auto = ARGV_ARRAY_INIT;
packet_trace_identity("fetch");
@@ -1886,13 +1879,8 @@
close_object_store(the_repository->objects);
- if (enable_auto_gc) {
- argv_array_pushl(&argv_gc_auto, "gc", "--auto", NULL);
- if (verbosity < 0)
- argv_array_push(&argv_gc_auto, "--quiet");
- run_command_v_opt(argv_gc_auto.argv, RUN_GIT_CMD);
- argv_array_clear(&argv_gc_auto);
- }
+ if (enable_auto_gc)
+ run_auto_gc(verbosity < 0);
return result;
}
diff --git a/builtin/fmt-merge-msg.c b/builtin/fmt-merge-msg.c
index 736f666..48a8699 100644
--- a/builtin/fmt-merge-msg.c
+++ b/builtin/fmt-merge-msg.c
@@ -1,666 +1,13 @@
#include "builtin.h"
-#include "cache.h"
#include "config.h"
-#include "refs.h"
-#include "object-store.h"
-#include "commit.h"
-#include "diff.h"
-#include "revision.h"
-#include "tag.h"
-#include "string-list.h"
-#include "branch.h"
#include "fmt-merge-msg.h"
-#include "gpg-interface.h"
-#include "repository.h"
-#include "commit-reach.h"
+#include "parse-options.h"
static const char * const fmt_merge_msg_usage[] = {
N_("git fmt-merge-msg [-m <message>] [--log[=<n>] | --no-log] [--file <file>]"),
NULL
};
-static int use_branch_desc;
-
-int fmt_merge_msg_config(const char *key, const char *value, void *cb)
-{
- if (!strcmp(key, "merge.log") || !strcmp(key, "merge.summary")) {
- int is_bool;
- merge_log_config = git_config_bool_or_int(key, value, &is_bool);
- if (!is_bool && merge_log_config < 0)
- return error("%s: negative length %s", key, value);
- if (is_bool && merge_log_config)
- merge_log_config = DEFAULT_MERGE_LOG_LEN;
- } else if (!strcmp(key, "merge.branchdesc")) {
- use_branch_desc = git_config_bool(key, value);
- } else {
- return git_default_config(key, value, cb);
- }
- return 0;
-}
-
-/* merge data per repository where the merged tips came from */
-struct src_data {
- struct string_list branch, tag, r_branch, generic;
- int head_status;
-};
-
-struct origin_data {
- struct object_id oid;
- unsigned is_local_branch:1;
-};
-
-static void init_src_data(struct src_data *data)
-{
- data->branch.strdup_strings = 1;
- data->tag.strdup_strings = 1;
- data->r_branch.strdup_strings = 1;
- data->generic.strdup_strings = 1;
-}
-
-static struct string_list srcs = STRING_LIST_INIT_DUP;
-static struct string_list origins = STRING_LIST_INIT_DUP;
-
-struct merge_parents {
- int alloc, nr;
- struct merge_parent {
- struct object_id given;
- struct object_id commit;
- unsigned char used;
- } *item;
-};
-
-/*
- * I know, I know, this is inefficient, but you won't be pulling and merging
- * hundreds of heads at a time anyway.
- */
-static struct merge_parent *find_merge_parent(struct merge_parents *table,
- struct object_id *given,
- struct object_id *commit)
-{
- int i;
- for (i = 0; i < table->nr; i++) {
- if (given && !oideq(&table->item[i].given, given))
- continue;
- if (commit && !oideq(&table->item[i].commit, commit))
- continue;
- return &table->item[i];
- }
- return NULL;
-}
-
-static void add_merge_parent(struct merge_parents *table,
- struct object_id *given,
- struct object_id *commit)
-{
- if (table->nr && find_merge_parent(table, given, commit))
- return;
- ALLOC_GROW(table->item, table->nr + 1, table->alloc);
- oidcpy(&table->item[table->nr].given, given);
- oidcpy(&table->item[table->nr].commit, commit);
- table->item[table->nr].used = 0;
- table->nr++;
-}
-
-static int handle_line(char *line, struct merge_parents *merge_parents)
-{
- int i, len = strlen(line);
- struct origin_data *origin_data;
- char *src;
- const char *origin, *tag_name;
- struct src_data *src_data;
- struct string_list_item *item;
- int pulling_head = 0;
- struct object_id oid;
- const unsigned hexsz = the_hash_algo->hexsz;
-
- if (len < hexsz + 3 || line[hexsz] != '\t')
- return 1;
-
- if (starts_with(line + hexsz + 1, "not-for-merge"))
- return 0;
-
- if (line[hexsz + 1] != '\t')
- return 2;
-
- i = get_oid_hex(line, &oid);
- if (i)
- return 3;
-
- if (!find_merge_parent(merge_parents, &oid, NULL))
- return 0; /* subsumed by other parents */
-
- origin_data = xcalloc(1, sizeof(struct origin_data));
- oidcpy(&origin_data->oid, &oid);
-
- if (line[len - 1] == '\n')
- line[len - 1] = 0;
- line += hexsz + 2;
-
- /*
- * At this point, line points at the beginning of comment e.g.
- * "branch 'frotz' of git://that/repository.git".
- * Find the repository name and point it with src.
- */
- src = strstr(line, " of ");
- if (src) {
- *src = 0;
- src += 4;
- pulling_head = 0;
- } else {
- src = line;
- pulling_head = 1;
- }
-
- item = unsorted_string_list_lookup(&srcs, src);
- if (!item) {
- item = string_list_append(&srcs, src);
- item->util = xcalloc(1, sizeof(struct src_data));
- init_src_data(item->util);
- }
- src_data = item->util;
-
- if (pulling_head) {
- origin = src;
- src_data->head_status |= 1;
- } else if (skip_prefix(line, "branch ", &origin)) {
- origin_data->is_local_branch = 1;
- string_list_append(&src_data->branch, origin);
- src_data->head_status |= 2;
- } else if (skip_prefix(line, "tag ", &tag_name)) {
- origin = line;
- string_list_append(&src_data->tag, tag_name);
- src_data->head_status |= 2;
- } else if (skip_prefix(line, "remote-tracking branch ", &origin)) {
- string_list_append(&src_data->r_branch, origin);
- src_data->head_status |= 2;
- } else {
- origin = src;
- string_list_append(&src_data->generic, line);
- src_data->head_status |= 2;
- }
-
- if (!strcmp(".", src) || !strcmp(src, origin)) {
- int len = strlen(origin);
- if (origin[0] == '\'' && origin[len - 1] == '\'')
- origin = xmemdupz(origin + 1, len - 2);
- } else
- origin = xstrfmt("%s of %s", origin, src);
- if (strcmp(".", src))
- origin_data->is_local_branch = 0;
- string_list_append(&origins, origin)->util = origin_data;
- return 0;
-}
-
-static void print_joined(const char *singular, const char *plural,
- struct string_list *list, struct strbuf *out)
-{
- if (list->nr == 0)
- return;
- if (list->nr == 1) {
- strbuf_addf(out, "%s%s", singular, list->items[0].string);
- } else {
- int i;
- strbuf_addstr(out, plural);
- for (i = 0; i < list->nr - 1; i++)
- strbuf_addf(out, "%s%s", i > 0 ? ", " : "",
- list->items[i].string);
- strbuf_addf(out, " and %s", list->items[list->nr - 1].string);
- }
-}
-
-static void add_branch_desc(struct strbuf *out, const char *name)
-{
- struct strbuf desc = STRBUF_INIT;
-
- if (!read_branch_desc(&desc, name)) {
- const char *bp = desc.buf;
- while (*bp) {
- const char *ep = strchrnul(bp, '\n');
- if (*ep)
- ep++;
- strbuf_addf(out, " : %.*s", (int)(ep - bp), bp);
- bp = ep;
- }
- strbuf_complete_line(out);
- }
- strbuf_release(&desc);
-}
-
-#define util_as_integral(elem) ((intptr_t)((elem)->util))
-
-static void record_person_from_buf(int which, struct string_list *people,
- const char *buffer)
-{
- char *name_buf, *name, *name_end;
- struct string_list_item *elem;
- const char *field;
-
- field = (which == 'a') ? "\nauthor " : "\ncommitter ";
- name = strstr(buffer, field);
- if (!name)
- return;
- name += strlen(field);
- name_end = strchrnul(name, '<');
- if (*name_end)
- name_end--;
- while (isspace(*name_end) && name <= name_end)
- name_end--;
- if (name_end < name)
- return;
- name_buf = xmemdupz(name, name_end - name + 1);
-
- elem = string_list_lookup(people, name_buf);
- if (!elem) {
- elem = string_list_insert(people, name_buf);
- elem->util = (void *)0;
- }
- elem->util = (void*)(util_as_integral(elem) + 1);
- free(name_buf);
-}
-
-
-static void record_person(int which, struct string_list *people,
- struct commit *commit)
-{
- const char *buffer = get_commit_buffer(commit, NULL);
- record_person_from_buf(which, people, buffer);
- unuse_commit_buffer(commit, buffer);
-}
-
-static int cmp_string_list_util_as_integral(const void *a_, const void *b_)
-{
- const struct string_list_item *a = a_, *b = b_;
- return util_as_integral(b) - util_as_integral(a);
-}
-
-static void add_people_count(struct strbuf *out, struct string_list *people)
-{
- if (people->nr == 1)
- strbuf_addstr(out, people->items[0].string);
- else if (people->nr == 2)
- strbuf_addf(out, "%s (%d) and %s (%d)",
- people->items[0].string,
- (int)util_as_integral(&people->items[0]),
- people->items[1].string,
- (int)util_as_integral(&people->items[1]));
- else if (people->nr)
- strbuf_addf(out, "%s (%d) and others",
- people->items[0].string,
- (int)util_as_integral(&people->items[0]));
-}
-
-static void credit_people(struct strbuf *out,
- struct string_list *them,
- int kind)
-{
- const char *label;
- const char *me;
-
- if (kind == 'a') {
- label = "By";
- me = git_author_info(IDENT_NO_DATE);
- } else {
- label = "Via";
- me = git_committer_info(IDENT_NO_DATE);
- }
-
- if (!them->nr ||
- (them->nr == 1 &&
- me &&
- skip_prefix(me, them->items->string, &me) &&
- starts_with(me, " <")))
- return;
- strbuf_addf(out, "\n%c %s ", comment_line_char, label);
- add_people_count(out, them);
-}
-
-static void add_people_info(struct strbuf *out,
- struct string_list *authors,
- struct string_list *committers)
-{
- QSORT(authors->items, authors->nr,
- cmp_string_list_util_as_integral);
- QSORT(committers->items, committers->nr,
- cmp_string_list_util_as_integral);
-
- credit_people(out, authors, 'a');
- credit_people(out, committers, 'c');
-}
-
-static void shortlog(const char *name,
- struct origin_data *origin_data,
- struct commit *head,
- struct rev_info *rev,
- struct fmt_merge_msg_opts *opts,
- struct strbuf *out)
-{
- int i, count = 0;
- struct commit *commit;
- struct object *branch;
- struct string_list subjects = STRING_LIST_INIT_DUP;
- struct string_list authors = STRING_LIST_INIT_DUP;
- struct string_list committers = STRING_LIST_INIT_DUP;
- int flags = UNINTERESTING | TREESAME | SEEN | SHOWN | ADDED;
- struct strbuf sb = STRBUF_INIT;
- const struct object_id *oid = &origin_data->oid;
- int limit = opts->shortlog_len;
-
- branch = deref_tag(the_repository, parse_object(the_repository, oid),
- oid_to_hex(oid),
- the_hash_algo->hexsz);
- if (!branch || branch->type != OBJ_COMMIT)
- return;
-
- setup_revisions(0, NULL, rev, NULL);
- add_pending_object(rev, branch, name);
- add_pending_object(rev, &head->object, "^HEAD");
- head->object.flags |= UNINTERESTING;
- if (prepare_revision_walk(rev))
- die("revision walk setup failed");
- while ((commit = get_revision(rev)) != NULL) {
- struct pretty_print_context ctx = {0};
-
- if (commit->parents && commit->parents->next) {
- /* do not list a merge but count committer */
- if (opts->credit_people)
- record_person('c', &committers, commit);
- continue;
- }
- if (!count && opts->credit_people)
- /* the 'tip' committer */
- record_person('c', &committers, commit);
- if (opts->credit_people)
- record_person('a', &authors, commit);
- count++;
- if (subjects.nr > limit)
- continue;
-
- format_commit_message(commit, "%s", &sb, &ctx);
- strbuf_ltrim(&sb);
-
- if (!sb.len)
- string_list_append(&subjects,
- oid_to_hex(&commit->object.oid));
- else
- string_list_append_nodup(&subjects,
- strbuf_detach(&sb, NULL));
- }
-
- if (opts->credit_people)
- add_people_info(out, &authors, &committers);
- if (count > limit)
- strbuf_addf(out, "\n* %s: (%d commits)\n", name, count);
- else
- strbuf_addf(out, "\n* %s:\n", name);
-
- if (origin_data->is_local_branch && use_branch_desc)
- add_branch_desc(out, name);
-
- for (i = 0; i < subjects.nr; i++)
- if (i >= limit)
- strbuf_addstr(out, " ...\n");
- else
- strbuf_addf(out, " %s\n", subjects.items[i].string);
-
- clear_commit_marks((struct commit *)branch, flags);
- clear_commit_marks(head, flags);
- free_commit_list(rev->commits);
- rev->commits = NULL;
- rev->pending.nr = 0;
-
- string_list_clear(&authors, 0);
- string_list_clear(&committers, 0);
- string_list_clear(&subjects, 0);
-}
-
-static void fmt_merge_msg_title(struct strbuf *out,
- const char *current_branch)
-{
- int i = 0;
- char *sep = "";
-
- strbuf_addstr(out, "Merge ");
- for (i = 0; i < srcs.nr; i++) {
- struct src_data *src_data = srcs.items[i].util;
- const char *subsep = "";
-
- strbuf_addstr(out, sep);
- sep = "; ";
-
- if (src_data->head_status == 1) {
- strbuf_addstr(out, srcs.items[i].string);
- continue;
- }
- if (src_data->head_status == 3) {
- subsep = ", ";
- strbuf_addstr(out, "HEAD");
- }
- if (src_data->branch.nr) {
- strbuf_addstr(out, subsep);
- subsep = ", ";
- print_joined("branch ", "branches ", &src_data->branch,
- out);
- }
- if (src_data->r_branch.nr) {
- strbuf_addstr(out, subsep);
- subsep = ", ";
- print_joined("remote-tracking branch ", "remote-tracking branches ",
- &src_data->r_branch, out);
- }
- if (src_data->tag.nr) {
- strbuf_addstr(out, subsep);
- subsep = ", ";
- print_joined("tag ", "tags ", &src_data->tag, out);
- }
- if (src_data->generic.nr) {
- strbuf_addstr(out, subsep);
- print_joined("commit ", "commits ", &src_data->generic,
- out);
- }
- if (strcmp(".", srcs.items[i].string))
- strbuf_addf(out, " of %s", srcs.items[i].string);
- }
-
- if (!strcmp("master", current_branch))
- strbuf_addch(out, '\n');
- else
- strbuf_addf(out, " into %s\n", current_branch);
-}
-
-static void fmt_tag_signature(struct strbuf *tagbuf,
- struct strbuf *sig,
- const char *buf,
- unsigned long len)
-{
- const char *tag_body = strstr(buf, "\n\n");
- if (tag_body) {
- tag_body += 2;
- strbuf_add(tagbuf, tag_body, buf + len - tag_body);
- }
- strbuf_complete_line(tagbuf);
- if (sig->len) {
- strbuf_addch(tagbuf, '\n');
- strbuf_add_commented_lines(tagbuf, sig->buf, sig->len);
- }
-}
-
-static void fmt_merge_msg_sigs(struct strbuf *out)
-{
- int i, tag_number = 0, first_tag = 0;
- struct strbuf tagbuf = STRBUF_INIT;
-
- for (i = 0; i < origins.nr; i++) {
- struct object_id *oid = origins.items[i].util;
- enum object_type type;
- unsigned long size, len;
- char *buf = read_object_file(oid, &type, &size);
- struct strbuf sig = STRBUF_INIT;
-
- if (!buf || type != OBJ_TAG)
- goto next;
- len = parse_signature(buf, size);
-
- if (size == len)
- ; /* merely annotated */
- else if (verify_signed_buffer(buf, len, buf + len, size - len, &sig, NULL)) {
- if (!sig.len)
- strbuf_addstr(&sig, "gpg verification failed.\n");
- }
-
- if (!tag_number++) {
- fmt_tag_signature(&tagbuf, &sig, buf, len);
- first_tag = i;
- } else {
- if (tag_number == 2) {
- struct strbuf tagline = STRBUF_INIT;
- strbuf_addch(&tagline, '\n');
- strbuf_add_commented_lines(&tagline,
- origins.items[first_tag].string,
- strlen(origins.items[first_tag].string));
- strbuf_insert(&tagbuf, 0, tagline.buf,
- tagline.len);
- strbuf_release(&tagline);
- }
- strbuf_addch(&tagbuf, '\n');
- strbuf_add_commented_lines(&tagbuf,
- origins.items[i].string,
- strlen(origins.items[i].string));
- fmt_tag_signature(&tagbuf, &sig, buf, len);
- }
- strbuf_release(&sig);
- next:
- free(buf);
- }
- if (tagbuf.len) {
- strbuf_addch(out, '\n');
- strbuf_addbuf(out, &tagbuf);
- }
- strbuf_release(&tagbuf);
-}
-
-static void find_merge_parents(struct merge_parents *result,
- struct strbuf *in, struct object_id *head)
-{
- struct commit_list *parents;
- struct commit *head_commit;
- int pos = 0, i, j;
-
- parents = NULL;
- while (pos < in->len) {
- int len;
- char *p = in->buf + pos;
- char *newline = strchr(p, '\n');
- const char *q;
- struct object_id oid;
- struct commit *parent;
- struct object *obj;
-
- len = newline ? newline - p : strlen(p);
- pos += len + !!newline;
-
- if (parse_oid_hex(p, &oid, &q) ||
- q[0] != '\t' ||
- q[1] != '\t')
- continue; /* skip not-for-merge */
- /*
- * Do not use get_merge_parent() here; we do not have
- * "name" here and we do not want to contaminate its
- * util field yet.
- */
- obj = parse_object(the_repository, &oid);
- parent = (struct commit *)peel_to_type(NULL, 0, obj, OBJ_COMMIT);
- if (!parent)
- continue;
- commit_list_insert(parent, &parents);
- add_merge_parent(result, &obj->oid, &parent->object.oid);
- }
- head_commit = lookup_commit(the_repository, head);
- if (head_commit)
- commit_list_insert(head_commit, &parents);
- reduce_heads_replace(&parents);
-
- while (parents) {
- struct commit *cmit = pop_commit(&parents);
- for (i = 0; i < result->nr; i++)
- if (oideq(&result->item[i].commit, &cmit->object.oid))
- result->item[i].used = 1;
- }
-
- for (i = j = 0; i < result->nr; i++) {
- if (result->item[i].used) {
- if (i != j)
- result->item[j] = result->item[i];
- j++;
- }
- }
- result->nr = j;
-}
-
-int fmt_merge_msg(struct strbuf *in, struct strbuf *out,
- struct fmt_merge_msg_opts *opts)
-{
- int i = 0, pos = 0;
- struct object_id head_oid;
- const char *current_branch;
- void *current_branch_to_free;
- struct merge_parents merge_parents;
-
- memset(&merge_parents, 0, sizeof(merge_parents));
-
- /* get current branch */
- 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/"))
- current_branch += 11;
-
- find_merge_parents(&merge_parents, in, &head_oid);
-
- /* get a line */
- while (pos < in->len) {
- int len;
- char *newline, *p = in->buf + pos;
-
- newline = strchr(p, '\n');
- len = newline ? newline - p : strlen(p);
- pos += len + !!newline;
- i++;
- p[len] = 0;
- if (handle_line(p, &merge_parents))
- die("error in line %d: %.*s", i, len, p);
- }
-
- if (opts->add_title && srcs.nr)
- fmt_merge_msg_title(out, current_branch);
-
- if (origins.nr)
- fmt_merge_msg_sigs(out);
-
- if (opts->shortlog_len) {
- struct commit *head;
- struct rev_info rev;
-
- head = lookup_commit_or_die(&head_oid, "HEAD");
- repo_init_revisions(the_repository, &rev, NULL);
- rev.commit_format = CMIT_FMT_ONELINE;
- rev.ignore_merges = 1;
- rev.limited = 1;
-
- strbuf_complete_line(out);
-
- for (i = 0; i < origins.nr; i++)
- shortlog(origins.items[i].string,
- origins.items[i].util,
- head, &rev, opts, out);
- }
-
- strbuf_complete_line(out);
- free(current_branch_to_free);
- free(merge_parents.item);
- return 0;
-}
-
int cmd_fmt_merge_msg(int argc, const char **argv, const char *prefix)
{
const char *inpath = NULL;
diff --git a/builtin/for-each-ref.c b/builtin/for-each-ref.c
index 465153e..57489e4 100644
--- a/builtin/for-each-ref.c
+++ b/builtin/for-each-ref.c
@@ -70,7 +70,7 @@
if (!sorting)
sorting = ref_default_sorting();
- sorting->ignore_case = icase;
+ ref_sorting_icase_all(sorting, icase);
filter.ignore_case = icase;
filter.name_patterns = argv;
diff --git a/builtin/fsck.c b/builtin/fsck.c
index 8d13794..f02cbdb 100644
--- a/builtin/fsck.c
+++ b/builtin/fsck.c
@@ -49,6 +49,7 @@
#define ERROR_PACK 04
#define ERROR_REFS 010
#define ERROR_COMMIT_GRAPH 020
+#define ERROR_MULTI_PACK_INDEX 040
static const char *describe_object(const struct object_id *oid)
{
@@ -952,7 +953,7 @@
midx_argv[2] = "--object-dir";
midx_argv[3] = odb->path;
if (run_command(&midx_verify))
- errors_found |= ERROR_COMMIT_GRAPH;
+ errors_found |= ERROR_MULTI_PACK_INDEX;
}
}
diff --git a/builtin/grep.c b/builtin/grep.c
index 99e2685..a5056f3 100644
--- a/builtin/grep.c
+++ b/builtin/grep.c
@@ -295,6 +295,38 @@
return st;
}
+static void grep_source_name(struct grep_opt *opt, const char *filename,
+ int tree_name_len, struct strbuf *out)
+{
+ strbuf_reset(out);
+
+ if (opt->null_following_name) {
+ if (opt->relative && opt->prefix_length) {
+ struct strbuf rel_buf = STRBUF_INIT;
+ const char *rel_name =
+ relative_path(filename + tree_name_len,
+ opt->prefix, &rel_buf);
+
+ if (tree_name_len)
+ strbuf_add(out, filename, tree_name_len);
+
+ strbuf_addstr(out, rel_name);
+ strbuf_release(&rel_buf);
+ } else {
+ strbuf_addstr(out, filename);
+ }
+ return;
+ }
+
+ if (opt->relative && opt->prefix_length)
+ quote_path_relative(filename + tree_name_len, opt->prefix, out);
+ else
+ quote_c_style(filename + tree_name_len, out, NULL, 0);
+
+ if (tree_name_len)
+ strbuf_insert(out, 0, filename, tree_name_len);
+}
+
static int grep_oid(struct grep_opt *opt, const struct object_id *oid,
const char *filename, int tree_name_len,
const char *path)
@@ -302,13 +334,7 @@
struct strbuf pathbuf = STRBUF_INIT;
struct grep_source gs;
- if (opt->relative && opt->prefix_length) {
- quote_path_relative(filename + tree_name_len, opt->prefix, &pathbuf);
- strbuf_insert(&pathbuf, 0, filename, tree_name_len);
- } else {
- strbuf_addstr(&pathbuf, filename);
- }
-
+ grep_source_name(opt, filename, tree_name_len, &pathbuf);
grep_source_init(&gs, GREP_SOURCE_OID, pathbuf.buf, path, oid);
strbuf_release(&pathbuf);
@@ -334,11 +360,7 @@
struct strbuf buf = STRBUF_INIT;
struct grep_source gs;
- if (opt->relative && opt->prefix_length)
- quote_path_relative(filename, opt->prefix, &buf);
- else
- strbuf_addstr(&buf, filename);
-
+ grep_source_name(opt, filename, 0, &buf);
grep_source_init(&gs, GREP_SOURCE_FILE, buf.buf, filename, filename);
strbuf_release(&buf);
@@ -679,8 +701,6 @@
fill_directory(&dir, opt->repo->index, pathspec);
for (i = 0; i < dir.nr; i++) {
- if (!dir_path_match(opt->repo->index, dir.entries[i], pathspec, 0, NULL))
- continue;
hit |= grep_file(opt, dir.entries[i]->name);
if (hit && opt->status_only)
break;
@@ -886,20 +906,20 @@
OPT_GROUP(""),
OPT_CALLBACK('f', NULL, &opt, N_("file"),
N_("read patterns from file"), file_callback),
- { OPTION_CALLBACK, 'e', NULL, &opt, N_("pattern"),
- N_("match <pattern>"), PARSE_OPT_NONEG, pattern_callback },
- { OPTION_CALLBACK, 0, "and", &opt, NULL,
- N_("combine patterns specified with -e"),
- PARSE_OPT_NOARG | PARSE_OPT_NONEG, and_callback },
+ OPT_CALLBACK_F('e', NULL, &opt, N_("pattern"),
+ N_("match <pattern>"), PARSE_OPT_NONEG, pattern_callback),
+ OPT_CALLBACK_F(0, "and", &opt, NULL,
+ N_("combine patterns specified with -e"),
+ PARSE_OPT_NOARG | PARSE_OPT_NONEG, and_callback),
OPT_BOOL(0, "or", &dummy, ""),
- { OPTION_CALLBACK, 0, "not", &opt, NULL, "",
- PARSE_OPT_NOARG | PARSE_OPT_NONEG, not_callback },
- { OPTION_CALLBACK, '(', NULL, &opt, NULL, "",
- PARSE_OPT_NOARG | PARSE_OPT_NONEG | PARSE_OPT_NODASH,
- open_callback },
- { OPTION_CALLBACK, ')', NULL, &opt, NULL, "",
- PARSE_OPT_NOARG | PARSE_OPT_NONEG | PARSE_OPT_NODASH,
- close_callback },
+ OPT_CALLBACK_F(0, "not", &opt, NULL, "",
+ PARSE_OPT_NOARG | PARSE_OPT_NONEG, not_callback),
+ OPT_CALLBACK_F('(', NULL, &opt, NULL, "",
+ PARSE_OPT_NOARG | PARSE_OPT_NONEG | PARSE_OPT_NODASH,
+ open_callback),
+ OPT_CALLBACK_F(')', NULL, &opt, NULL, "",
+ PARSE_OPT_NOARG | PARSE_OPT_NONEG | PARSE_OPT_NODASH,
+ close_callback),
OPT__QUIET(&opt.status_only,
N_("indicate hit with exit status without output")),
OPT_BOOL(0, "all-match", &opt.all_match,
diff --git a/builtin/help.c b/builtin/help.c
index e5590d7..299206e 100644
--- a/builtin/help.c
+++ b/builtin/help.c
@@ -8,6 +8,7 @@
#include "parse-options.h"
#include "run-command.h"
#include "column.h"
+#include "config-list.h"
#include "help.h"
#include "alias.h"
@@ -62,6 +63,91 @@
NULL
};
+struct slot_expansion {
+ const char *prefix;
+ const char *placeholder;
+ void (*fn)(struct string_list *list, const char *prefix);
+ int found;
+};
+
+static void list_config_help(int for_human)
+{
+ struct slot_expansion slot_expansions[] = {
+ { "advice", "*", list_config_advices },
+ { "color.branch", "<slot>", list_config_color_branch_slots },
+ { "color.decorate", "<slot>", list_config_color_decorate_slots },
+ { "color.diff", "<slot>", list_config_color_diff_slots },
+ { "color.grep", "<slot>", list_config_color_grep_slots },
+ { "color.interactive", "<slot>", list_config_color_interactive_slots },
+ { "color.remote", "<slot>", list_config_color_sideband_slots },
+ { "color.status", "<slot>", list_config_color_status_slots },
+ { "fsck", "<msg-id>", list_config_fsck_msg_ids },
+ { "receive.fsck", "<msg-id>", list_config_fsck_msg_ids },
+ { NULL, NULL, NULL }
+ };
+ const char **p;
+ struct slot_expansion *e;
+ struct string_list keys = STRING_LIST_INIT_DUP;
+ int i;
+
+ for (p = config_name_list; *p; p++) {
+ const char *var = *p;
+ struct strbuf sb = STRBUF_INIT;
+
+ for (e = slot_expansions; e->prefix; e++) {
+
+ strbuf_reset(&sb);
+ strbuf_addf(&sb, "%s.%s", e->prefix, e->placeholder);
+ if (!strcasecmp(var, sb.buf)) {
+ e->fn(&keys, e->prefix);
+ e->found++;
+ break;
+ }
+ }
+ strbuf_release(&sb);
+ if (!e->prefix)
+ string_list_append(&keys, var);
+ }
+
+ for (e = slot_expansions; e->prefix; e++)
+ if (!e->found)
+ BUG("slot_expansion %s.%s is not used",
+ e->prefix, e->placeholder);
+
+ string_list_sort(&keys);
+ for (i = 0; i < keys.nr; i++) {
+ const char *var = keys.items[i].string;
+ const char *wildcard, *tag, *cut;
+
+ if (for_human) {
+ puts(var);
+ continue;
+ }
+
+ wildcard = strchr(var, '*');
+ tag = strchr(var, '<');
+
+ if (!wildcard && !tag) {
+ puts(var);
+ continue;
+ }
+
+ if (wildcard && !tag)
+ cut = wildcard;
+ else if (!wildcard && tag)
+ cut = tag;
+ else
+ cut = wildcard < tag ? wildcard : tag;
+
+ /*
+ * We may produce duplicates, but that's up to
+ * git-completion.bash to handle
+ */
+ printf("%.*s\n", (int)(cut - var), var);
+ }
+ string_list_clear(&keys, 0);
+}
+
static enum help_format parse_help_format(const char *format)
{
if (!strcmp(format, "man"))
@@ -242,7 +328,7 @@
static int add_man_viewer_info(const char *var, const char *value)
{
const char *name, *subkey;
- int namelen;
+ size_t namelen;
if (parse_config_key(var, "man", &name, &namelen, &subkey) < 0 || !name)
return 0;
diff --git a/builtin/index-pack.c b/builtin/index-pack.c
index d967d18..f176dd2 100644
--- a/builtin/index-pack.c
+++ b/builtin/index-pack.c
@@ -1368,9 +1368,8 @@
continue;
oid_array_append(&to_fetch, &d->oid);
}
- if (to_fetch.nr)
- promisor_remote_get_direct(the_repository,
- to_fetch.oid, to_fetch.nr);
+ promisor_remote_get_direct(the_repository,
+ to_fetch.oid, to_fetch.nr);
oid_array_clear(&to_fetch);
}
diff --git a/builtin/init-db.c b/builtin/init-db.c
index 944ec77..0b7222e 100644
--- a/builtin/init-db.c
+++ b/builtin/init-db.c
@@ -20,6 +20,8 @@
#define TEST_FILEMODE 1
#endif
+#define GIT_DEFAULT_HASH_ENVIRONMENT "GIT_DEFAULT_HASH"
+
static int init_is_bare_repository = 0;
static int init_shared_repository = -1;
static const char *init_db_template_dir;
@@ -176,13 +178,36 @@
return 1;
}
+void initialize_repository_version(int hash_algo)
+{
+ char repo_version_string[10];
+ int repo_version = GIT_REPO_VERSION;
+
+#ifndef ENABLE_SHA256
+ if (hash_algo != GIT_HASH_SHA1)
+ die(_("The hash algorithm %s is not supported in this build."), hash_algos[hash_algo].name);
+#endif
+
+ if (hash_algo != GIT_HASH_SHA1)
+ repo_version = GIT_REPO_VERSION_READ;
+
+ /* This forces creation of new config file */
+ xsnprintf(repo_version_string, sizeof(repo_version_string),
+ "%d", repo_version);
+ git_config_set("core.repositoryformatversion", repo_version_string);
+
+ if (hash_algo != GIT_HASH_SHA1)
+ git_config_set("extensions.objectformat",
+ hash_algos[hash_algo].name);
+}
+
static int create_default_files(const char *template_path,
- const char *original_git_dir)
+ const char *original_git_dir,
+ const struct repository_format *fmt)
{
struct stat st1;
struct strbuf buf = STRBUF_INIT;
char *path;
- char repo_version_string[10];
char junk[2];
int reinit;
int filemode;
@@ -244,10 +269,7 @@
exit(1);
}
- /* This forces creation of new config file */
- xsnprintf(repo_version_string, sizeof(repo_version_string),
- "%d", GIT_REPO_VERSION);
- git_config_set("core.repositoryformatversion", repo_version_string);
+ initialize_repository_version(fmt->hash_algo);
/* Check filemode trustability */
path = git_path_buf(&buf, "config");
@@ -340,12 +362,33 @@
write_file(git_link, "gitdir: %s", git_dir);
}
+static void validate_hash_algorithm(struct repository_format *repo_fmt, int hash)
+{
+ const char *env = getenv(GIT_DEFAULT_HASH_ENVIRONMENT);
+ /*
+ * If we already have an initialized repo, don't allow the user to
+ * specify a different algorithm, as that could cause corruption.
+ * Otherwise, if the user has specified one on the command line, use it.
+ */
+ if (repo_fmt->version >= 0 && hash != GIT_HASH_UNKNOWN && hash != repo_fmt->hash_algo)
+ die(_("attempt to reinitialize repository with different hash"));
+ else if (hash != GIT_HASH_UNKNOWN)
+ repo_fmt->hash_algo = hash;
+ else if (env) {
+ int env_algo = hash_algo_by_name(env);
+ if (env_algo == GIT_HASH_UNKNOWN)
+ die(_("unknown hash algorithm '%s'"), env);
+ repo_fmt->hash_algo = env_algo;
+ }
+}
+
int init_db(const char *git_dir, const char *real_git_dir,
- const char *template_dir, unsigned int flags)
+ const char *template_dir, int hash, unsigned int flags)
{
int reinit;
int exist_ok = flags & INIT_DB_EXIST_OK;
char *original_git_dir = real_pathdup(git_dir, 1);
+ struct repository_format repo_fmt = REPOSITORY_FORMAT_INIT;
if (real_git_dir) {
struct stat st;
@@ -356,12 +399,12 @@
if (!exist_ok && !stat(real_git_dir, &st))
die(_("%s already exists"), real_git_dir);
- set_git_dir(real_path(real_git_dir));
+ set_git_dir(real_git_dir, 1);
git_dir = get_git_dir();
separate_git_dir(git_dir, original_git_dir);
}
else {
- set_git_dir(real_path(git_dir));
+ set_git_dir(git_dir, 1);
git_dir = get_git_dir();
}
startup_info->have_repository = 1;
@@ -378,9 +421,11 @@
* config file, so this will not fail. What we are catching
* is an attempt to reinitialize new repository with an old tool.
*/
- check_repository_format();
+ check_repository_format(&repo_fmt);
- reinit = create_default_files(template_dir, original_git_dir);
+ validate_hash_algorithm(&repo_fmt, hash);
+
+ reinit = create_default_files(template_dir, original_git_dir, &repo_fmt);
create_object_directory();
@@ -482,6 +527,8 @@
const char *work_tree;
const char *template_dir = NULL;
unsigned int flags = 0;
+ const char *object_format = NULL;
+ int hash_algo = GIT_HASH_UNKNOWN;
const struct option init_db_options[] = {
OPT_STRING(0, "template", &template_dir, N_("template-directory"),
N_("directory from which templates will be used")),
@@ -494,6 +541,8 @@
OPT_BIT('q', "quiet", &flags, N_("be quiet"), INIT_DB_QUIET),
OPT_STRING(0, "separate-git-dir", &real_git_dir, N_("gitdir"),
N_("separate git dir from working tree")),
+ OPT_STRING(0, "object-format", &object_format, N_("hash"),
+ N_("specify the hash algorithm to use")),
OPT_END()
};
@@ -546,6 +595,12 @@
free(cwd);
}
+ if (object_format) {
+ hash_algo = hash_algo_by_name(object_format);
+ if (hash_algo == GIT_HASH_UNKNOWN)
+ die(_("unknown hash algorithm '%s'"), object_format);
+ }
+
if (init_shared_repository != -1)
set_shared_repository(init_shared_repository);
@@ -597,5 +652,5 @@
UNLEAK(work_tree);
flags |= INIT_DB_EXIST_OK;
- return init_db(git_dir, real_git_dir, template_dir, flags);
+ return init_db(git_dir, real_git_dir, template_dir, hash_algo, flags);
}
diff --git a/builtin/interpret-trailers.c b/builtin/interpret-trailers.c
index f101d09..84748ea 100644
--- a/builtin/interpret-trailers.c
+++ b/builtin/interpret-trailers.c
@@ -105,8 +105,8 @@
OPT_BOOL(0, "only-trailers", &opts.only_trailers, N_("output only the trailers")),
OPT_BOOL(0, "only-input", &opts.only_input, N_("do not apply config rules")),
OPT_BOOL(0, "unfold", &opts.unfold, N_("join whitespace-continued values")),
- { OPTION_CALLBACK, 0, "parse", &opts, NULL, N_("set parsing options"),
- PARSE_OPT_NOARG | PARSE_OPT_NONEG, parse_opt_parse },
+ OPT_CALLBACK_F(0, "parse", &opts, NULL, N_("set parsing options"),
+ PARSE_OPT_NOARG | PARSE_OPT_NONEG, parse_opt_parse),
OPT_BOOL(0, "no-divider", &opts.no_divider, N_("do not treat --- specially")),
OPT_CALLBACK(0, "trailer", &trailers, N_("trailer"),
N_("trailer(s) to add"), option_parse_trailer),
diff --git a/builtin/log.c b/builtin/log.c
index 83a4a61..d104d5c 100644
--- a/builtin/log.c
+++ b/builtin/log.c
@@ -46,6 +46,7 @@
static int default_show_root = 1;
static int default_follow;
static int default_show_signature;
+static int default_encode_email_headers = 1;
static int decoration_style;
static int decoration_given;
static int use_mailmap_config = 1;
@@ -151,6 +152,7 @@
rev->show_root_diff = default_show_root;
rev->subject_prefix = fmt_patch_subject_prefix;
rev->show_signature = default_show_signature;
+ rev->encode_email_headers = default_encode_email_headers;
rev->diffopt.flags.allow_textconv = 1;
if (default_date_mode)
@@ -164,21 +166,24 @@
int quiet = 0, source = 0, mailmap;
static struct line_opt_callback_data line_cb = {NULL, NULL, STRING_LIST_INIT_DUP};
static struct string_list decorate_refs_exclude = STRING_LIST_INIT_NODUP;
+ static struct string_list decorate_refs_exclude_config = STRING_LIST_INIT_NODUP;
static struct string_list decorate_refs_include = STRING_LIST_INIT_NODUP;
struct decoration_filter decoration_filter = {&decorate_refs_include,
- &decorate_refs_exclude};
+ &decorate_refs_exclude,
+ &decorate_refs_exclude_config};
static struct revision_sources revision_sources;
const struct option builtin_log_options[] = {
OPT__QUIET(&quiet, N_("suppress diff output")),
OPT_BOOL(0, "source", &source, N_("show source")),
OPT_BOOL(0, "use-mailmap", &mailmap, N_("Use mail map file")),
+ OPT_ALIAS(0, "mailmap", "use-mailmap"),
OPT_STRING_LIST(0, "decorate-refs", &decorate_refs_include,
N_("pattern"), N_("only decorate refs that match <pattern>")),
OPT_STRING_LIST(0, "decorate-refs-exclude", &decorate_refs_exclude,
N_("pattern"), N_("do not decorate refs that match <pattern>")),
- { OPTION_CALLBACK, 0, "decorate", NULL, NULL, N_("decorate options"),
- PARSE_OPT_OPTARG, decorate_callback},
+ OPT_CALLBACK_F(0, "decorate", NULL, NULL, N_("decorate options"),
+ PARSE_OPT_OPTARG, decorate_callback),
OPT_CALLBACK('L', NULL, &line_cb, "n,m:file",
N_("Process line range n,m in file, counting from 1"),
log_line_range_callback),
@@ -236,7 +241,19 @@
}
if (decoration_style) {
+ const struct string_list *config_exclude =
+ repo_config_get_value_multi(the_repository,
+ "log.excludeDecoration");
+
+ if (config_exclude) {
+ struct string_list_item *item;
+ for_each_string_list_item(item, config_exclude)
+ string_list_append(&decorate_refs_exclude_config,
+ item->string);
+ }
+
rev->show_decorations = 1;
+
load_ref_decorations(&decoration_filter, decoration_style);
}
@@ -438,6 +455,10 @@
return git_config_string(&fmt_pretty, var, value);
if (!strcmp(var, "format.subjectprefix"))
return git_config_string(&fmt_patch_subject_prefix, var, value);
+ if (!strcmp(var, "format.encodeemailheaders")) {
+ default_encode_email_headers = git_config_bool(var, value);
+ return 0;
+ }
if (!strcmp(var, "log.abbrevcommit")) {
default_abbrev_commit = git_config_bool(var, value);
return 0;
@@ -1625,12 +1646,12 @@
int creation_factor = -1;
const struct option builtin_format_patch_options[] = {
- { OPTION_CALLBACK, 'n', "numbered", &numbered, NULL,
+ OPT_CALLBACK_F('n', "numbered", &numbered, NULL,
N_("use [PATCH n/m] even with a single patch"),
- PARSE_OPT_NOARG, numbered_callback },
- { OPTION_CALLBACK, 'N', "no-numbered", &numbered, NULL,
+ PARSE_OPT_NOARG, numbered_callback),
+ OPT_CALLBACK_F('N', "no-numbered", &numbered, NULL,
N_("use [PATCH] even with multiple patches"),
- PARSE_OPT_NOARG | PARSE_OPT_NONEG, no_numbered_callback },
+ PARSE_OPT_NOARG | PARSE_OPT_NONEG, no_numbered_callback),
OPT_BOOL('s', "signoff", &do_signoff, N_("add Signed-off-by:")),
OPT_BOOL(0, "stdout", &use_stdout,
N_("print patches to standard out")),
@@ -1644,21 +1665,21 @@
N_("start numbering patches at <n> instead of 1")),
OPT_INTEGER('v', "reroll-count", &reroll_count,
N_("mark the series as Nth re-roll")),
- { OPTION_CALLBACK, 0, "rfc", &rev, NULL,
+ OPT_CALLBACK_F(0, "rfc", &rev, NULL,
N_("Use [RFC PATCH] instead of [PATCH]"),
- PARSE_OPT_NOARG | PARSE_OPT_NONEG, rfc_callback },
+ PARSE_OPT_NOARG | PARSE_OPT_NONEG, rfc_callback),
OPT_STRING(0, "cover-from-description", &cover_from_description_arg,
N_("cover-from-description-mode"),
N_("generate parts of a cover letter based on a branch's description")),
- { OPTION_CALLBACK, 0, "subject-prefix", &rev, N_("prefix"),
+ OPT_CALLBACK_F(0, "subject-prefix", &rev, N_("prefix"),
N_("Use [<prefix>] instead of [PATCH]"),
- PARSE_OPT_NONEG, subject_prefix_callback },
- { OPTION_CALLBACK, 'o', "output-directory", &output_directory,
+ PARSE_OPT_NONEG, subject_prefix_callback),
+ OPT_CALLBACK_F('o', "output-directory", &output_directory,
N_("dir"), N_("store resulting files in <dir>"),
- PARSE_OPT_NONEG, output_directory_callback },
- { OPTION_CALLBACK, 'k', "keep-subject", &rev, NULL,
+ PARSE_OPT_NONEG, output_directory_callback),
+ OPT_CALLBACK_F('k', "keep-subject", &rev, NULL,
N_("don't strip/add [PATCH]"),
- PARSE_OPT_NOARG | PARSE_OPT_NONEG, keep_callback },
+ PARSE_OPT_NOARG | PARSE_OPT_NONEG, keep_callback),
OPT_BOOL(0, "no-binary", &no_binary_diff,
N_("don't output binary diffs")),
OPT_BOOL(0, "zero-commit", &zero_commit,
@@ -1669,27 +1690,25 @@
N_("show patch format instead of default (patch + stat)"),
1, PARSE_OPT_NONEG),
OPT_GROUP(N_("Messaging")),
- { OPTION_CALLBACK, 0, "add-header", NULL, N_("header"),
- N_("add email header"), 0, header_callback },
- { OPTION_CALLBACK, 0, "to", NULL, N_("email"), N_("add To: header"),
- 0, to_callback },
- { OPTION_CALLBACK, 0, "cc", NULL, N_("email"), N_("add Cc: header"),
- 0, cc_callback },
- { OPTION_CALLBACK, 0, "from", &from, N_("ident"),
+ OPT_CALLBACK(0, "add-header", NULL, N_("header"),
+ N_("add email header"), header_callback),
+ OPT_CALLBACK(0, "to", NULL, N_("email"), N_("add To: header"), to_callback),
+ OPT_CALLBACK(0, "cc", NULL, N_("email"), N_("add Cc: header"), cc_callback),
+ OPT_CALLBACK_F(0, "from", &from, N_("ident"),
N_("set From address to <ident> (or committer ident if absent)"),
- PARSE_OPT_OPTARG, from_callback },
+ PARSE_OPT_OPTARG, from_callback),
OPT_STRING(0, "in-reply-to", &in_reply_to, N_("message-id"),
N_("make first mail a reply to <message-id>")),
- { OPTION_CALLBACK, 0, "attach", &rev, N_("boundary"),
+ OPT_CALLBACK_F(0, "attach", &rev, N_("boundary"),
N_("attach the patch"), PARSE_OPT_OPTARG,
- attach_callback },
- { OPTION_CALLBACK, 0, "inline", &rev, N_("boundary"),
+ attach_callback),
+ OPT_CALLBACK_F(0, "inline", &rev, N_("boundary"),
N_("inline the patch"),
PARSE_OPT_OPTARG | PARSE_OPT_NONEG,
- inline_callback },
- { OPTION_CALLBACK, 0, "thread", &thread, N_("style"),
+ inline_callback),
+ OPT_CALLBACK_F(0, "thread", &thread, N_("style"),
N_("enable message threading, styles: shallow, deep"),
- PARSE_OPT_OPTARG, thread_callback },
+ PARSE_OPT_OPTARG, thread_callback),
OPT_STRING(0, "signature", &signature, N_("signature"),
N_("add a signature")),
OPT_STRING(0, "base", &base_commit, N_("base-commit"),
@@ -1719,6 +1738,7 @@
rev.show_notes = show_notes;
memcpy(&rev.notes_opt, ¬es_opt, sizeof(notes_opt));
rev.commit_format = CMIT_FMT_EMAIL;
+ rev.encode_email_headers = default_encode_email_headers;
rev.expand_tabs_in_log_default = 0;
rev.verbose_header = 1;
rev.diff = 1;
diff --git a/builtin/ls-files.c b/builtin/ls-files.c
index f069a02..30a4c10 100644
--- a/builtin/ls-files.c
+++ b/builtin/ls-files.c
@@ -128,8 +128,9 @@
if (len > ent->len)
die("git ls-files: internal error - directory entry not superset of prefix");
- if (!dir_path_match(istate, ent, &pathspec, len, ps_matched))
- return;
+ /* If ps_matches is non-NULL, figure out which pathspec(s) match. */
+ if (ps_matched)
+ dir_path_match(istate, ent, &pathspec, len, ps_matched);
fputs(tag, stdout);
write_eolinfo(istate, NULL, ent->name);
@@ -554,18 +555,18 @@
N_("show unmerged files in the output")),
OPT_BOOL(0, "resolve-undo", &show_resolve_undo,
N_("show resolve-undo information")),
- { OPTION_CALLBACK, 'x', "exclude", &exclude_list, N_("pattern"),
+ OPT_CALLBACK_F('x', "exclude", &exclude_list, N_("pattern"),
N_("skip files matching pattern"),
- PARSE_OPT_NONEG, option_parse_exclude },
- { OPTION_CALLBACK, 'X', "exclude-from", &dir, N_("file"),
+ PARSE_OPT_NONEG, option_parse_exclude),
+ OPT_CALLBACK_F('X', "exclude-from", &dir, N_("file"),
N_("exclude patterns are read from <file>"),
- PARSE_OPT_NONEG, option_parse_exclude_from },
+ PARSE_OPT_NONEG, option_parse_exclude_from),
OPT_STRING(0, "exclude-per-directory", &dir.exclude_per_dir, N_("file"),
N_("read additional per-directory exclude patterns in <file>")),
- { OPTION_CALLBACK, 0, "exclude-standard", &dir, NULL,
+ OPT_CALLBACK_F(0, "exclude-standard", &dir, NULL,
N_("add the standard git exclusions"),
PARSE_OPT_NOARG | PARSE_OPT_NONEG,
- option_parse_exclude_standard },
+ option_parse_exclude_standard),
OPT_SET_INT_F(0, "full-name", &prefix_len,
N_("make the output relative to the project top directory"),
0, PARSE_OPT_NONEG),
diff --git a/builtin/merge-base.c b/builtin/merge-base.c
index e3f8da1..6719ac1 100644
--- a/builtin/merge-base.c
+++ b/builtin/merge-base.c
@@ -114,26 +114,16 @@
static int handle_fork_point(int argc, const char **argv)
{
struct object_id oid;
- char *refname;
struct commit *derived, *fork_point;
const char *commitname;
- switch (dwim_ref(argv[0], strlen(argv[0]), &oid, &refname)) {
- case 0:
- die("No such ref: '%s'", argv[0]);
- case 1:
- break; /* good */
- default:
- die("Ambiguous refname: '%s'", argv[0]);
- }
-
commitname = (argc == 2) ? argv[1] : "HEAD";
if (get_oid(commitname, &oid))
die("Not a valid object name: '%s'", commitname);
derived = lookup_commit_reference(the_repository, &oid);
- fork_point = get_fork_point(refname, derived);
+ fork_point = get_fork_point(argv[0], derived);
if (!fork_point)
return 1;
diff --git a/builtin/merge.c b/builtin/merge.c
index d127d22..ca6a5dc 100644
--- a/builtin/merge.c
+++ b/builtin/merge.c
@@ -40,6 +40,7 @@
#include "branch.h"
#include "commit-reach.h"
#include "wt-status.h"
+#include "commit-graph.h"
#define DEFAULT_TWOHEAD (1<<0)
#define DEFAULT_OCTOPUS (1<<1)
@@ -82,6 +83,7 @@
static int default_to_upstream = 1;
static int signoff;
static const char *sign_commit;
+static int autostash;
static int no_verify;
static struct strategy all_strategy[] = {
@@ -241,9 +243,9 @@
}
static struct option builtin_merge_options[] = {
- { OPTION_CALLBACK, 'n', NULL, NULL, NULL,
+ OPT_CALLBACK_F('n', NULL, NULL, NULL,
N_("do not show a diffstat at the end of the merge"),
- PARSE_OPT_NOARG, option_parse_n },
+ PARSE_OPT_NOARG, option_parse_n),
OPT_BOOL(0, "stat", &show_diffstat,
N_("show a diffstat at the end of the merge")),
OPT_BOOL(0, "summary", &show_diffstat, N_("(synonym to --stat)")),
@@ -286,6 +288,7 @@
OPT_SET_INT(0, "progress", &show_progress, N_("force progress reporting"), 1),
{ OPTION_STRING, 'S', "gpg-sign", &sign_commit, N_("key-id"),
N_("GPG sign commit"), PARSE_OPT_OPTARG, NULL, (intptr_t) "" },
+ OPT_AUTOSTASH(&autostash),
OPT_BOOL(0, "overwrite-ignore", &overwrite_ignore, N_("update ignored files (default)")),
OPT_BOOL(0, "signoff", &signoff, N_("add Signed-off-by:")),
OPT_BOOL(0, "no-verify", &no_verify, N_("bypass pre-merge-commit and commit-msg hooks")),
@@ -447,7 +450,6 @@
if (verbosity >= 0 && !merge_msg.len)
printf(_("No merge message -- not updating HEAD\n"));
else {
- const char *argv_gc_auto[] = { "gc", "--auto", NULL };
update_ref(reflog_message.buf, "HEAD", new_head, head,
0, UPDATE_REFS_DIE_ON_ERR);
/*
@@ -455,7 +457,7 @@
* user should see them.
*/
close_object_store(the_repository->objects);
- run_command_v_opt(argv_gc_auto, RUN_GIT_CMD);
+ run_auto_gc(verbosity < 0);
}
}
if (new_head && show_diffstat) {
@@ -475,6 +477,7 @@
/* Run a post-merge hook */
run_hook_le(NULL, "post-merge", squash ? "1" : "0", NULL);
+ apply_autostash(git_path_merge_autostash(the_repository));
strbuf_release(&reflog_message);
}
@@ -597,10 +600,12 @@
static int git_merge_config(const char *k, const char *v, void *cb)
{
int status;
+ const char *str;
- if (branch && starts_with(k, "branch.") &&
- starts_with(k + 7, branch) &&
- !strcmp(k + 7 + strlen(branch), ".mergeoptions")) {
+ if (branch &&
+ skip_prefix(k, "branch.", &str) &&
+ skip_prefix(str, branch, &str) &&
+ !strcmp(str, ".mergeoptions")) {
free(branch_mergeoptions);
branch_mergeoptions = xstrdup(v);
return 0;
@@ -634,6 +639,9 @@
return 0;
} else if (!strcmp(k, "gpg.mintrustlevel")) {
check_trust_level = 0;
+ } else if (!strcmp(k, "merge.autostash")) {
+ autostash = git_config_bool(k, v);
+ return 0;
}
status = fmt_merge_msg_config(k, v, cb);
@@ -1281,6 +1289,7 @@
if (abort_current_merge) {
int nargc = 2;
const char *nargv[] = {"reset", "--merge", NULL};
+ struct strbuf stash_oid = STRBUF_INIT;
if (orig_argc != 2)
usage_msg_opt(_("--abort expects no arguments"),
@@ -1289,8 +1298,17 @@
if (!file_exists(git_path_merge_head(the_repository)))
die(_("There is no merge to abort (MERGE_HEAD missing)."));
+ if (read_oneliner(&stash_oid, git_path_merge_autostash(the_repository),
+ READ_ONELINER_SKIP_IF_EMPTY))
+ unlink(git_path_merge_autostash(the_repository));
+
/* Invoke 'git reset --merge' */
ret = cmd_reset(nargc, nargv, prefix);
+
+ if (stash_oid.len)
+ apply_autostash_oid(stash_oid.buf);
+
+ strbuf_release(&stash_oid);
goto done;
}
@@ -1513,6 +1531,10 @@
goto done;
}
+ if (autostash)
+ create_autostash(the_repository,
+ git_path_merge_autostash(the_repository),
+ "merge");
if (checkout_fast_forward(the_repository,
&head_commit->object.oid,
&commit->object.oid,
@@ -1579,6 +1601,11 @@
if (fast_forward == FF_ONLY)
die(_("Not possible to fast-forward, aborting."));
+ if (autostash)
+ create_autostash(the_repository,
+ git_path_merge_autostash(the_repository),
+ "merge");
+
/* We are going to make a new commit. */
git_committer_info(IDENT_STRICT);
@@ -1673,9 +1700,11 @@
head_commit);
}
- if (squash)
+ if (squash) {
finish(head_commit, remoteheads, NULL, NULL);
- else
+
+ git_test_write_commit_graph_or_die();
+ } else
write_merge_state(remoteheads);
if (merge_was_ok)
diff --git a/builtin/notes.c b/builtin/notes.c
index 35e468e..2987c08 100644
--- a/builtin/notes.c
+++ b/builtin/notes.c
@@ -406,18 +406,18 @@
const struct object_id *note;
struct note_data d = { 0, 0, NULL, STRBUF_INIT };
struct option options[] = {
- { OPTION_CALLBACK, 'm', "message", &d, N_("message"),
+ OPT_CALLBACK_F('m', "message", &d, N_("message"),
N_("note contents as a string"), PARSE_OPT_NONEG,
- parse_msg_arg},
- { OPTION_CALLBACK, 'F', "file", &d, N_("file"),
+ parse_msg_arg),
+ OPT_CALLBACK_F('F', "file", &d, N_("file"),
N_("note contents in a file"), PARSE_OPT_NONEG,
- parse_file_arg},
- { OPTION_CALLBACK, 'c', "reedit-message", &d, N_("object"),
+ parse_file_arg),
+ OPT_CALLBACK_F('c', "reedit-message", &d, N_("object"),
N_("reuse and edit specified note object"), PARSE_OPT_NONEG,
- parse_reedit_arg},
- { OPTION_CALLBACK, 'C', "reuse-message", &d, N_("object"),
+ parse_reedit_arg),
+ OPT_CALLBACK_F('C', "reuse-message", &d, N_("object"),
N_("reuse specified note object"), PARSE_OPT_NONEG,
- parse_reuse_arg},
+ parse_reuse_arg),
OPT_BOOL(0, "allow-empty", &allow_empty,
N_("allow storing empty note")),
OPT__FORCE(&force, N_("replace existing notes"), PARSE_OPT_NOCOMPLETE),
@@ -572,18 +572,18 @@
const char * const *usage;
struct note_data d = { 0, 0, NULL, STRBUF_INIT };
struct option options[] = {
- { OPTION_CALLBACK, 'm', "message", &d, N_("message"),
+ OPT_CALLBACK_F('m', "message", &d, N_("message"),
N_("note contents as a string"), PARSE_OPT_NONEG,
- parse_msg_arg},
- { OPTION_CALLBACK, 'F', "file", &d, N_("file"),
+ parse_msg_arg),
+ OPT_CALLBACK_F('F', "file", &d, N_("file"),
N_("note contents in a file"), PARSE_OPT_NONEG,
- parse_file_arg},
- { OPTION_CALLBACK, 'c', "reedit-message", &d, N_("object"),
+ parse_file_arg),
+ OPT_CALLBACK_F('c', "reedit-message", &d, N_("object"),
N_("reuse and edit specified note object"), PARSE_OPT_NONEG,
- parse_reedit_arg},
- { OPTION_CALLBACK, 'C', "reuse-message", &d, N_("object"),
+ parse_reedit_arg),
+ OPT_CALLBACK_F('C', "reuse-message", &d, N_("object"),
N_("reuse specified note object"), PARSE_OPT_NONEG,
- parse_reuse_arg},
+ parse_reuse_arg),
OPT_BOOL(0, "allow-empty", &allow_empty,
N_("allow storing empty note")),
OPT_END()
diff --git a/builtin/pack-objects.c b/builtin/pack-objects.c
index 02aa6ee..c5b433a 100644
--- a/builtin/pack-objects.c
+++ b/builtin/pack-objects.c
@@ -26,7 +26,7 @@
#include "pack-bitmap.h"
#include "delta-islands.h"
#include "reachable.h"
-#include "sha1-array.h"
+#include "oid-array.h"
#include "argv-array.h"
#include "list.h"
#include "packfile.h"
@@ -34,6 +34,7 @@
#include "dir.h"
#include "midx.h"
#include "trace2.h"
+#include "shallow.h"
#define IN_PACK(obj) oe_in_pack(&to_pack, obj)
#define SIZE(obj) oe_size(&to_pack, obj)
@@ -880,7 +881,7 @@
len = encode_in_pack_object_header(header, sizeof(header),
OBJ_REF_DELTA, size);
hashwrite(out, header, len);
- hashwrite(out, base_oid.hash, 20);
+ hashwrite(out, base_oid.hash, the_hash_algo->rawsz);
copy_pack_data(out, reuse_packfile, w_curs, cur, next - cur);
return;
}
@@ -3380,9 +3381,9 @@
OPT_BOOL(0, "all-progress-implied",
&all_progress_implied,
N_("similar to --all-progress when progress meter is shown")),
- { OPTION_CALLBACK, 0, "index-version", NULL, N_("<version>[,<offset>]"),
+ OPT_CALLBACK_F(0, "index-version", NULL, N_("<version>[,<offset>]"),
N_("write the pack index file in the specified idx format version"),
- PARSE_OPT_NONEG, option_parse_index_version },
+ PARSE_OPT_NONEG, option_parse_index_version),
OPT_MAGNITUDE(0, "max-pack-size", &pack_size_limit,
N_("maximum size of each output pack file")),
OPT_BOOL(0, "local", &local,
@@ -3427,9 +3428,9 @@
N_("keep unreachable objects")),
OPT_BOOL(0, "pack-loose-unreachable", &pack_loose_unreachable,
N_("pack loose unreachable objects")),
- { OPTION_CALLBACK, 0, "unpack-unreachable", NULL, N_("time"),
+ OPT_CALLBACK_F(0, "unpack-unreachable", NULL, N_("time"),
N_("unpack unreachable objects newer than <time>"),
- PARSE_OPT_OPTARG, option_parse_unpack_unreachable },
+ PARSE_OPT_OPTARG, option_parse_unpack_unreachable),
OPT_BOOL(0, "sparse", &sparse,
N_("use the sparse reachability algorithm")),
OPT_BOOL(0, "thin", &thin,
@@ -3454,9 +3455,9 @@
N_("write a bitmap index if possible"),
WRITE_BITMAP_QUIET, PARSE_OPT_HIDDEN),
OPT_PARSE_LIST_OBJECTS_FILTER(&filter_options),
- { OPTION_CALLBACK, 0, "missing", NULL, N_("action"),
+ OPT_CALLBACK_F(0, "missing", NULL, N_("action"),
N_("handling for missing objects"), PARSE_OPT_NONEG,
- option_parse_missing_action },
+ option_parse_missing_action),
OPT_BOOL(0, "exclude-promisor-objects", &exclude_promisor_objects,
N_("do not pack objects in promisor packfiles")),
OPT_BOOL(0, "delta-islands", &use_delta_islands,
@@ -3469,9 +3470,9 @@
read_replace_refs = 0;
- sparse = git_env_bool("GIT_TEST_PACK_SPARSE", 0);
+ sparse = git_env_bool("GIT_TEST_PACK_SPARSE", -1);
prepare_repo_settings(the_repository);
- if (!sparse && the_repository->settings.pack_use_sparse != -1)
+ if (sparse < 0)
sparse = the_repository->settings.pack_use_sparse;
reset_pack_idx_option(&pack_idx_opts);
diff --git a/builtin/prune-packed.c b/builtin/prune-packed.c
index 48c5e78..b7b9281 100644
--- a/builtin/prune-packed.c
+++ b/builtin/prune-packed.c
@@ -1,54 +1,12 @@
#include "builtin.h"
-#include "cache.h"
-#include "progress.h"
#include "parse-options.h"
-#include "packfile.h"
-#include "object-store.h"
+#include "prune-packed.h"
static const char * const prune_packed_usage[] = {
N_("git prune-packed [-n | --dry-run] [-q | --quiet]"),
NULL
};
-static struct progress *progress;
-
-static int prune_subdir(unsigned int nr, const char *path, void *data)
-{
- int *opts = data;
- display_progress(progress, nr + 1);
- if (!(*opts & PRUNE_PACKED_DRY_RUN))
- rmdir(path);
- return 0;
-}
-
-static int prune_object(const struct object_id *oid, const char *path,
- void *data)
-{
- int *opts = data;
-
- if (!has_object_pack(oid))
- return 0;
-
- if (*opts & PRUNE_PACKED_DRY_RUN)
- printf("rm -f %s\n", path);
- else
- unlink_or_warn(path);
- return 0;
-}
-
-void prune_packed_objects(int opts)
-{
- if (opts & PRUNE_PACKED_VERBOSE)
- progress = start_delayed_progress(_("Removing duplicate objects"), 256);
-
- for_each_loose_file_in_objdir(get_object_directory(),
- prune_object, NULL, prune_subdir, &opts);
-
- /* Ensure we show 100% before finishing progress */
- display_progress(progress, 256);
- stop_progress(&progress);
-}
-
int cmd_prune_packed(int argc, const char **argv, const char *prefix)
{
int opts = isatty(2) ? PRUNE_PACKED_VERBOSE : 0;
diff --git a/builtin/prune.c b/builtin/prune.c
index 2b76872..02c6ab7 100644
--- a/builtin/prune.c
+++ b/builtin/prune.c
@@ -6,7 +6,9 @@
#include "reachable.h"
#include "parse-options.h"
#include "progress.h"
+#include "prune-packed.h"
#include "object-store.h"
+#include "shallow.h"
static const char * const prune_usage[] = {
N_("git prune [-n] [-v] [--progress] [--expire <time>] [--] [<head>...]"),
diff --git a/builtin/pull.c b/builtin/pull.c
index 3e624d1..00e5857 100644
--- a/builtin/pull.c
+++ b/builtin/pull.c
@@ -12,7 +12,7 @@
#include "parse-options.h"
#include "exec-cmd.h"
#include "run-command.h"
-#include "sha1-array.h"
+#include "oid-array.h"
#include "remote.h"
#include "dir.h"
#include "rebase.h"
@@ -110,6 +110,7 @@
static char *opt_ipv6;
static int opt_show_forced_updates = -1;
static char *set_upstream;
+static struct argv_array opt_fetch = ARGV_ARRAY_INIT;
static struct option pull_options[] = {
/* Shared options */
@@ -117,17 +118,17 @@
OPT_PASSTHRU(0, "progress", &opt_progress, NULL,
N_("force progress reporting"),
PARSE_OPT_NOARG),
- { OPTION_CALLBACK, 0, "recurse-submodules",
+ OPT_CALLBACK_F(0, "recurse-submodules",
&recurse_submodules, N_("on-demand"),
N_("control for recursive fetching of submodules"),
- PARSE_OPT_OPTARG, option_fetch_parse_recurse_submodules },
+ PARSE_OPT_OPTARG, option_fetch_parse_recurse_submodules),
/* Options passed to git-merge or git-rebase */
OPT_GROUP(N_("Options related to merging")),
- { OPTION_CALLBACK, 'r', "rebase", &opt_rebase,
+ OPT_CALLBACK_F('r', "rebase", &opt_rebase,
"(false|true|merges|preserve|interactive)",
N_("incorporate changes by rebasing rather than merging"),
- PARSE_OPT_OPTARG, parse_opt_rebase },
+ PARSE_OPT_OPTARG, parse_opt_rebase),
OPT_PASSTHRU('n', NULL, &opt_diffstat, NULL,
N_("do not show a diffstat at the end of the merge"),
PARSE_OPT_NOARG | PARSE_OPT_NONEG),
@@ -163,7 +164,7 @@
N_("verify that the named commit has a valid GPG signature"),
PARSE_OPT_NOARG),
OPT_BOOL(0, "autostash", &opt_autostash,
- N_("automatically stash/stash pop before and after rebase")),
+ N_("automatically stash/stash pop before and after")),
OPT_PASSTHRU_ARGV('s', "strategy", &opt_strategies, N_("strategy"),
N_("merge strategy to use"),
0),
@@ -207,6 +208,15 @@
OPT_PASSTHRU(0, "depth", &opt_depth, N_("depth"),
N_("deepen history of shallow clone"),
0),
+ OPT_PASSTHRU_ARGV(0, "shallow-since", &opt_fetch, N_("time"),
+ N_("deepen history of shallow repository based on time"),
+ 0),
+ OPT_PASSTHRU_ARGV(0, "shallow-exclude", &opt_fetch, N_("revision"),
+ N_("deepen history of shallow clone, excluding rev"),
+ 0),
+ OPT_PASSTHRU_ARGV(0, "deepen", &opt_fetch, N_("n"),
+ N_("deepen history of shallow clone"),
+ 0),
OPT_PASSTHRU(0, "unshallow", &opt_unshallow, NULL,
N_("convert to a complete repository"),
PARSE_OPT_NONEG | PARSE_OPT_NOARG),
@@ -216,12 +226,19 @@
OPT_PASSTHRU(0, "refmap", &opt_refmap, N_("refmap"),
N_("specify fetch refmap"),
PARSE_OPT_NONEG),
+ OPT_PASSTHRU_ARGV('o', "server-option", &opt_fetch,
+ N_("server-specific"),
+ N_("option to transmit"),
+ 0),
OPT_PASSTHRU('4', "ipv4", &opt_ipv4, NULL,
N_("use IPv4 addresses only"),
PARSE_OPT_NOARG),
OPT_PASSTHRU('6', "ipv6", &opt_ipv6, NULL,
N_("use IPv6 addresses only"),
PARSE_OPT_NOARG),
+ OPT_PASSTHRU_ARGV(0, "negotiation-tip", &opt_fetch, N_("revision"),
+ N_("report that we have only objects reachable from this object"),
+ 0),
OPT_BOOL(0, "show-forced-updates", &opt_show_forced_updates,
N_("check for forced-updates on all updated branches")),
OPT_PASSTHRU(0, "set-upstream", &set_upstream, NULL,
@@ -327,6 +344,22 @@
if (!git_config_get_value("pull.rebase", &value))
return parse_config_rebase("pull.rebase", value, 1);
+ if (opt_verbosity >= 0 &&
+ (!opt_ff || strcmp(opt_ff, "--ff-only"))) {
+ warning(_("Pulling without specifying how to reconcile divergent branches is\n"
+ "discouraged. You can squelch this message by running one of the following\n"
+ "commands sometime before your next pull:\n"
+ "\n"
+ " git config pull.rebase false # merge (the default strategy)\n"
+ " git config pull.rebase true # rebase\n"
+ " git config pull.ff only # fast-forward only\n"
+ "\n"
+ "You can replace \"git config\" with \"git config --global\" to set a default\n"
+ "preference for all repositories. You can also pass --rebase, --no-rebase,\n"
+ "or --ff-only on the command line to override the configured default per\n"
+ "invocation.\n"));
+ }
+
return REBASE_FALSE;
}
@@ -551,6 +584,7 @@
argv_array_push(&args, "--no-show-forced-updates");
if (set_upstream)
argv_array_push(&args, set_upstream);
+ argv_array_pushv(&args, opt_fetch.argv);
if (repo) {
argv_array_push(&args, repo);
@@ -661,6 +695,10 @@
argv_array_pushv(&args, opt_strategy_opts.argv);
if (opt_gpg_sign)
argv_array_push(&args, opt_gpg_sign);
+ if (opt_autostash == 0)
+ argv_array_push(&args, "--no-autostash");
+ else if (opt_autostash == 1)
+ argv_array_push(&args, "--autostash");
if (opt_allow_unrelated_histories > 0)
argv_array_push(&args, "--allow-unrelated-histories");
@@ -908,9 +946,6 @@
if (get_oid("HEAD", &orig_head))
oidclr(&orig_head);
- if (!opt_rebase && opt_autostash != -1)
- die(_("--[no-]autostash option is only valid with --rebase."));
-
autostash = config_autostash;
if (opt_rebase) {
if (opt_autostash != -1)
@@ -976,6 +1011,7 @@
if (opt_rebase) {
int ret = 0;
+ int ran_ff = 0;
if ((recurse_submodules == RECURSE_SUBMODULES_ON ||
recurse_submodules == RECURSE_SUBMODULES_ON_DEMAND) &&
submodule_touches_in_range(the_repository, &rebase_fork_point, &curr_head))
@@ -992,10 +1028,12 @@
if (is_descendant_of(merge_head, list)) {
/* we can fast-forward this without invoking rebase */
opt_ff = "--ff-only";
+ ran_ff = 1;
ret = run_merge();
}
}
- ret = run_rebase(&curr_head, merge_heads.oid, &rebase_fork_point);
+ if (!ran_ff)
+ ret = run_rebase(&curr_head, merge_heads.oid, &rebase_fork_point);
if (!ret && (recurse_submodules == RECURSE_SUBMODULES_ON ||
recurse_submodules == RECURSE_SUBMODULES_ON_DEMAND))
diff --git a/builtin/push.c b/builtin/push.c
index 6dbf0f0..bc94078 100644
--- a/builtin/push.c
+++ b/builtin/push.c
@@ -340,6 +340,7 @@
{
int err;
unsigned int reject_reasons;
+ char *anon_url = transport_anonymize_url(transport->url);
transport_set_verbosity(transport, verbosity, progress);
transport->family = family;
@@ -357,18 +358,19 @@
}
if (verbosity > 0)
- fprintf(stderr, _("Pushing to %s\n"), transport->url);
+ fprintf(stderr, _("Pushing to %s\n"), anon_url);
trace2_region_enter("push", "transport_push", the_repository);
err = transport_push(the_repository, transport,
rs, flags, &reject_reasons);
trace2_region_leave("push", "transport_push", the_repository);
if (err != 0) {
fprintf(stderr, "%s", push_get_color(PUSH_COLOR_ERROR));
- error(_("failed to push some refs to '%s'"), transport->url);
+ error(_("failed to push some refs to '%s'"), anon_url);
fprintf(stderr, "%s", push_get_color(PUSH_COLOR_RESET));
}
err |= transport_disconnect(transport);
+ free(anon_url);
if (!err)
return 0;
@@ -434,10 +436,8 @@
if (unset)
*recurse_submodules = RECURSE_SUBMODULES_OFF;
- else if (arg)
- *recurse_submodules = parse_push_recurse_submodules_arg(opt->long_name, arg);
else
- die("%s missing parameter", opt->long_name);
+ *recurse_submodules = parse_push_recurse_submodules_arg(opt->long_name, arg);
return 0;
}
@@ -548,13 +548,11 @@
OPT_BIT('n' , "dry-run", &flags, N_("dry run"), TRANSPORT_PUSH_DRY_RUN),
OPT_BIT( 0, "porcelain", &flags, N_("machine-readable output"), TRANSPORT_PUSH_PORCELAIN),
OPT_BIT('f', "force", &flags, N_("force updates"), TRANSPORT_PUSH_FORCE),
- { OPTION_CALLBACK,
- 0, CAS_OPT_NAME, &cas, N_("<refname>:<expect>"),
- N_("require old value of ref to be at this value"),
- PARSE_OPT_OPTARG | PARSE_OPT_LITERAL_ARGHELP, parseopt_push_cas_option },
- { OPTION_CALLBACK, 0, "recurse-submodules", &recurse_submodules, "(check|on-demand|no)",
- N_("control recursive pushing of submodules"),
- PARSE_OPT_OPTARG, option_parse_recurse_submodules },
+ OPT_CALLBACK_F(0, CAS_OPT_NAME, &cas, N_("<refname>:<expect>"),
+ N_("require old value of ref to be at this value"),
+ PARSE_OPT_OPTARG | PARSE_OPT_LITERAL_ARGHELP, parseopt_push_cas_option),
+ OPT_CALLBACK(0, "recurse-submodules", &recurse_submodules, "(check|on-demand|no)",
+ N_("control recursive pushing of submodules"), option_parse_recurse_submodules),
OPT_BOOL_F( 0 , "thin", &thin, N_("use thin pack"), PARSE_OPT_NOCOMPLETE),
OPT_STRING( 0 , "receive-pack", &receivepack, "receive-pack", N_("receive pack program")),
OPT_STRING( 0 , "exec", &receivepack, "receive-pack", N_("receive pack program")),
@@ -566,9 +564,8 @@
OPT_BIT(0, "no-verify", &flags, N_("bypass pre-push hook"), TRANSPORT_PUSH_NO_HOOK),
OPT_BIT(0, "follow-tags", &flags, N_("push missing but relevant tags"),
TRANSPORT_PUSH_FOLLOW_TAGS),
- { OPTION_CALLBACK,
- 0, "signed", &push_cert, "(yes|no|if-asked)", N_("GPG sign the push"),
- PARSE_OPT_OPTARG, option_parse_push_signed },
+ OPT_CALLBACK_F(0, "signed", &push_cert, "(yes|no|if-asked)", N_("GPG sign the push"),
+ PARSE_OPT_OPTARG, option_parse_push_signed),
OPT_BIT(0, "atomic", &flags, N_("request atomic transaction on remote side"), TRANSPORT_PUSH_ATOMIC),
OPT_STRING_LIST('o', "push-option", &push_options_cmdline, N_("server-specific"), N_("option to transmit")),
OPT_SET_INT('4', "ipv4", &family, N_("use IPv4 addresses only"),
diff --git a/builtin/read-tree.c b/builtin/read-tree.c
index af7424b..485e7b0 100644
--- a/builtin/read-tree.c
+++ b/builtin/read-tree.c
@@ -120,9 +120,9 @@
int prefix_set = 0;
struct lock_file lock_file = LOCK_INIT;
const struct option read_tree_options[] = {
- { OPTION_CALLBACK, 0, "index-output", NULL, N_("file"),
+ OPT_CALLBACK_F(0, "index-output", NULL, N_("file"),
N_("write resulting index to <file>"),
- PARSE_OPT_NONEG, index_output_cb },
+ PARSE_OPT_NONEG, index_output_cb),
OPT_BOOL(0, "empty", &read_empty,
N_("only empty the index")),
OPT__VERBOSE(&opts.verbose_update, N_("be verbose")),
@@ -140,10 +140,10 @@
PARSE_OPT_NONEG },
OPT_BOOL('u', NULL, &opts.update,
N_("update working tree with merge result")),
- { OPTION_CALLBACK, 0, "exclude-per-directory", &opts,
+ OPT_CALLBACK_F(0, "exclude-per-directory", &opts,
N_("gitignore"),
N_("allow explicitly ignored files to be overwritten"),
- PARSE_OPT_NONEG, exclude_per_directory_cb },
+ PARSE_OPT_NONEG, exclude_per_directory_cb),
OPT_BOOL('i', NULL, &opts.index_only,
N_("don't check the working tree after merging")),
OPT__DRY_RUN(&opts.dry_run, N_("don't update the index or the work tree")),
@@ -151,9 +151,9 @@
N_("skip applying sparse checkout filter")),
OPT_BOOL(0, "debug-unpack", &opts.debug_unpack,
N_("debug unpack-trees")),
- { OPTION_CALLBACK, 0, "recurse-submodules", NULL,
+ OPT_CALLBACK_F(0, "recurse-submodules", NULL,
"checkout", "control recursive updating of submodules",
- PARSE_OPT_OPTARG, option_parse_recurse_submodules_worktree_updater },
+ PARSE_OPT_OPTARG, option_parse_recurse_submodules_worktree_updater),
OPT__QUIET(&opts.quiet, N_("suppress feedback messages")),
OPT_END()
};
diff --git a/builtin/rebase.c b/builtin/rebase.c
index bff53d5..37ba76a 100644
--- a/builtin/rebase.c
+++ b/builtin/rebase.c
@@ -27,6 +27,9 @@
#include "branch.h"
#include "sequencer.h"
#include "rebase-interactive.h"
+#include "reset.h"
+
+#define DEFAULT_REFLOG_ACTION "rebase"
static char const * const builtin_rebase_usage[] = {
N_("git rebase [-i] [options] [--exec <cmd>] "
@@ -85,6 +88,7 @@
const char *action;
int signoff;
int allow_rerere_autoupdate;
+ int keep_empty;
int autosquash;
char *gpg_sign_opt;
int autostash;
@@ -95,11 +99,13 @@
struct strbuf git_format_patch_opt;
int reschedule_failed_exec;
int use_legacy_rebase;
+ int reapply_cherry_picks;
};
#define REBASE_OPTIONS_INIT { \
.type = REBASE_UNSPECIFIED, \
.empty = EMPTY_UNSPECIFIED, \
+ .keep_empty = 1, \
.default_backend = "merge", \
.flags = REBASE_NO_QUIET, \
.git_am_opts = ARGV_ARRAY_INIT, \
@@ -379,11 +385,13 @@
git_config_get_bool("rebase.abbreviatecommands", &abbreviate_commands);
+ flags |= opts->keep_empty ? TODO_LIST_KEEP_EMPTY : 0;
flags |= abbreviate_commands ? TODO_LIST_ABBREVIATE_CMDS : 0;
flags |= opts->rebase_merges ? TODO_LIST_REBASE_MERGES : 0;
flags |= opts->rebase_cousins > 0 ? TODO_LIST_REBASE_COUSINS : 0;
flags |= opts->root_with_onto ? TODO_LIST_ROOT_WITH_ONTO : 0;
flags |= command == ACTION_SHORTEN_OIDS ? TODO_LIST_SHORTEN_IDS : 0;
+ flags |= opts->reapply_cherry_picks ? TODO_LIST_REAPPLY_CHERRY_PICKS : 0;
switch (command) {
case ACTION_NONE: {
@@ -442,6 +450,7 @@
return ret;
}
+static void imply_merge(struct rebase_options *opts, const char *option);
static int parse_opt_keep_empty(const struct option *opt, const char *arg,
int unset)
{
@@ -449,10 +458,8 @@
BUG_ON_OPT_ARG(arg);
- /*
- * If we ever want to remap --keep-empty to --empty=keep, insert:
- * opts->empty = unset ? EMPTY_UNSPECIFIED : EMPTY_KEEP;
- */
+ imply_merge(opts, unset ? "--no-keep-empty" : "--keep-empty");
+ opts->keep_empty = !unset;
opts->type = REBASE_MERGE;
return 0;
}
@@ -470,10 +477,10 @@
struct option options[] = {
OPT_NEGBIT(0, "ff", &opts.flags, N_("allow fast-forward"),
REBASE_FORCE),
- { OPTION_CALLBACK, 'k', "keep-empty", &options, NULL,
- N_("(DEPRECATED) keep empty commits"),
+ OPT_CALLBACK_F('k', "keep-empty", &options, NULL,
+ N_("keep commits which start empty"),
PARSE_OPT_NOARG | PARSE_OPT_HIDDEN,
- parse_opt_keep_empty },
+ parse_opt_keep_empty),
OPT_BOOL_F(0, "allow-empty-message", &opts.allow_empty_message,
N_("allow commits with empty messages"),
PARSE_OPT_HIDDEN),
@@ -559,7 +566,7 @@
{
switch (opts->type) {
case REBASE_APPLY:
- die(_("%s requires an interactive rebase"), option);
+ die(_("%s requires the merge backend"), option);
break;
case REBASE_MERGE:
case REBASE_PRESERVE_MERGES:
@@ -586,15 +593,6 @@
return path.buf;
}
-/* Read one file, then strip line endings */
-static int read_one(const char *path, struct strbuf *buf)
-{
- if (strbuf_read_file(buf, path, 0) < 0)
- return error_errno(_("could not read '%s'"), path);
- strbuf_trim_trailing_newline(buf);
- return 0;
-}
-
/* Initialize the rebase options from the state directory. */
static int read_basic_state(struct rebase_options *opts)
{
@@ -602,8 +600,10 @@
struct strbuf buf = STRBUF_INIT;
struct object_id oid;
- if (read_one(state_dir_path("head-name", opts), &head_name) ||
- read_one(state_dir_path("onto", opts), &buf))
+ if (!read_oneliner(&head_name, state_dir_path("head-name", opts),
+ READ_ONELINER_WARN_MISSING) ||
+ !read_oneliner(&buf, state_dir_path("onto", opts),
+ READ_ONELINER_WARN_MISSING))
return -1;
opts->head_name = starts_with(head_name.buf, "refs/") ?
xstrdup(head_name.buf) : NULL;
@@ -619,9 +619,11 @@
*/
strbuf_reset(&buf);
if (file_exists(state_dir_path("orig-head", opts))) {
- if (read_one(state_dir_path("orig-head", opts), &buf))
+ if (!read_oneliner(&buf, state_dir_path("orig-head", opts),
+ READ_ONELINER_WARN_MISSING))
return -1;
- } else if (read_one(state_dir_path("head", opts), &buf))
+ } else if (!read_oneliner(&buf, state_dir_path("head", opts),
+ READ_ONELINER_WARN_MISSING))
return -1;
if (get_oid(buf.buf, &opts->orig_head))
return error(_("invalid orig-head: '%s'"), buf.buf);
@@ -641,8 +643,8 @@
if (file_exists(state_dir_path("allow_rerere_autoupdate", opts))) {
strbuf_reset(&buf);
- if (read_one(state_dir_path("allow_rerere_autoupdate", opts),
- &buf))
+ if (!read_oneliner(&buf, state_dir_path("allow_rerere_autoupdate", opts),
+ READ_ONELINER_WARN_MISSING))
return -1;
if (!strcmp(buf.buf, "--rerere-autoupdate"))
opts->allow_rerere_autoupdate = RERERE_AUTOUPDATE;
@@ -655,8 +657,8 @@
if (file_exists(state_dir_path("gpg_sign_opt", opts))) {
strbuf_reset(&buf);
- if (read_one(state_dir_path("gpg_sign_opt", opts),
- &buf))
+ if (!read_oneliner(&buf, state_dir_path("gpg_sign_opt", opts),
+ READ_ONELINER_WARN_MISSING))
return -1;
free(opts->gpg_sign_opt);
opts->gpg_sign_opt = xstrdup(buf.buf);
@@ -664,7 +666,8 @@
if (file_exists(state_dir_path("strategy", opts))) {
strbuf_reset(&buf);
- if (read_one(state_dir_path("strategy", opts), &buf))
+ if (!read_oneliner(&buf, state_dir_path("strategy", opts),
+ READ_ONELINER_WARN_MISSING))
return -1;
free(opts->strategy);
opts->strategy = xstrdup(buf.buf);
@@ -672,7 +675,8 @@
if (file_exists(state_dir_path("strategy_opts", opts))) {
strbuf_reset(&buf);
- if (read_one(state_dir_path("strategy_opts", opts), &buf))
+ if (!read_oneliner(&buf, state_dir_path("strategy_opts", opts),
+ READ_ONELINER_WARN_MISSING))
return -1;
free(opts->strategy_opts);
opts->strategy_opts = xstrdup(buf.buf);
@@ -715,65 +719,19 @@
return 0;
}
-static int apply_autostash(struct rebase_options *opts)
-{
- const char *path = state_dir_path("autostash", opts);
- struct strbuf autostash = STRBUF_INIT;
- struct child_process stash_apply = CHILD_PROCESS_INIT;
-
- if (!file_exists(path))
- return 0;
-
- if (read_one(path, &autostash))
- return error(_("Could not read '%s'"), path);
- /* Ensure that the hash is not mistaken for a number */
- strbuf_addstr(&autostash, "^0");
- argv_array_pushl(&stash_apply.args,
- "stash", "apply", autostash.buf, NULL);
- stash_apply.git_cmd = 1;
- stash_apply.no_stderr = stash_apply.no_stdout =
- stash_apply.no_stdin = 1;
- if (!run_command(&stash_apply))
- printf(_("Applied autostash.\n"));
- else {
- struct argv_array args = ARGV_ARRAY_INIT;
- int res = 0;
-
- argv_array_pushl(&args,
- "stash", "store", "-m", "autostash", "-q",
- autostash.buf, NULL);
- if (run_command_v_opt(args.argv, RUN_GIT_CMD))
- res = error(_("Cannot store %s"), autostash.buf);
- argv_array_clear(&args);
- strbuf_release(&autostash);
- if (res)
- return res;
-
- fprintf(stderr,
- _("Applying autostash resulted in conflicts.\n"
- "Your changes are safe in the stash.\n"
- "You can run \"git stash pop\" or \"git stash drop\" "
- "at any time.\n"));
- }
-
- strbuf_release(&autostash);
- return 0;
-}
-
static int finish_rebase(struct rebase_options *opts)
{
struct strbuf dir = STRBUF_INIT;
- const char *argv_gc_auto[] = { "gc", "--auto", NULL };
int ret = 0;
delete_ref(NULL, "REBASE_HEAD", NULL, REF_NO_DEREF);
- apply_autostash(opts);
+ apply_autostash(state_dir_path("autostash", opts));
close_object_store(the_repository->objects);
/*
* We ignore errors in 'gc --auto', since the
* user should see them.
*/
- run_command_v_opt(argv_gc_auto, RUN_GIT_CMD);
+ run_auto_gc(!(opts->flags & (REBASE_NO_QUIET|REBASE_VERBOSE)));
if (opts->type == REBASE_MERGE) {
struct replay_opts replay = REPLAY_OPTS_INIT;
@@ -812,143 +770,6 @@
}
}
-#define GIT_REFLOG_ACTION_ENVIRONMENT "GIT_REFLOG_ACTION"
-
-#define RESET_HEAD_DETACH (1<<0)
-#define RESET_HEAD_HARD (1<<1)
-#define RESET_HEAD_RUN_POST_CHECKOUT_HOOK (1<<2)
-#define RESET_HEAD_REFS_ONLY (1<<3)
-#define RESET_ORIG_HEAD (1<<4)
-
-static int reset_head(struct object_id *oid, const char *action,
- const char *switch_to_branch, unsigned flags,
- const char *reflog_orig_head, const char *reflog_head)
-{
- unsigned detach_head = flags & RESET_HEAD_DETACH;
- unsigned reset_hard = flags & RESET_HEAD_HARD;
- unsigned run_hook = flags & RESET_HEAD_RUN_POST_CHECKOUT_HOOK;
- unsigned refs_only = flags & RESET_HEAD_REFS_ONLY;
- unsigned update_orig_head = flags & RESET_ORIG_HEAD;
- struct object_id head_oid;
- struct tree_desc desc[2] = { { NULL }, { NULL } };
- struct lock_file lock = LOCK_INIT;
- struct unpack_trees_options unpack_tree_opts;
- struct tree *tree;
- const char *reflog_action;
- struct strbuf msg = STRBUF_INIT;
- size_t prefix_len;
- struct object_id *orig = NULL, oid_orig,
- *old_orig = NULL, oid_old_orig;
- int ret = 0, nr = 0;
-
- if (switch_to_branch && !starts_with(switch_to_branch, "refs/"))
- BUG("Not a fully qualified branch: '%s'", switch_to_branch);
-
- if (!refs_only && hold_locked_index(&lock, LOCK_REPORT_ON_ERROR) < 0) {
- ret = -1;
- goto leave_reset_head;
- }
-
- if ((!oid || !reset_hard) && get_oid("HEAD", &head_oid)) {
- ret = error(_("could not determine HEAD revision"));
- goto leave_reset_head;
- }
-
- if (!oid)
- oid = &head_oid;
-
- if (refs_only)
- goto reset_head_refs;
-
- memset(&unpack_tree_opts, 0, sizeof(unpack_tree_opts));
- setup_unpack_trees_porcelain(&unpack_tree_opts, action);
- unpack_tree_opts.head_idx = 1;
- unpack_tree_opts.src_index = the_repository->index;
- unpack_tree_opts.dst_index = the_repository->index;
- unpack_tree_opts.fn = reset_hard ? oneway_merge : twoway_merge;
- unpack_tree_opts.update = 1;
- unpack_tree_opts.merge = 1;
- if (!detach_head)
- unpack_tree_opts.reset = 1;
-
- if (repo_read_index_unmerged(the_repository) < 0) {
- ret = error(_("could not read index"));
- goto leave_reset_head;
- }
-
- if (!reset_hard && !fill_tree_descriptor(the_repository, &desc[nr++], &head_oid)) {
- ret = error(_("failed to find tree of %s"),
- oid_to_hex(&head_oid));
- goto leave_reset_head;
- }
-
- if (!fill_tree_descriptor(the_repository, &desc[nr++], oid)) {
- ret = error(_("failed to find tree of %s"), oid_to_hex(oid));
- goto leave_reset_head;
- }
-
- if (unpack_trees(nr, desc, &unpack_tree_opts)) {
- ret = -1;
- goto leave_reset_head;
- }
-
- tree = parse_tree_indirect(oid);
- prime_cache_tree(the_repository, the_repository->index, tree);
-
- if (write_locked_index(the_repository->index, &lock, COMMIT_LOCK) < 0) {
- ret = error(_("could not write index"));
- goto leave_reset_head;
- }
-
-reset_head_refs:
- reflog_action = getenv(GIT_REFLOG_ACTION_ENVIRONMENT);
- strbuf_addf(&msg, "%s: ", reflog_action ? reflog_action : "rebase");
- prefix_len = msg.len;
-
- if (update_orig_head) {
- if (!get_oid("ORIG_HEAD", &oid_old_orig))
- old_orig = &oid_old_orig;
- if (!get_oid("HEAD", &oid_orig)) {
- orig = &oid_orig;
- if (!reflog_orig_head) {
- strbuf_addstr(&msg, "updating ORIG_HEAD");
- reflog_orig_head = msg.buf;
- }
- update_ref(reflog_orig_head, "ORIG_HEAD", orig,
- old_orig, 0, UPDATE_REFS_MSG_ON_ERR);
- } else if (old_orig)
- delete_ref(NULL, "ORIG_HEAD", old_orig, 0);
- }
-
- if (!reflog_head) {
- strbuf_setlen(&msg, prefix_len);
- strbuf_addstr(&msg, "updating HEAD");
- reflog_head = msg.buf;
- }
- if (!switch_to_branch)
- ret = update_ref(reflog_head, "HEAD", oid, orig,
- detach_head ? REF_NO_DEREF : 0,
- UPDATE_REFS_MSG_ON_ERR);
- else {
- ret = update_ref(reflog_head, switch_to_branch, oid,
- NULL, 0, UPDATE_REFS_MSG_ON_ERR);
- if (!ret)
- ret = create_symref("HEAD", switch_to_branch,
- reflog_head);
- }
- if (run_hook)
- run_hook_le(NULL, "post-checkout",
- oid_to_hex(orig ? orig : &null_oid),
- oid_to_hex(oid), "1", NULL);
-
-leave_reset_head:
- strbuf_release(&msg);
- rollback_lock_file(&lock);
- while (nr)
- free((void *)desc[--nr].buffer);
- return ret;
-}
-
static int move_to_original_branch(struct rebase_options *opts)
{
struct strbuf orig_head_reflog = STRBUF_INIT, head_reflog = STRBUF_INIT;
@@ -964,8 +785,10 @@
opts->head_name, oid_to_hex(&opts->onto->object.oid));
strbuf_addf(&head_reflog, "rebase finished: returning to %s",
opts->head_name);
- ret = reset_head(NULL, "", opts->head_name, RESET_HEAD_REFS_ONLY,
- orig_head_reflog.buf, head_reflog.buf);
+ ret = reset_head(the_repository, NULL, "", opts->head_name,
+ RESET_HEAD_REFS_ONLY,
+ orig_head_reflog.buf, head_reflog.buf,
+ DEFAULT_REFLOG_ACTION);
strbuf_release(&orig_head_reflog);
strbuf_release(&head_reflog);
@@ -1053,8 +876,9 @@
free(rebased_patches);
argv_array_clear(&am.args);
- reset_head(&opts->orig_head, "checkout", opts->head_name, 0,
- "HEAD", NULL);
+ reset_head(the_repository, &opts->orig_head, "checkout",
+ opts->head_name, 0,
+ "HEAD", NULL, DEFAULT_REFLOG_ACTION);
error(_("\ngit encountered an error while preparing the "
"patches to replay\n"
"these revisions:\n"
@@ -1162,6 +986,7 @@
opts->allow_rerere_autoupdate ?
opts->allow_rerere_autoupdate == RERERE_AUTOUPDATE ?
"--rerere-autoupdate" : "--no-rerere-autoupdate" : "");
+ add_var(&script_snippet, "keep_empty", opts->keep_empty ? "yes" : "");
add_var(&script_snippet, "autosquash", opts->autosquash ? "t" : "");
add_var(&script_snippet, "gpg_sign_opt", opts->gpg_sign_opt);
add_var(&script_snippet, "cmd", opts->cmd);
@@ -1212,7 +1037,7 @@
} else if (status == 2) {
struct strbuf dir = STRBUF_INIT;
- apply_autostash(opts);
+ apply_autostash(state_dir_path("autostash", opts));
strbuf_addstr(&dir, opts->state_dir);
remove_dir_recursively(&dir, 0);
strbuf_release(&dir);
@@ -1453,7 +1278,6 @@
return 0;
}
-
int cmd_rebase(int argc, const char **argv, const char *prefix)
{
struct rebase_options options = REBASE_OPTIONS_INIT;
@@ -1526,18 +1350,18 @@
OPT_CMDMODE(0, "show-current-patch", &action,
N_("show the patch file being applied or merged"),
ACTION_SHOW_CURRENT_PATCH),
- { OPTION_CALLBACK, 0, "apply", &options, NULL,
+ OPT_CALLBACK_F(0, "apply", &options, NULL,
N_("use apply strategies to rebase"),
PARSE_OPT_NOARG | PARSE_OPT_NONEG,
- parse_opt_am },
- { OPTION_CALLBACK, 'm', "merge", &options, NULL,
+ parse_opt_am),
+ OPT_CALLBACK_F('m', "merge", &options, NULL,
N_("use merging strategies to rebase"),
PARSE_OPT_NOARG | PARSE_OPT_NONEG,
- parse_opt_merge },
- { OPTION_CALLBACK, 'i', "interactive", &options, NULL,
+ parse_opt_merge),
+ OPT_CALLBACK_F('i', "interactive", &options, NULL,
N_("let the user edit the list of commits to rebase"),
PARSE_OPT_NOARG | PARSE_OPT_NONEG,
- parse_opt_interactive },
+ parse_opt_interactive),
OPT_SET_INT_F('p', "preserve-merges", &options.type,
N_("(DEPRECATED) try to recreate merges instead of "
"ignoring them"),
@@ -1546,18 +1370,17 @@
OPT_CALLBACK_F(0, "empty", &options, "{drop,keep,ask}",
N_("how to handle commits that become empty"),
PARSE_OPT_NONEG, parse_opt_empty),
- { OPTION_CALLBACK, 'k', "keep-empty", &options, NULL,
- N_("(DEPRECATED) keep empty commits"),
+ OPT_CALLBACK_F('k', "keep-empty", &options, NULL,
+ N_("keep commits which start empty"),
PARSE_OPT_NOARG | PARSE_OPT_HIDDEN,
- parse_opt_keep_empty },
+ parse_opt_keep_empty),
OPT_BOOL(0, "autosquash", &options.autosquash,
N_("move commits that begin with "
"squash!/fixup! under -i")),
{ OPTION_STRING, 'S', "gpg-sign", &gpg_sign, N_("key-id"),
N_("GPG-sign commits"),
PARSE_OPT_OPTARG, NULL, (intptr_t) "" },
- OPT_BOOL(0, "autostash", &options.autostash,
- N_("automatically stash/stash pop before and after")),
+ OPT_AUTOSTASH(&options.autostash),
OPT_STRING_LIST('x', "exec", &exec, N_("exec"),
N_("add exec lines after each commit of the "
"editable list")),
@@ -1582,6 +1405,8 @@
OPT_BOOL(0, "reschedule-failed-exec",
&reschedule_failed_exec,
N_("automatically re-schedule any `exec` that fails")),
+ OPT_BOOL(0, "reapply-cherry-picks", &options.reapply_cherry_picks,
+ N_("apply all changes, even those already present upstream")),
OPT_END(),
};
int i;
@@ -1592,6 +1417,9 @@
options.allow_empty_message = 1;
git_config(rebase_config, &options);
+ /* options.gpg_sign_opt will be either "-S" or NULL */
+ gpg_sign = options.gpg_sign_opt ? "" : NULL;
+ FREE_AND_NULL(options.gpg_sign_opt);
if (options.use_legacy_rebase ||
!git_env_bool("GIT_TEST_REBASE_USE_BUILTIN", -1))
@@ -1652,6 +1480,9 @@
die(_("cannot combine '--keep-base' with '--root'"));
}
+ if (options.root && fork_point > 0)
+ die(_("cannot combine '--root' with '--fork-point'"));
+
if (action != ACTION_NONE && !in_progress)
die(_("No rebase in progress?"));
setenv(GIT_REFLOG_ACTION_ENVIRONMENT, "rebase", 0);
@@ -1709,8 +1540,8 @@
rerere_clear(the_repository, &merge_rr);
string_list_clear(&merge_rr, 1);
- if (reset_head(NULL, "reset", NULL, RESET_HEAD_HARD,
- NULL, NULL) < 0)
+ if (reset_head(the_repository, NULL, "reset", NULL, RESET_HEAD_HARD,
+ NULL, NULL, DEFAULT_REFLOG_ACTION) < 0)
die(_("could not discard worktree changes"));
remove_branch_state(the_repository, 0);
if (read_basic_state(&options))
@@ -1727,9 +1558,9 @@
if (read_basic_state(&options))
exit(1);
- if (reset_head(&options.orig_head, "reset",
+ if (reset_head(the_repository, &options.orig_head, "reset",
options.head_name, RESET_HEAD_HARD,
- NULL, NULL) < 0)
+ NULL, NULL, DEFAULT_REFLOG_ACTION) < 0)
die(_("could not move back to %s"),
oid_to_hex(&options.orig_head));
remove_branch_state(the_repository, 0);
@@ -1737,6 +1568,7 @@
goto cleanup;
}
case ACTION_QUIT: {
+ save_autostash(state_dir_path("autostash", &options));
if (options.type == REBASE_MERGE) {
struct replay_opts replay = REPLAY_OPTS_INIT;
@@ -1822,10 +1654,11 @@
if (options.empty != EMPTY_UNSPECIFIED)
imply_merge(&options, "--empty");
- if (gpg_sign) {
- free(options.gpg_sign_opt);
+ if (options.reapply_cherry_picks)
+ imply_merge(&options, "--reapply-cherry-picks");
+
+ if (gpg_sign)
options.gpg_sign_opt = xstrfmt("-S%s", gpg_sign);
- }
if (exec.nr) {
int i;
@@ -2086,49 +1919,8 @@
die(_("could not read index"));
if (options.autostash) {
- struct lock_file lock_file = LOCK_INIT;
- int fd;
-
- fd = hold_locked_index(&lock_file, 0);
- refresh_cache(REFRESH_QUIET);
- if (0 <= fd)
- repo_update_index_if_able(the_repository, &lock_file);
- rollback_lock_file(&lock_file);
-
- if (has_unstaged_changes(the_repository, 1) ||
- has_uncommitted_changes(the_repository, 1)) {
- const char *autostash =
- state_dir_path("autostash", &options);
- struct child_process stash = CHILD_PROCESS_INIT;
- struct object_id oid;
-
- argv_array_pushl(&stash.args,
- "stash", "create", "autostash", NULL);
- stash.git_cmd = 1;
- stash.no_stdin = 1;
- strbuf_reset(&buf);
- if (capture_command(&stash, &buf, GIT_MAX_HEXSZ))
- die(_("Cannot autostash"));
- strbuf_trim_trailing_newline(&buf);
- if (get_oid(buf.buf, &oid))
- die(_("Unexpected stash response: '%s'"),
- buf.buf);
- strbuf_reset(&buf);
- strbuf_add_unique_abbrev(&buf, &oid, DEFAULT_ABBREV);
-
- if (safe_create_leading_directories_const(autostash))
- die(_("Could not create directory for '%s'"),
- options.state_dir);
- write_file(autostash, "%s", oid_to_hex(&oid));
- printf(_("Created autostash: %s\n"), buf.buf);
- if (reset_head(NULL, "reset --hard",
- NULL, RESET_HEAD_HARD, NULL, NULL) < 0)
- die(_("could not reset --hard"));
-
- if (discard_index(the_repository->index) < 0 ||
- repo_read_index(the_repository) < 0)
- die(_("could not read index"));
- }
+ create_autostash(the_repository, state_dir_path("autostash", &options),
+ DEFAULT_REFLOG_ACTION);
}
if (require_clean_work_tree(the_repository, "rebase",
@@ -2162,10 +1954,12 @@
strbuf_addf(&buf, "%s: checkout %s",
getenv(GIT_REFLOG_ACTION_ENVIRONMENT),
options.switch_to);
- if (reset_head(&options.orig_head, "checkout",
+ if (reset_head(the_repository,
+ &options.orig_head, "checkout",
options.head_name,
RESET_HEAD_RUN_POST_CHECKOUT_HOOK,
- NULL, buf.buf) < 0) {
+ NULL, buf.buf,
+ DEFAULT_REFLOG_ACTION) < 0) {
ret = !!error(_("could not switch to "
"%s"),
options.switch_to);
@@ -2237,10 +2031,10 @@
strbuf_addf(&msg, "%s: checkout %s",
getenv(GIT_REFLOG_ACTION_ENVIRONMENT), options.onto_name);
- if (reset_head(&options.onto->object.oid, "checkout", NULL,
+ if (reset_head(the_repository, &options.onto->object.oid, "checkout", NULL,
RESET_HEAD_DETACH | RESET_ORIG_HEAD |
RESET_HEAD_RUN_POST_CHECKOUT_HOOK,
- NULL, msg.buf))
+ NULL, msg.buf, DEFAULT_REFLOG_ACTION))
die(_("Could not detach HEAD"));
strbuf_release(&msg);
@@ -2255,8 +2049,9 @@
strbuf_addf(&msg, "rebase finished: %s onto %s",
options.head_name ? options.head_name : "detached HEAD",
oid_to_hex(&options.onto->object.oid));
- reset_head(NULL, "Fast-forwarded", options.head_name,
- RESET_HEAD_REFS_ONLY, "HEAD", msg.buf);
+ reset_head(the_repository, NULL, "Fast-forwarded", options.head_name,
+ RESET_HEAD_REFS_ONLY, "HEAD", msg.buf,
+ DEFAULT_REFLOG_ACTION);
strbuf_release(&msg);
ret = !!finish_rebase(&options);
goto cleanup;
diff --git a/builtin/receive-pack.c b/builtin/receive-pack.c
index 2cc18bb..ea3d0f0 100644
--- a/builtin/receive-pack.c
+++ b/builtin/receive-pack.c
@@ -13,7 +13,7 @@
#include "remote.h"
#include "connect.h"
#include "string-list.h"
-#include "sha1-array.h"
+#include "oid-array.h"
#include "connected.h"
#include "argv-array.h"
#include "version.h"
@@ -28,6 +28,7 @@
#include "protocol.h"
#include "commit-reach.h"
#include "worktree.h"
+#include "shallow.h"
static const char * const receive_pack_usage[] = {
N_("git receive-pack <git-dir>"),
@@ -418,7 +419,7 @@
return 0;
}
-static void hmac(unsigned char *out,
+static void hmac_hash(unsigned char *out,
const char *key_in, size_t key_len,
const char *text, size_t text_len)
{
@@ -463,10 +464,10 @@
unsigned char hash[GIT_MAX_RAWSZ];
strbuf_addf(&buf, "%s:%"PRItime, path, stamp);
- hmac(hash, buf.buf, buf.len, cert_nonce_seed, strlen(cert_nonce_seed));
+ hmac_hash(hash, buf.buf, buf.len, cert_nonce_seed, strlen(cert_nonce_seed));
strbuf_release(&buf);
- /* RFC 2104 5. HMAC-SHA1-80 */
+ /* RFC 2104 5. HMAC-SHA1 or HMAC-SHA256 */
strbuf_addf(&buf, "%"PRItime"-%.*s", stamp, (int)the_hash_algo->hexsz, hash_to_hex(hash));
return strbuf_detach(&buf, NULL);
}
@@ -499,12 +500,27 @@
return NULL;
}
+/*
+ * Return zero if a and b are equal up to n bytes and nonzero if they are not.
+ * This operation is guaranteed to run in constant time to avoid leaking data.
+ */
+static int constant_memequal(const char *a, const char *b, size_t n)
+{
+ int res = 0;
+ size_t i;
+
+ for (i = 0; i < n; i++)
+ res |= a[i] ^ b[i];
+ return res;
+}
+
static const char *check_nonce(const char *buf, size_t len)
{
char *nonce = find_header(buf, len, "nonce", NULL);
timestamp_t stamp, ostamp;
char *bohmac, *expect = NULL;
const char *retval = NONCE_BAD;
+ size_t noncelen;
if (!nonce) {
retval = NONCE_MISSING;
@@ -546,8 +562,14 @@
goto leave;
}
+ noncelen = strlen(nonce);
expect = prepare_push_cert_nonce(service_dir, stamp);
- if (strcmp(expect, nonce)) {
+ if (noncelen != strlen(expect)) {
+ /* This is not even the right size. */
+ retval = NONCE_BAD;
+ goto leave;
+ }
+ if (constant_memequal(expect, nonce, noncelen)) {
/* Not what we would have signed earlier */
retval = NONCE_BAD;
goto leave;
@@ -855,7 +877,7 @@
static int command_singleton_iterator(void *cb_data, struct object_id *oid);
static int update_shallow_ref(struct command *cmd, struct shallow_info *si)
{
- struct lock_file shallow_lock = LOCK_INIT;
+ struct shallow_lock shallow_lock = SHALLOW_LOCK_INIT;
struct oid_array extra = OID_ARRAY_INIT;
struct check_connected_options opt = CHECK_CONNECTED_INIT;
uint32_t mask = 1 << (cmd->index % 32);
@@ -872,12 +894,12 @@
opt.env = tmp_objdir_env(tmp_objdir);
setup_alternate_shallow(&shallow_lock, &opt.shallow_file, &extra);
if (check_connected(command_singleton_iterator, cmd, &opt)) {
- rollback_lock_file(&shallow_lock);
+ rollback_shallow_file(the_repository, &shallow_lock);
oid_array_clear(&extra);
return -1;
}
- commit_lock_file(&shallow_lock);
+ commit_shallow_file(the_repository, &shallow_lock);
/*
* Make sure setup_alternate_shallow() for the next ref does
diff --git a/builtin/reflog.c b/builtin/reflog.c
index 81dfd56..52ecf6d 100644
--- a/builtin/reflog.c
+++ b/builtin/reflog.c
@@ -459,7 +459,7 @@
static int reflog_expire_config(const char *var, const char *value, void *cb)
{
const char *pattern, *key;
- int pattern_len;
+ size_t pattern_len;
timestamp_t expire;
int slot;
struct reflog_expire_cfg *ent;
diff --git a/builtin/remote.c b/builtin/remote.c
index 555d4c8..e837799 100644
--- a/builtin/remote.c
+++ b/builtin/remote.c
@@ -170,9 +170,9 @@
OPT_STRING_LIST('t', "track", &track, N_("branch"),
N_("branch(es) to track")),
OPT_STRING('m', "master", &master, N_("branch"), N_("master branch")),
- { OPTION_CALLBACK, 0, "mirror", &mirror, "(push|fetch)",
+ OPT_CALLBACK_F(0, "mirror", &mirror, "(push|fetch)",
N_("set up remote as a mirror to push to or fetch from"),
- PARSE_OPT_OPTARG | PARSE_OPT_COMP_ARG, parse_mirror_opt },
+ PARSE_OPT_OPTARG | PARSE_OPT_COMP_ARG, parse_mirror_opt),
OPT_END()
};
diff --git a/builtin/repack.c b/builtin/repack.c
index 0781763..df28773 100644
--- a/builtin/repack.c
+++ b/builtin/repack.c
@@ -10,8 +10,10 @@
#include "argv-array.h"
#include "midx.h"
#include "packfile.h"
+#include "prune-packed.h"
#include "object-store.h"
#include "promisor-remote.h"
+#include "shallow.h"
static int delta_base_offset = 1;
static int pack_kept_objects = -1;
diff --git a/builtin/reset.c b/builtin/reset.c
index 18228c3..8ae69d6 100644
--- a/builtin/reset.c
+++ b/builtin/reset.c
@@ -46,7 +46,7 @@
return !access(git_path_merge_head(the_repository), F_OK);
}
-static int reset_index(const struct object_id *oid, int reset_type, int quiet)
+static int reset_index(const char *ref, const struct object_id *oid, int reset_type, int quiet)
{
int i, nr = 0;
struct tree_desc desc[2];
@@ -60,6 +60,7 @@
opts.dst_index = &the_index;
opts.fn = oneway_merge;
opts.merge = 1;
+ init_checkout_metadata(&opts.meta, ref, oid, NULL);
if (!quiet)
opts.verbose_update = 1;
switch (reset_type) {
@@ -301,9 +302,9 @@
N_("reset HEAD, index and working tree"), MERGE),
OPT_SET_INT(0, "keep", &reset_type,
N_("reset HEAD but keep local changes"), KEEP),
- { OPTION_CALLBACK, 0, "recurse-submodules", NULL,
+ OPT_CALLBACK_F(0, "recurse-submodules", NULL,
"reset", "control recursive updating of submodules",
- PARSE_OPT_OPTARG, option_parse_recurse_submodules_worktree_updater },
+ PARSE_OPT_OPTARG, option_parse_recurse_submodules_worktree_updater),
OPT_BOOL('p', "patch", &patch_mode, N_("select hunks interactively")),
OPT_BOOL('N', "intent-to-add", &intent_to_add,
N_("record only the fact that removed paths will be added later")),
@@ -418,11 +419,20 @@
}
}
} else {
- int err = reset_index(&oid, reset_type, quiet);
+ struct object_id dummy;
+ char *ref = NULL;
+ int err;
+
+ dwim_ref(rev, strlen(rev), &dummy, &ref);
+ if (ref && !starts_with(ref, "refs/"))
+ ref = NULL;
+
+ err = reset_index(ref, &oid, reset_type, quiet);
if (reset_type == KEEP && !err)
- err = reset_index(&oid, MIXED, quiet);
+ err = reset_index(ref, &oid, MIXED, quiet);
if (err)
die(_("Could not reset index file to revision '%s'."), rev);
+ free(ref);
}
if (write_locked_index(&the_index, &lock, COMMIT_LOCK))
diff --git a/builtin/rev-parse.c b/builtin/rev-parse.c
index 7a00da8..669dd2f 100644
--- a/builtin/rev-parse.c
+++ b/builtin/rev-parse.c
@@ -16,6 +16,7 @@
#include "split-index.h"
#include "submodule.h"
#include "commit-reach.h"
+#include "shallow.h"
#define DO_REVS 1
#define DO_NOREV 2
@@ -808,9 +809,10 @@
continue;
}
if (!strcmp(arg, "--show-superproject-working-tree")) {
- const char *superproject = get_superproject_working_tree();
- if (superproject)
- puts(superproject);
+ struct strbuf superproject = STRBUF_INIT;
+ if (get_superproject_working_tree(&superproject))
+ puts(superproject.buf);
+ strbuf_release(&superproject);
continue;
}
if (!strcmp(arg, "--show-prefix")) {
@@ -857,7 +859,10 @@
if (!gitdir && !prefix)
gitdir = ".git";
if (gitdir) {
- puts(real_path(gitdir));
+ struct strbuf realpath = STRBUF_INIT;
+ strbuf_realpath(&realpath, gitdir, 1);
+ puts(realpath.buf);
+ strbuf_release(&realpath);
continue;
}
}
diff --git a/builtin/send-pack.c b/builtin/send-pack.c
index 098ebf2..2b9610f 100644
--- a/builtin/send-pack.c
+++ b/builtin/send-pack.c
@@ -11,7 +11,7 @@
#include "quote.h"
#include "transport.h"
#include "version.h"
-#include "sha1-array.h"
+#include "oid-array.h"
#include "gpg-interface.h"
#include "gettext.h"
#include "protocol.h"
@@ -165,9 +165,8 @@
OPT_BOOL('n' , "dry-run", &dry_run, N_("dry run")),
OPT_BOOL(0, "mirror", &send_mirror, N_("mirror all refs")),
OPT_BOOL('f', "force", &force_update, N_("force updates")),
- { OPTION_CALLBACK,
- 0, "signed", &push_cert, "(yes|no|if-asked)", N_("GPG sign the push"),
- PARSE_OPT_OPTARG, option_parse_push_signed },
+ OPT_CALLBACK_F(0, "signed", &push_cert, "(yes|no|if-asked)", N_("GPG sign the push"),
+ PARSE_OPT_OPTARG, option_parse_push_signed),
OPT_STRING_LIST(0, "push-option", &push_options,
N_("server-specific"),
N_("option to transmit")),
@@ -177,10 +176,9 @@
OPT_BOOL(0, "stateless-rpc", &stateless_rpc, N_("use stateless RPC protocol")),
OPT_BOOL(0, "stdin", &from_stdin, N_("read refs from stdin")),
OPT_BOOL(0, "helper-status", &helper_status, N_("print status from remote helper")),
- { OPTION_CALLBACK,
- 0, CAS_OPT_NAME, &cas, N_("<refname>:<expect>"),
+ OPT_CALLBACK_F(0, CAS_OPT_NAME, &cas, N_("<refname>:<expect>"),
N_("require old value of ref to be at this value"),
- PARSE_OPT_OPTARG, parseopt_push_cas_option },
+ PARSE_OPT_OPTARG, parseopt_push_cas_option),
OPT_END()
};
diff --git a/builtin/shortlog.c b/builtin/shortlog.c
index 65cd413..c856c58 100644
--- a/builtin/shortlog.c
+++ b/builtin/shortlog.c
@@ -268,9 +268,9 @@
N_("Suppress commit descriptions, only provides commit count")),
OPT_BOOL('e', "email", &log.email,
N_("Show the email address of each author")),
- { OPTION_CALLBACK, 'w', NULL, &log, N_("<w>[,<i1>[,<i2>]]"),
+ OPT_CALLBACK_F('w', NULL, &log, N_("<w>[,<i1>[,<i2>]]"),
N_("Linewrap output"), PARSE_OPT_OPTARG,
- &parse_wrap_args },
+ &parse_wrap_args),
OPT_END(),
};
diff --git a/builtin/show-branch.c b/builtin/show-branch.c
index 8c90cbb..7e52ee9 100644
--- a/builtin/show-branch.c
+++ b/builtin/show-branch.c
@@ -671,11 +671,11 @@
N_("topologically sort, maintaining date order "
"where possible"),
REV_SORT_BY_COMMIT_DATE),
- { OPTION_CALLBACK, 'g', "reflog", &reflog_base, N_("<n>[,<base>]"),
+ OPT_CALLBACK_F('g', "reflog", &reflog_base, N_("<n>[,<base>]"),
N_("show <n> most recent ref-log entries starting at "
"base"),
PARSE_OPT_OPTARG | PARSE_OPT_NONEG,
- parse_reflog_param },
+ parse_reflog_param),
OPT_END()
};
diff --git a/builtin/show-ref.c b/builtin/show-ref.c
index 6456da7..ae60b4a 100644
--- a/builtin/show-ref.c
+++ b/builtin/show-ref.c
@@ -169,15 +169,15 @@
N_("show the HEAD reference, even if it would be filtered out")),
OPT_BOOL('d', "dereference", &deref_tags,
N_("dereference tags into object IDs")),
- { OPTION_CALLBACK, 's', "hash", &abbrev, N_("n"),
- N_("only show SHA1 hash using <n> digits"),
- PARSE_OPT_OPTARG, &hash_callback },
+ OPT_CALLBACK_F('s', "hash", &abbrev, N_("n"),
+ N_("only show SHA1 hash using <n> digits"),
+ PARSE_OPT_OPTARG, &hash_callback),
OPT__ABBREV(&abbrev),
OPT__QUIET(&quiet,
N_("do not print results to stdout (useful with --verify)")),
- { OPTION_CALLBACK, 0, "exclude-existing", &exclude_existing_arg,
- N_("pattern"), N_("show refs from stdin that aren't in local repository"),
- PARSE_OPT_OPTARG | PARSE_OPT_NONEG, exclude_existing_callback },
+ OPT_CALLBACK_F(0, "exclude-existing", &exclude_existing_arg,
+ N_("pattern"), N_("show refs from stdin that aren't in local repository"),
+ PARSE_OPT_OPTARG | PARSE_OPT_NONEG, exclude_existing_callback),
OPT_END()
};
diff --git a/builtin/sparse-checkout.c b/builtin/sparse-checkout.c
index 740da4b..95d0882 100644
--- a/builtin/sparse-checkout.c
+++ b/builtin/sparse-checkout.c
@@ -18,7 +18,7 @@
static const char *empty_base = "";
static char const * const builtin_sparse_checkout_usage[] = {
- N_("git sparse-checkout (init|list|set|add|disable) <options>"),
+ N_("git sparse-checkout (init|list|set|add|reapply|disable) <options>"),
NULL
};
@@ -94,50 +94,37 @@
static int update_working_directory(struct pattern_list *pl)
{
- int result = 0;
+ enum update_sparsity_result result;
struct unpack_trees_options o;
struct lock_file lock_file = LOCK_INIT;
- struct object_id oid;
- struct tree *tree;
- struct tree_desc t;
struct repository *r = the_repository;
- if (repo_read_index_unmerged(r))
- die(_("you need to resolve your current index first"));
-
- if (get_oid("HEAD", &oid))
- return 0;
-
- tree = parse_tree_indirect(&oid);
- parse_tree(tree);
- init_tree_desc(&t, tree->buffer, tree->size);
-
memset(&o, 0, sizeof(o));
o.verbose_update = isatty(2);
- o.merge = 1;
o.update = 1;
- o.fn = oneway_merge;
o.head_idx = -1;
o.src_index = r->index;
o.dst_index = r->index;
o.skip_sparse_checkout = 0;
o.pl = pl;
- o.keep_pattern_list = !!pl;
- resolve_undo_clear_index(r->index);
setup_work_tree();
- cache_tree_free(&r->index->cache_tree);
-
repo_hold_locked_index(r, &lock_file, LOCK_DIE_ON_ERROR);
- core_apply_sparse_checkout = 1;
- result = unpack_trees(1, &t, &o);
+ setup_unpack_trees_porcelain(&o, "sparse-checkout");
+ result = update_sparsity(&o);
+ clear_unpack_trees_porcelain(&o);
- if (!result) {
- prime_cache_tree(r, r->index, tree);
+ if (result == UPDATE_SPARSITY_WARNINGS)
+ /*
+ * We don't do any special handling of warnings from untracked
+ * files in the way or dirty entries that can't be removed.
+ */
+ result = UPDATE_SPARSITY_SUCCESS;
+ if (result == UPDATE_SPARSITY_SUCCESS)
write_locked_index(r->index, &lock_file, COMMIT_LOCK);
- } else
+ else
rollback_lock_file(&lock_file);
return result;
@@ -304,8 +291,6 @@
};
repo_read_index(the_repository);
- require_clean_work_tree(the_repository,
- N_("initialize sparse-checkout"), NULL, 1, 0);
argc = parse_options(argc, argv, NULL,
builtin_sparse_checkout_init_options,
@@ -560,8 +545,6 @@
};
repo_read_index(the_repository);
- require_clean_work_tree(the_repository,
- N_("set sparse-checkout patterns"), NULL, 1, 0);
argc = parse_options(argc, argv, prefix,
builtin_sparse_checkout_set_options,
@@ -571,14 +554,18 @@
return modify_pattern_list(argc, argv, m);
}
+static int sparse_checkout_reapply(int argc, const char **argv)
+{
+ repo_read_index(the_repository);
+ return update_working_directory(NULL);
+}
+
static int sparse_checkout_disable(int argc, const char **argv)
{
struct pattern_list pl;
struct strbuf match_all = STRBUF_INIT;
repo_read_index(the_repository);
- require_clean_work_tree(the_repository,
- N_("disable sparse-checkout"), NULL, 1, 0);
memset(&pl, 0, sizeof(pl));
hashmap_init(&pl.recursive_hashmap, pl_hashmap_cmp, NULL, 0);
@@ -622,6 +609,8 @@
return sparse_checkout_set(argc, argv, prefix, REPLACE);
if (!strcmp(argv[0], "add"))
return sparse_checkout_set(argc, argv, prefix, ADD);
+ if (!strcmp(argv[0], "reapply"))
+ return sparse_checkout_reapply(argc, argv);
if (!strcmp(argv[0], "disable"))
return sparse_checkout_disable(argc, argv);
}
diff --git a/builtin/stash.c b/builtin/stash.c
index 78af6ce..0c52a3b 100644
--- a/builtin/stash.c
+++ b/builtin/stash.c
@@ -702,6 +702,7 @@
static int show_stat = 1;
static int show_patch;
+static int use_legacy_stash;
static int git_stash_config(const char *var, const char *value, void *cb)
{
@@ -713,7 +714,11 @@
show_patch = git_config_bool(var, value);
return 0;
}
- return git_default_config(var, value, cb);
+ if (!strcmp(var, "stash.usebuiltin")) {
+ use_legacy_stash = !git_config_bool(var, value);
+ return 0;
+ }
+ return git_diff_basic_config(var, value, cb);
}
static int show_stash(int argc, const char **argv, const char *prefix)
@@ -750,7 +755,6 @@
* any options.
*/
if (revision_args.argc == 1) {
- git_config(git_stash_config, NULL);
if (show_stat)
rev.diffopt.output_format = DIFF_FORMAT_DIFFSTAT;
@@ -857,30 +861,23 @@
struct strbuf *untracked_files)
{
int i;
- int max_len;
int found = 0;
- char *seen;
struct dir_struct dir;
memset(&dir, 0, sizeof(dir));
if (include_untracked != INCLUDE_ALL_FILES)
setup_standard_excludes(&dir);
- seen = xcalloc(ps->nr, 1);
-
- max_len = fill_directory(&dir, the_repository->index, ps);
+ fill_directory(&dir, the_repository->index, ps);
for (i = 0; i < dir.nr; i++) {
struct dir_entry *ent = dir.entries[i];
- if (dir_path_match(&the_index, ent, ps, max_len, seen)) {
- found++;
- strbuf_addstr(untracked_files, ent->name);
- /* NUL-terminate: will be fed to update-index -z */
- strbuf_addch(untracked_files, '\0');
- }
+ found++;
+ strbuf_addstr(untracked_files, ent->name);
+ /* NUL-terminate: will be fed to update-index -z */
+ strbuf_addch(untracked_files, '\0');
free(ent);
}
- free(seen);
free(dir.entries);
free(dir.ignored);
clear_directory(&dir);
@@ -1037,7 +1034,7 @@
}
cp_diff_tree.git_cmd = 1;
- argv_array_pushl(&cp_diff_tree.args, "diff-tree", "-p", "HEAD",
+ argv_array_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;
@@ -1559,29 +1556,6 @@
return ret;
}
-static int use_builtin_stash(void)
-{
- struct child_process cp = CHILD_PROCESS_INIT;
- struct strbuf out = STRBUF_INIT;
- int ret, env = git_env_bool("GIT_TEST_STASH_USE_BUILTIN", -1);
-
- if (env != -1)
- return env;
-
- argv_array_pushl(&cp.args,
- "config", "--bool", "stash.usebuiltin", NULL);
- cp.git_cmd = 1;
- if (capture_command(&cp, &out, 6)) {
- strbuf_release(&out);
- return 1;
- }
-
- strbuf_trim(&out);
- ret = !strcmp("true", out.buf);
- strbuf_release(&out);
- return ret;
-}
-
int cmd_stash(int argc, const char **argv, const char *prefix)
{
pid_t pid = getpid();
@@ -1592,21 +1566,12 @@
OPT_END()
};
- if (!use_builtin_stash()) {
- const char *path = mkpath("%s/git-legacy-stash",
- git_exec_path());
+ git_config(git_stash_config, NULL);
- if (sane_execvp(path, (char **)argv) < 0)
- die_errno(_("could not exec %s"), path);
- else
- BUG("sane_execvp() returned???");
- }
-
- prefix = setup_git_directory();
- trace_repo_setup(prefix);
- setup_work_tree();
-
- git_config(git_diff_basic_config, NULL);
+ if (use_legacy_stash ||
+ !git_env_bool("GIT_TEST_STASH_USE_BUILTIN", -1))
+ warning(_("the stash.useBuiltin support has been removed!\n"
+ "See its entry in 'git help config' for details."));
argc = parse_options(argc, argv, prefix, options, git_stash_usage,
PARSE_OPT_KEEP_UNKNOWN | PARSE_OPT_KEEP_DASHDASH);
diff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c
index 86a608e..46c03d2 100644
--- a/builtin/submodule--helper.c
+++ b/builtin/submodule--helper.c
@@ -444,19 +444,19 @@
fn(list->entries[i], cb_data);
}
-struct cb_foreach {
+struct foreach_cb {
int argc;
const char **argv;
const char *prefix;
int quiet;
int recursive;
};
-#define CB_FOREACH_INIT { 0 }
+#define FOREACH_CB_INIT { 0 }
static void runcommand_in_submodule_cb(const struct cache_entry *list_item,
void *cb_data)
{
- struct cb_foreach *info = cb_data;
+ struct foreach_cb *info = cb_data;
const char *path = list_item->name;
const struct object_id *ce_oid = &list_item->oid;
@@ -557,7 +557,7 @@
static int module_foreach(int argc, const char **argv, const char *prefix)
{
- struct cb_foreach info = CB_FOREACH_INIT;
+ struct foreach_cb info = FOREACH_CB_INIT;
struct pathspec pathspec;
struct module_list list = MODULE_LIST_INIT;
@@ -2246,6 +2246,37 @@
usage_with_options(git_submodule_helper_usage, module_config_options);
}
+static int module_set_url(int argc, const char **argv, const char *prefix)
+{
+ int quiet = 0;
+ const char *newurl;
+ const char *path;
+ char *config_name;
+
+ struct option options[] = {
+ OPT__QUIET(&quiet, N_("Suppress output for setting url of a submodule")),
+ OPT_END()
+ };
+ const char *const usage[] = {
+ N_("git submodule--helper set-url [--quiet] <path> <newurl>"),
+ NULL
+ };
+
+ argc = parse_options(argc, argv, prefix, options, usage, 0);
+
+ if (argc != 2 || !(path = argv[0]) || !(newurl = argv[1]))
+ usage_with_options(usage, options);
+
+ config_name = xstrfmt("submodule.%s.url", path);
+
+ config_set_in_gitmodules_file_gently(config_name, newurl);
+ sync_submodule(path, prefix, quiet ? OPT_QUIET : 0);
+
+ free(config_name);
+
+ return 0;
+}
+
#define SUPPORT_SUPER_PREFIX (1<<0)
struct cmd_struct {
@@ -2276,6 +2307,7 @@
{"is-active", is_active, 0},
{"check-name", check_name, 0},
{"config", module_config, 0},
+ {"set-url", module_set_url, 0},
};
int cmd_submodule__helper(int argc, const char **argv, const char *prefix)
diff --git a/builtin/tag.c b/builtin/tag.c
index e0a4c25..5cbd80d 100644
--- a/builtin/tag.c
+++ b/builtin/tag.c
@@ -17,7 +17,7 @@
#include "diff.h"
#include "revision.h"
#include "gpg-interface.h"
-#include "sha1-array.h"
+#include "oid-array.h"
#include "column.h"
#include "ref-filter.h"
@@ -231,8 +231,9 @@
if (type <= OBJ_NONE)
die(_("bad object type."));
- if (type == OBJ_TAG && advice_nested_tag)
- advise(_(message_advice_nested_tag), tag, object_ref);
+ if (type == OBJ_TAG)
+ advise_if_enabled(ADVICE_NESTED_TAG, _(message_advice_nested_tag),
+ tag, object_ref);
strbuf_addf(&header,
"object %s\n"
@@ -409,8 +410,8 @@
OPT_GROUP(N_("Tag creation options")),
OPT_BOOL('a', "annotate", &annotate,
N_("annotated tag, needs a message")),
- { OPTION_CALLBACK, 'm', "message", &msg, N_("message"),
- N_("tag message"), PARSE_OPT_NONEG, parse_msg_arg },
+ OPT_CALLBACK_F('m', "message", &msg, N_("message"),
+ N_("tag message"), PARSE_OPT_NONEG, parse_msg_arg),
OPT_FILENAME('F', "file", &msgfile, N_("read message from file")),
OPT_BOOL('e', "edit", &edit_flag, N_("force edit of tag message")),
OPT_BOOL('s', "sign", &opt.sign, N_("annotated and GPG-signed tag")),
@@ -484,7 +485,7 @@
}
if (!sorting)
sorting = ref_default_sorting();
- sorting->ignore_case = icase;
+ ref_sorting_icase_all(sorting, icase);
filter.ignore_case = icase;
if (cmdmode == 'l') {
int ret;
diff --git a/builtin/update-index.c b/builtin/update-index.c
index d527b8f..79087bc 100644
--- a/builtin/update-index.c
+++ b/builtin/update-index.c
@@ -985,14 +985,14 @@
OPT_BIT(0, "unmerged", &refresh_args.flags,
N_("refresh even if index contains unmerged entries"),
REFRESH_UNMERGED),
- {OPTION_CALLBACK, 0, "refresh", &refresh_args, NULL,
+ OPT_CALLBACK_F(0, "refresh", &refresh_args, NULL,
N_("refresh stat information"),
PARSE_OPT_NOARG | PARSE_OPT_NONEG,
- refresh_callback},
- {OPTION_CALLBACK, 0, "really-refresh", &refresh_args, NULL,
+ refresh_callback),
+ OPT_CALLBACK_F(0, "really-refresh", &refresh_args, NULL,
N_("like --refresh, but ignore assume-unchanged setting"),
PARSE_OPT_NOARG | PARSE_OPT_NONEG,
- really_refresh_callback},
+ really_refresh_callback),
{OPTION_LOWLEVEL_CALLBACK, 0, "cacheinfo", NULL,
N_("<mode>,<object>,<path>"),
N_("add the specified entry to the index"),
@@ -1000,10 +1000,10 @@
PARSE_OPT_NONEG | PARSE_OPT_LITERAL_ARGHELP,
NULL, 0,
cacheinfo_callback},
- {OPTION_CALLBACK, 0, "chmod", &set_executable_bit, "(+|-)x",
+ OPT_CALLBACK_F(0, "chmod", &set_executable_bit, "(+|-)x",
N_("override the executable bit of the listed files"),
PARSE_OPT_NONEG,
- chmod_callback},
+ chmod_callback),
{OPTION_SET_INT, 0, "assume-unchanged", &mark_valid_only, NULL,
N_("mark files as \"not changing\""),
PARSE_OPT_NOARG | PARSE_OPT_NONEG, NULL, MARK_FLAG},
@@ -1045,10 +1045,10 @@
REFRESH_IGNORE_MISSING),
OPT_SET_INT(0, "verbose", &verbose,
N_("report actions to standard output"), 1),
- {OPTION_CALLBACK, 0, "clear-resolve-undo", NULL, NULL,
+ OPT_CALLBACK_F(0, "clear-resolve-undo", NULL, NULL,
N_("(for porcelains) forget saved unresolved conflicts"),
PARSE_OPT_NOARG | PARSE_OPT_NONEG,
- resolve_undo_clear_callback},
+ resolve_undo_clear_callback),
OPT_INTEGER(0, "index-version", &preferred_index_format,
N_("write index in this format")),
OPT_BOOL(0, "split-index", &split_index,
diff --git a/builtin/update-ref.c b/builtin/update-ref.c
index 2d8f7f0..b74dd9a6 100644
--- a/builtin/update-ref.c
+++ b/builtin/update-ref.c
@@ -50,7 +50,7 @@
* the argument. Die if C-quoting is malformed or the reference name
* is invalid.
*/
-static char *parse_refname(struct strbuf *input, const char **next)
+static char *parse_refname(const char **next)
{
struct strbuf ref = STRBUF_INIT;
@@ -95,7 +95,7 @@
* provided but cannot be converted to a SHA-1, die. flags can
* include PARSE_SHA1_OLD and/or PARSE_SHA1_ALLOW_EMPTY.
*/
-static int parse_next_oid(struct strbuf *input, const char **next,
+static int parse_next_oid(const char **next, const char *end,
struct object_id *oid,
const char *command, const char *refname,
int flags)
@@ -103,7 +103,7 @@
struct strbuf arg = STRBUF_INIT;
int ret = 0;
- if (*next == input->buf + input->len)
+ if (*next == end)
goto eof;
if (line_termination) {
@@ -128,7 +128,7 @@
die("%s %s: expected NUL but got: %s",
command, refname, *next);
(*next)++;
- if (*next == input->buf + input->len)
+ if (*next == end)
goto eof;
strbuf_addstr(&arg, *next);
*next += arg.len;
@@ -178,23 +178,23 @@
* depending on how line_termination is set.
*/
-static const char *parse_cmd_update(struct ref_transaction *transaction,
- struct strbuf *input, const char *next)
+static void parse_cmd_update(struct ref_transaction *transaction,
+ const char *next, const char *end)
{
struct strbuf err = STRBUF_INIT;
char *refname;
struct object_id new_oid, old_oid;
int have_old;
- refname = parse_refname(input, &next);
+ refname = parse_refname(&next);
if (!refname)
die("update: missing <ref>");
- if (parse_next_oid(input, &next, &new_oid, "update", refname,
+ if (parse_next_oid(&next, end, &new_oid, "update", refname,
PARSE_SHA1_ALLOW_EMPTY))
die("update %s: missing <newvalue>", refname);
- have_old = !parse_next_oid(input, &next, &old_oid, "update", refname,
+ have_old = !parse_next_oid(&next, end, &old_oid, "update", refname,
PARSE_SHA1_OLD);
if (*next != line_termination)
@@ -209,22 +209,20 @@
update_flags = default_flags;
free(refname);
strbuf_release(&err);
-
- return next;
}
-static const char *parse_cmd_create(struct ref_transaction *transaction,
- struct strbuf *input, const char *next)
+static void parse_cmd_create(struct ref_transaction *transaction,
+ const char *next, const char *end)
{
struct strbuf err = STRBUF_INIT;
char *refname;
struct object_id new_oid;
- refname = parse_refname(input, &next);
+ refname = parse_refname(&next);
if (!refname)
die("create: missing <ref>");
- if (parse_next_oid(input, &next, &new_oid, "create", refname, 0))
+ if (parse_next_oid(&next, end, &new_oid, "create", refname, 0))
die("create %s: missing <newvalue>", refname);
if (is_null_oid(&new_oid))
@@ -241,23 +239,21 @@
update_flags = default_flags;
free(refname);
strbuf_release(&err);
-
- return next;
}
-static const char *parse_cmd_delete(struct ref_transaction *transaction,
- struct strbuf *input, const char *next)
+static void parse_cmd_delete(struct ref_transaction *transaction,
+ const char *next, const char *end)
{
struct strbuf err = STRBUF_INIT;
char *refname;
struct object_id old_oid;
int have_old;
- refname = parse_refname(input, &next);
+ refname = parse_refname(&next);
if (!refname)
die("delete: missing <ref>");
- if (parse_next_oid(input, &next, &old_oid, "delete", refname,
+ if (parse_next_oid(&next, end, &old_oid, "delete", refname,
PARSE_SHA1_OLD)) {
have_old = 0;
} else {
@@ -277,22 +273,20 @@
update_flags = default_flags;
free(refname);
strbuf_release(&err);
-
- return next;
}
-static const char *parse_cmd_verify(struct ref_transaction *transaction,
- struct strbuf *input, const char *next)
+static void parse_cmd_verify(struct ref_transaction *transaction,
+ const char *next, const char *end)
{
struct strbuf err = STRBUF_INIT;
char *refname;
struct object_id old_oid;
- refname = parse_refname(input, &next);
+ refname = parse_refname(&next);
if (!refname)
die("verify: missing <ref>");
- if (parse_next_oid(input, &next, &old_oid, "verify", refname,
+ if (parse_next_oid(&next, end, &old_oid, "verify", refname,
PARSE_SHA1_OLD))
oidclr(&old_oid);
@@ -306,50 +300,179 @@
update_flags = default_flags;
free(refname);
strbuf_release(&err);
-
- return next;
}
-static const char *parse_cmd_option(struct strbuf *input, const char *next)
+static void parse_cmd_option(struct ref_transaction *transaction,
+ const char *next, const char *end)
{
const char *rest;
if (skip_prefix(next, "no-deref", &rest) && *rest == line_termination)
update_flags |= REF_NO_DEREF;
else
die("option unknown: %s", next);
- return rest;
}
-static void update_refs_stdin(struct ref_transaction *transaction)
+static void parse_cmd_start(struct ref_transaction *transaction,
+ const char *next, const char *end)
{
- struct strbuf input = STRBUF_INIT;
- const char *next;
+ if (*next != line_termination)
+ die("start: extra input: %s", next);
+ puts("start: ok");
+}
- if (strbuf_read(&input, 0, 1000) < 0)
- die_errno("could not read from stdin");
- next = input.buf;
+static void parse_cmd_prepare(struct ref_transaction *transaction,
+ const char *next, const char *end)
+{
+ struct strbuf error = STRBUF_INIT;
+ if (*next != line_termination)
+ die("prepare: extra input: %s", next);
+ if (ref_transaction_prepare(transaction, &error))
+ die("prepare: %s", error.buf);
+ puts("prepare: ok");
+}
+
+static void parse_cmd_abort(struct ref_transaction *transaction,
+ const char *next, const char *end)
+{
+ struct strbuf error = STRBUF_INIT;
+ if (*next != line_termination)
+ die("abort: extra input: %s", next);
+ if (ref_transaction_abort(transaction, &error))
+ die("abort: %s", error.buf);
+ puts("abort: ok");
+}
+
+static void parse_cmd_commit(struct ref_transaction *transaction,
+ const char *next, const char *end)
+{
+ struct strbuf error = STRBUF_INIT;
+ if (*next != line_termination)
+ die("commit: extra input: %s", next);
+ if (ref_transaction_commit(transaction, &error))
+ die("commit: %s", error.buf);
+ puts("commit: ok");
+ ref_transaction_free(transaction);
+}
+
+enum update_refs_state {
+ /* Non-transactional state open for updates. */
+ UPDATE_REFS_OPEN,
+ /* A transaction has been started. */
+ UPDATE_REFS_STARTED,
+ /* References are locked and ready for commit */
+ UPDATE_REFS_PREPARED,
+ /* Transaction has been committed or closed. */
+ UPDATE_REFS_CLOSED,
+};
+
+static const struct parse_cmd {
+ const char *prefix;
+ void (*fn)(struct ref_transaction *, const char *, const char *);
+ unsigned args;
+ enum update_refs_state state;
+} command[] = {
+ { "update", parse_cmd_update, 3, UPDATE_REFS_OPEN },
+ { "create", parse_cmd_create, 2, UPDATE_REFS_OPEN },
+ { "delete", parse_cmd_delete, 2, UPDATE_REFS_OPEN },
+ { "verify", parse_cmd_verify, 2, UPDATE_REFS_OPEN },
+ { "option", parse_cmd_option, 1, UPDATE_REFS_OPEN },
+ { "start", parse_cmd_start, 0, UPDATE_REFS_STARTED },
+ { "prepare", parse_cmd_prepare, 0, UPDATE_REFS_PREPARED },
+ { "abort", parse_cmd_abort, 0, UPDATE_REFS_CLOSED },
+ { "commit", parse_cmd_commit, 0, UPDATE_REFS_CLOSED },
+};
+
+static void update_refs_stdin(void)
+{
+ struct strbuf input = STRBUF_INIT, err = STRBUF_INIT;
+ enum update_refs_state state = UPDATE_REFS_OPEN;
+ struct ref_transaction *transaction;
+ int i, j;
+
+ transaction = ref_transaction_begin(&err);
+ if (!transaction)
+ die("%s", err.buf);
+
/* Read each line dispatch its command */
- while (next < input.buf + input.len) {
- if (*next == line_termination)
- die("empty command in input");
- else if (isspace(*next))
- die("whitespace before command: %s", next);
- else if (skip_prefix(next, "update ", &next))
- next = parse_cmd_update(transaction, &input, next);
- else if (skip_prefix(next, "create ", &next))
- next = parse_cmd_create(transaction, &input, next);
- else if (skip_prefix(next, "delete ", &next))
- next = parse_cmd_delete(transaction, &input, next);
- else if (skip_prefix(next, "verify ", &next))
- next = parse_cmd_verify(transaction, &input, next);
- else if (skip_prefix(next, "option ", &next))
- next = parse_cmd_option(&input, next);
- else
- die("unknown command: %s", next);
+ while (!strbuf_getwholeline(&input, stdin, line_termination)) {
+ const struct parse_cmd *cmd = NULL;
- next++;
+ if (*input.buf == line_termination)
+ die("empty command in input");
+ else if (isspace(*input.buf))
+ die("whitespace before command: %s", input.buf);
+
+ for (i = 0; i < ARRAY_SIZE(command); i++) {
+ const char *prefix = command[i].prefix;
+ char c;
+
+ if (!starts_with(input.buf, prefix))
+ continue;
+
+ /*
+ * If the command has arguments, verify that it's
+ * followed by a space. Otherwise, it shall be followed
+ * by a line terminator.
+ */
+ c = command[i].args ? ' ' : line_termination;
+ if (input.buf[strlen(prefix)] != c)
+ continue;
+
+ cmd = &command[i];
+ break;
+ }
+ if (!cmd)
+ die("unknown command: %s", input.buf);
+
+ /*
+ * Read additional arguments if NUL-terminated. Do not raise an
+ * error in case there is an early EOF to let the command
+ * handle missing arguments with a proper error message.
+ */
+ for (j = 1; line_termination == '\0' && j < cmd->args; j++)
+ if (strbuf_appendwholeline(&input, stdin, line_termination))
+ break;
+
+ switch (state) {
+ case UPDATE_REFS_OPEN:
+ case UPDATE_REFS_STARTED:
+ /* Do not downgrade a transaction to a non-transaction. */
+ if (cmd->state >= state)
+ state = cmd->state;
+ break;
+ case UPDATE_REFS_PREPARED:
+ if (cmd->state != UPDATE_REFS_CLOSED)
+ die("prepared transactions can only be closed");
+ state = cmd->state;
+ break;
+ case UPDATE_REFS_CLOSED:
+ die("transaction is closed");
+ break;
+ }
+
+ cmd->fn(transaction, input.buf + strlen(cmd->prefix) + !!cmd->args,
+ input.buf + input.len);
}
+ switch (state) {
+ case UPDATE_REFS_OPEN:
+ /* Commit by default if no transaction was requested. */
+ if (ref_transaction_commit(transaction, &err))
+ die("%s", err.buf);
+ ref_transaction_free(transaction);
+ break;
+ case UPDATE_REFS_STARTED:
+ case UPDATE_REFS_PREPARED:
+ /* If using a transaction, we want to abort it. */
+ if (ref_transaction_abort(transaction, &err))
+ die("%s", err.buf);
+ break;
+ case UPDATE_REFS_CLOSED:
+ /* Otherwise no need to do anything, the transaction was closed already. */
+ break;
+ }
+
+ strbuf_release(&err);
strbuf_release(&input);
}
@@ -384,21 +507,11 @@
}
if (read_stdin) {
- struct strbuf err = STRBUF_INIT;
- struct ref_transaction *transaction;
-
- transaction = ref_transaction_begin(&err);
- if (!transaction)
- die("%s", err.buf);
if (delete || argc > 0)
usage_with_options(git_update_ref_usage, options);
if (end_null)
line_termination = '\0';
- update_refs_stdin(transaction);
- if (ref_transaction_commit(transaction, &err))
- die("%s", err.buf);
- ref_transaction_free(transaction);
- strbuf_release(&err);
+ update_refs_stdin();
return 0;
}
diff --git a/builtin/worktree.c b/builtin/worktree.c
index 24f2280..d99db35 100644
--- a/builtin/worktree.c
+++ b/builtin/worktree.c
@@ -258,7 +258,7 @@
const struct add_opts *opts)
{
struct strbuf sb_git = STRBUF_INIT, sb_repo = STRBUF_INIT;
- struct strbuf sb = STRBUF_INIT;
+ struct strbuf sb = STRBUF_INIT, realpath = STRBUF_INIT;
const char *name;
struct child_process cp = CHILD_PROCESS_INIT;
struct argv_array child_env = ARGV_ARRAY_INIT;
@@ -330,9 +330,11 @@
strbuf_reset(&sb);
strbuf_addf(&sb, "%s/gitdir", sb_repo.buf);
- write_file(sb.buf, "%s", real_path(sb_git.buf));
+ strbuf_realpath(&realpath, sb_git.buf, 1);
+ write_file(sb.buf, "%s", realpath.buf);
+ strbuf_realpath(&realpath, get_git_common_dir(), 1);
write_file(sb_git.buf, "gitdir: %s/worktrees/%s",
- real_path(get_git_common_dir()), name);
+ realpath.buf, name);
/*
* This is to keep resolve_ref() happy. We need a valid HEAD
* or is_git_directory() will reject the directory. Any value which
@@ -418,6 +420,7 @@
strbuf_release(&sb_repo);
strbuf_release(&sb_git);
strbuf_release(&sb_name);
+ strbuf_release(&realpath);
return ret;
}
diff --git a/cache.h b/cache.h
index 37c899b..0f0485e 100644
--- a/cache.h
+++ b/cache.h
@@ -14,7 +14,7 @@
#include "pack-revindex.h"
#include "hash.h"
#include "path.h"
-#include "sha1-array.h"
+#include "oid-array.h"
#include "repository.h"
#include "mem-pool.h"
@@ -543,7 +543,7 @@
char *get_object_directory(void);
char *get_index_file(void);
char *get_graft_file(struct repository *r);
-void set_git_dir(const char *path);
+void set_git_dir(const char *path, int make_realpath);
int get_common_dir_noenv(struct strbuf *sb, const char *gitdir);
int get_common_dir(struct strbuf *sb, const char *gitdir);
const char *get_git_namespace(void);
@@ -627,7 +627,9 @@
#define INIT_DB_EXIST_OK 0x0002
int init_db(const char *git_dir, const char *real_git_dir,
- const char *template_dir, unsigned int flags);
+ const char *template_dir, int hash_algo,
+ unsigned int flags);
+void initialize_repository_version(int hash_algo);
void sanitize_stdfds(void);
int daemonize(void);
@@ -1086,8 +1088,10 @@
* and die if it is a version we don't understand. Generally one would
* set_git_dir() before calling this, and use it only for "are we in a valid
* repo?".
+ *
+ * If successful and fmt is not NULL, fill fmt with data.
*/
-void check_repository_format(void);
+void check_repository_format(struct repository_format *fmt);
#define MTIME_CHANGED 0x0001
#define CTIME_CHANGED 0x0002
@@ -1314,8 +1318,6 @@
int is_directory(const char *);
char *strbuf_realpath(struct strbuf *resolved, const char *path,
int die_on_error);
-const char *real_path(const char *path);
-const char *real_path_if_valid(const char *path);
char *real_pathdup(const char *path, int die_on_error);
const char *absolute_path(const char *path);
char *absolute_pathdup(const char *path);
@@ -1481,6 +1483,9 @@
int get_sha1_hex(const char *hex, unsigned char *sha1);
int get_oid_hex(const char *hex, struct object_id *sha1);
+/* Like get_oid_hex, but for an arbitrary hash algorithm. */
+int get_oid_hex_algop(const char *hex, struct object_id *oid, const struct git_hash_algo *algop);
+
/*
* Read `len` pairs of hexadecimal digits from `hex` and write the
* values to `binary` as `len` bytes. Return 0 on success, or -1 if
@@ -1516,6 +1521,20 @@
*/
int parse_oid_hex(const char *hex, struct object_id *oid, const char **end);
+/* Like parse_oid_hex, but for an arbitrary hash algorithm. */
+int parse_oid_hex_algop(const char *hex, struct object_id *oid, const char **end,
+ const struct git_hash_algo *algo);
+
+
+/*
+ * These functions work like get_oid_hex and parse_oid_hex, but they will parse
+ * a hex value for any algorithm. The algorithm is detected based on the length
+ * and the algorithm in use is returned. If this is not a hex object ID in any
+ * algorithm, returns GIT_HASH_UNKNOWN.
+ */
+int get_oid_hex_any(const char *hex, struct object_id *oid);
+int parse_oid_hex_any(const char *hex, struct object_id *oid, const char **end);
+
/*
* This reads short-hand syntax that not only evaluates to a commit
* object name, but also can act as if the end user spelled the name
@@ -1679,6 +1698,7 @@
const char *base_dir;
int base_dir_len;
struct delayed_checkout *delayed_checkout;
+ struct checkout_metadata meta;
unsigned force:1,
quiet:1,
not_new:1,
diff --git a/ci/config/allow-ref.sample b/ci/config/allow-ref.sample
new file mode 100755
index 0000000..af0e076
--- /dev/null
+++ b/ci/config/allow-ref.sample
@@ -0,0 +1,27 @@
+#!/bin/sh
+#
+# Sample script for enabling/disabling GitHub Actions CI runs on
+# particular refs. By default, CI is run for all branches pushed to
+# GitHub. You can override this by dropping the ".sample" from the script,
+# editing it, committing, and pushing the result to the "ci-config" branch of
+# your repository:
+#
+# git checkout -b ci-config
+# cp allow-ref.sample allow-ref
+# $EDITOR allow-ref
+# git add allow-ref
+# git commit -am "implement my ci preferences"
+# git push
+#
+# This script will then be run when any refs are pushed to that repository. It
+# gets the fully qualified refname as the first argument, and should exit with
+# success only for refs for which you want to run CI.
+
+case "$1" in
+# allow one-off tests by pushing to "for-ci" or "for-ci/mybranch"
+refs/heads/for-ci*) true ;;
+# always build your integration branch
+refs/heads/my-integration-branch) true ;;
+# don't build any other branches or tags
+*) false ;;
+esac
diff --git a/ci/install-dependencies.sh b/ci/install-dependencies.sh
index 497fd32..0229a77 100755
--- a/ci/install-dependencies.sh
+++ b/ci/install-dependencies.sh
@@ -7,12 +7,16 @@
P4WHENCE=http://filehost.perforce.com/perforce/r$LINUX_P4_VERSION
LFSWHENCE=https://github.com/github/git-lfs/releases/download/v$LINUX_GIT_LFS_VERSION
+UBUNTU_COMMON_PKGS="make libssl-dev libcurl4-openssl-dev libexpat-dev
+ 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)
sudo apt-add-repository -y "ppa:ubuntu-toolchain-r/test"
sudo apt-get -q update
- sudo apt-get -q -y install language-pack-is libsvn-perl apache2
+ 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
@@ -59,14 +63,18 @@
StaticAnalysis)
sudo apt-get -q update
sudo apt-get -q -y install coccinelle libcurl4-openssl-dev libssl-dev \
- libexpat-dev gettext
+ libexpat-dev gettext make
;;
Documentation)
sudo apt-get -q update
- sudo apt-get -q -y install asciidoc xmlto docbook-xsl-ns
+ sudo apt-get -q -y install asciidoc xmlto docbook-xsl-ns make
test -n "$ALREADY_HAVE_ASCIIDOCTOR" ||
- gem install --version 1.5.8 asciidoctor
+ sudo gem install --version 1.5.8 asciidoctor
+ ;;
+linux-gcc-4.8|GETTEXT_POISON)
+ sudo apt-get -q update
+ sudo apt-get -q -y install $UBUNTU_COMMON_PKGS
;;
esac
diff --git a/ci/install-docker-dependencies.sh b/ci/install-docker-dependencies.sh
new file mode 100755
index 0000000..26a6689
--- /dev/null
+++ b/ci/install-docker-dependencies.sh
@@ -0,0 +1,18 @@
+#!/bin/sh
+#
+# Install dependencies required to build and test Git inside container
+#
+
+case "$jobname" in
+Linux32)
+ linux32 --32bit i386 sh -c '
+ apt update >/dev/null &&
+ apt install -y build-essential libcurl4-openssl-dev \
+ libssl-dev libexpat-dev gettext python >/dev/null
+ '
+ ;;
+linux-musl)
+ apk add --update build-base curl-dev openssl-dev expat-dev gettext \
+ pcre2-dev python3 musl-libintl perl-utils ncurses >/dev/null
+ ;;
+esac
diff --git a/ci/lib.sh b/ci/lib.sh
index a90d0dc..ff24c54 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
+ if test "$TRAVIS_DEBUG_MODE" = true || test true = "$GITHUB_ACTIONS"
then
return
fi
@@ -79,6 +79,9 @@
}
}
+# GitHub Action doesn't set TERM, which is required by tput
+export TERM=${TERM:-dumb}
+
# Clear MAKEFLAGS that may come from the outside world.
export MAKEFLAGS=
@@ -136,8 +139,27 @@
MAKEFLAGS="$MAKEFLAGS --jobs=10"
test windows_nt != "$CI_OS_NAME" ||
GIT_TEST_OPTS="--no-chain-lint --no-bin-wrappers $GIT_TEST_OPTS"
+elif test true = "$GITHUB_ACTIONS"
+then
+ CI_TYPE=github-actions
+ CI_BRANCH="$GITHUB_REF"
+ CI_COMMIT="$GITHUB_SHA"
+ CI_OS_NAME="$(echo "$RUNNER_OS" | tr A-Z a-z)"
+ test macos != "$CI_OS_NAME" || CI_OS_NAME=osx
+ CI_REPO_SLUG="$GITHUB_REPOSITORY"
+ CI_JOB_ID="$GITHUB_RUN_ID"
+ CC="${CC:-gcc}"
+
+ cache_dir="$HOME/none"
+
+ export GIT_PROVE_OPTS="--timer --jobs 10"
+ export GIT_TEST_OPTS="--verbose-log -x"
+ MAKEFLAGS="$MAKEFLAGS --jobs=10"
+ test windows != "$CI_OS_NAME" ||
+ GIT_TEST_OPTS="--no-chain-lint --no-bin-wrappers $GIT_TEST_OPTS"
else
echo "Could not identify CI type" >&2
+ env >&2
exit 1
fi
@@ -162,6 +184,9 @@
if [ "$jobname" = linux-gcc ]
then
export CC=gcc-8
+ MAKEFLAGS="$MAKEFLAGS PYTHON_PATH=$(which python3)"
+ else
+ MAKEFLAGS="$MAKEFLAGS PYTHON_PATH=$(which python2)"
fi
export GIT_TEST_HTTPD=true
@@ -182,6 +207,9 @@
if [ "$jobname" = osx-gcc ]
then
export CC=gcc-9
+ MAKEFLAGS="$MAKEFLAGS PYTHON_PATH=$(which python3)"
+ else
+ MAKEFLAGS="$MAKEFLAGS PYTHON_PATH=$(which python2)"
fi
# t9810 occasionally fails on Travis CI OS X
@@ -189,9 +217,17 @@
# Travis CI OS X
export GIT_SKIP_TESTS="t9810 t9816"
;;
-GIT_TEST_GETTEXT_POISON)
+GETTEXT_POISON)
export GIT_TEST_GETTEXT_POISON=true
;;
+Linux32)
+ CC=gcc
+ ;;
+linux-musl)
+ CC=gcc
+ MAKEFLAGS="$MAKEFLAGS PYTHON_PATH=/usr/bin/python3 USE_LIBPCRE2=Yes"
+ MAKEFLAGS="$MAKEFLAGS NO_REGEX=Yes ICONV_OMITS_BOM=Yes"
+ ;;
esac
MAKEFLAGS="$MAKEFLAGS CC=${CC:-cc}"
diff --git a/ci/print-test-failures.sh b/ci/print-test-failures.sh
index e688a26..92a983a 100755
--- a/ci/print-test-failures.sh
+++ b/ci/print-test-failures.sh
@@ -46,6 +46,13 @@
mv "$trash_dir" failed-test-artifacts
continue
;;
+ github-actions)
+ mkdir -p failed-test-artifacts
+ echo "::set-env name=FAILED_TEST_ARTIFACTS::t/failed-test-artifacts"
+ cp "${TEST_EXIT%.exit}.out" failed-test-artifacts/
+ tar czf failed-test-artifacts/"$test_name".trash.tar.gz "$trash_dir"
+ continue
+ ;;
*)
echo "Unhandled CI type: $CI_TYPE" >&2
exit 1
diff --git a/ci/run-build-and-tests.sh b/ci/run-build-and-tests.sh
index 4df54c4..17e25aa 100755
--- a/ci/run-build-and-tests.sh
+++ b/ci/run-build-and-tests.sh
@@ -19,6 +19,7 @@
export GIT_TEST_OE_SIZE=10
export GIT_TEST_OE_DELTA_SIZE=5
export GIT_TEST_COMMIT_GRAPH=1
+ export GIT_TEST_COMMIT_GRAPH_CHANGED_PATHS=1
export GIT_TEST_MULTI_PACK_INDEX=1
export GIT_TEST_ADD_I_USE_BUILTIN=1
make test
diff --git a/ci/run-linux32-build.sh b/ci/run-docker-build.sh
similarity index 63%
rename from ci/run-linux32-build.sh
rename to ci/run-docker-build.sh
index e3a193a..8d47a5f 100755
--- a/ci/run-linux32-build.sh
+++ b/ci/run-docker-build.sh
@@ -1,25 +1,33 @@
#!/bin/sh
#
-# Build and test Git in a 32-bit environment
+# Build and test Git inside container
#
# Usage:
-# run-linux32-build.sh <host-user-id>
+# run-docker-build.sh <host-user-id>
#
set -ex
if test $# -ne 1 || test -z "$1"
then
- echo >&2 "usage: run-linux32-build.sh <host-user-id>"
+ echo >&2 "usage: run-docker-build.sh <host-user-id>"
exit 1
fi
-# Update packages to the latest available versions
-linux32 --32bit i386 sh -c '
- apt update >/dev/null &&
- apt install -y build-essential libcurl4-openssl-dev libssl-dev \
- libexpat-dev gettext python >/dev/null
-'
+case "$jobname" in
+Linux32)
+ switch_cmd="linux32 --32bit i386"
+ ;;
+linux-musl)
+ switch_cmd=
+ useradd () { adduser -D "$@"; }
+ ;;
+*)
+ exit 1
+ ;;
+esac
+
+"${0%/*}/install-docker-dependencies.sh"
# If this script runs inside a docker container, then all commands are
# usually executed as root. Consequently, the host user might not be
@@ -51,10 +59,17 @@
fi
# Build and test
-linux32 --32bit i386 su -m -l $CI_USER -c '
+command $switch_cmd su -m -l $CI_USER -c "
set -ex
+ export DEVELOPER='$DEVELOPER'
+ export DEFAULT_TEST_TARGET='$DEFAULT_TEST_TARGET'
+ export GIT_PROVE_OPTS='$GIT_PROVE_OPTS'
+ export GIT_TEST_OPTS='$GIT_TEST_OPTS'
+ export GIT_TEST_CLONE_2GB='$GIT_TEST_CLONE_2GB'
+ export MAKEFLAGS='$MAKEFLAGS'
+ export cache_dir='$cache_dir'
cd /usr/src/git
- test -n "$cache_dir" && ln -s "$cache_dir/.prove" t/.prove
+ test -n '$cache_dir' && ln -s '$cache_dir/.prove' t/.prove
make
make test
-'
+"
diff --git a/ci/run-docker.sh b/ci/run-docker.sh
new file mode 100755
index 0000000..37fa372
--- /dev/null
+++ b/ci/run-docker.sh
@@ -0,0 +1,47 @@
+#!/bin/sh
+#
+# Download and run Docker image to build and test Git
+#
+
+. ${0%/*}/lib.sh
+
+case "$jobname" in
+Linux32)
+ CI_CONTAINER="daald/ubuntu32:xenial"
+ ;;
+linux-musl)
+ CI_CONTAINER=alpine
+ ;;
+*)
+ exit 1
+ ;;
+esac
+
+docker pull "$CI_CONTAINER"
+
+# Use the following command to debug the docker build locally:
+# <host-user-id> must be 0 if podman is used as drop-in replacement for docker
+# $ docker run -itv "${PWD}:/usr/src/git" --entrypoint /bin/sh "$CI_CONTAINER"
+# root@container:/# export jobname=<jobname>
+# root@container:/# /usr/src/git/ci/run-docker-build.sh <host-user-id>
+
+container_cache_dir=/tmp/travis-cache
+
+docker run \
+ --interactive \
+ --env DEVELOPER \
+ --env DEFAULT_TEST_TARGET \
+ --env GIT_PROVE_OPTS \
+ --env GIT_TEST_OPTS \
+ --env GIT_TEST_CLONE_2GB \
+ --env MAKEFLAGS \
+ --env jobname \
+ --env cache_dir="$container_cache_dir" \
+ --volume "${PWD}:/usr/src/git" \
+ --volume "$cache_dir:$container_cache_dir" \
+ "$CI_CONTAINER" \
+ /usr/src/git/ci/run-docker-build.sh $(id -u $USER)
+
+check_unignored_build_artifacts
+
+save_good_tree
diff --git a/ci/run-linux32-docker.sh b/ci/run-linux32-docker.sh
deleted file mode 100755
index 751acfc..0000000
--- a/ci/run-linux32-docker.sh
+++ /dev/null
@@ -1,31 +0,0 @@
-#!/bin/sh
-#
-# Download and run Docker image to build and test 32-bit Git
-#
-
-. ${0%/*}/lib.sh
-
-docker pull daald/ubuntu32:xenial
-
-# Use the following command to debug the docker build locally:
-# $ docker run -itv "${PWD}:/usr/src/git" --entrypoint /bin/bash daald/ubuntu32:xenial
-# root@container:/# /usr/src/git/ci/run-linux32-build.sh <host-user-id>
-
-container_cache_dir=/tmp/travis-cache
-
-docker run \
- --interactive \
- --env DEVELOPER \
- --env DEFAULT_TEST_TARGET \
- --env GIT_PROVE_OPTS \
- --env GIT_TEST_OPTS \
- --env GIT_TEST_CLONE_2GB \
- --env cache_dir="$container_cache_dir" \
- --volume "${PWD}:/usr/src/git" \
- --volume "$cache_dir:$container_cache_dir" \
- daald/ubuntu32:xenial \
- /usr/src/git/ci/run-linux32-build.sh $(id -u $USER)
-
-check_unignored_build_artifacts
-
-save_good_tree
diff --git a/combine-diff.c b/combine-diff.c
index d5c4d83..002e0e5 100644
--- a/combine-diff.c
+++ b/combine-diff.c
@@ -10,7 +10,7 @@
#include "log-tree.h"
#include "refs.h"
#include "userdiff.h"
-#include "sha1-array.h"
+#include "oid-array.h"
#include "revision.h"
static int compare_paths(const struct combine_diff_path *one,
diff --git a/command-list.txt b/command-list.txt
index 2087894..cbb960c 100644
--- a/command-list.txt
+++ b/command-list.txt
@@ -54,6 +54,7 @@
git-bisect mainporcelain info
git-blame ancillaryinterrogators complete
git-branch mainporcelain history
+git-bugreport ancillaryinterrogators
git-bundle mainporcelain
git-cat-file plumbinginterrogators
git-check-attr purehelpers
@@ -197,6 +198,7 @@
gitcvs-migration guide
gitdiffcore guide
giteveryday guide
+gitfaq guide
gitglossary guide
githooks guide
gitignore guide
diff --git a/commit-graph.c b/commit-graph.c
index f013a84..e3420dd 100644
--- a/commit-graph.c
+++ b/commit-graph.c
@@ -16,13 +16,33 @@
#include "hashmap.h"
#include "replace-object.h"
#include "progress.h"
+#include "bloom.h"
+#include "commit-slab.h"
+#include "shallow.h"
+
+void git_test_write_commit_graph_or_die(void)
+{
+ int flags = 0;
+ if (!git_env_bool(GIT_TEST_COMMIT_GRAPH, 0))
+ return;
+
+ if (git_env_bool(GIT_TEST_COMMIT_GRAPH_CHANGED_PATHS, 0))
+ flags = COMMIT_GRAPH_WRITE_BLOOM_FILTERS;
+
+ if (write_commit_graph_reachable(the_repository->objects->odb,
+ flags, NULL))
+ die("failed to write commit-graph under GIT_TEST_COMMIT_GRAPH");
+}
#define GRAPH_SIGNATURE 0x43475048 /* "CGPH" */
#define GRAPH_CHUNKID_OIDFANOUT 0x4f494446 /* "OIDF" */
#define GRAPH_CHUNKID_OIDLOOKUP 0x4f49444c /* "OIDL" */
#define GRAPH_CHUNKID_DATA 0x43444154 /* "CDAT" */
#define GRAPH_CHUNKID_EXTRAEDGES 0x45444745 /* "EDGE" */
+#define GRAPH_CHUNKID_BLOOMINDEXES 0x42494458 /* "BIDX" */
+#define GRAPH_CHUNKID_BLOOMDATA 0x42444154 /* "BDAT" */
#define GRAPH_CHUNKID_BASE 0x42415345 /* "BASE" */
+#define MAX_NUM_CHUNKS 7
#define GRAPH_DATA_WIDTH (the_hash_algo->rawsz + 16)
@@ -44,9 +64,51 @@
/* Remember to update object flag allocation in object.h */
#define REACHABLE (1u<<15)
-char *get_commit_graph_filename(struct object_directory *odb)
+/* Keep track of the order in which commits are added to our list. */
+define_commit_slab(commit_pos, int);
+static struct commit_pos commit_pos = COMMIT_SLAB_INIT(1, commit_pos);
+
+static void set_commit_pos(struct repository *r, const struct object_id *oid)
{
- return xstrfmt("%s/info/commit-graph", odb->path);
+ static int32_t max_pos;
+ struct commit *commit = lookup_commit(r, oid);
+
+ if (!commit)
+ return; /* should never happen, but be lenient */
+
+ *commit_pos_at(&commit_pos, commit) = max_pos++;
+}
+
+static int commit_pos_cmp(const void *va, const void *vb)
+{
+ const struct commit *a = *(const struct commit **)va;
+ const struct commit *b = *(const struct commit **)vb;
+ return commit_pos_at(&commit_pos, a) -
+ commit_pos_at(&commit_pos, b);
+}
+
+static int commit_gen_cmp(const void *va, const void *vb)
+{
+ const struct commit *a = *(const struct commit **)va;
+ const struct commit *b = *(const struct commit **)vb;
+
+ /* lower generation commits first */
+ if (a->generation < b->generation)
+ return -1;
+ else if (a->generation > b->generation)
+ return 1;
+
+ /* use date as a heuristic when generations are equal */
+ if (a->date < b->date)
+ return -1;
+ else if (a->date > b->date)
+ return 1;
+ return 0;
+}
+
+char *get_commit_graph_filename(struct object_directory *obj_dir)
+{
+ return xstrfmt("%s/info/commit-graph", obj_dir->path);
}
static char *get_split_graph_filename(struct object_directory *odb,
@@ -69,7 +131,6 @@
static struct commit_graph *alloc_commit_graph(void)
{
struct commit_graph *g = xcalloc(1, sizeof(*g));
- g->graph_fd = -1;
return g;
}
@@ -123,14 +184,13 @@
return NULL;
}
graph_map = xmmap(NULL, graph_size, PROT_READ, MAP_PRIVATE, fd, 0);
- ret = parse_commit_graph(graph_map, fd, graph_size);
+ close(fd);
+ ret = parse_commit_graph(graph_map, graph_size);
if (ret)
ret->odb = odb;
- else {
+ else
munmap(graph_map, graph_size);
- close(fd);
- }
return ret;
}
@@ -165,8 +225,7 @@
return 0;
}
-struct commit_graph *parse_commit_graph(void *graph_map, int fd,
- size_t graph_size)
+struct commit_graph *parse_commit_graph(void *graph_map, size_t graph_size)
{
const unsigned char *data, *chunk_lookup;
uint32_t i;
@@ -209,7 +268,6 @@
graph->hash_len = the_hash_algo->rawsz;
graph->num_chunks = *(unsigned char*)(data + 6);
- graph->graph_fd = fd;
graph->data = graph_map;
graph->data_len = graph_size;
@@ -224,8 +282,7 @@
if (data + graph_size - chunk_lookup <
GRAPH_CHUNKLOOKUP_WIDTH) {
error(_("commit-graph chunk lookup table entry missing; file may be incomplete"));
- free(graph);
- return NULL;
+ goto free_and_return;
}
chunk_id = get_be32(chunk_lookup + 0);
@@ -236,8 +293,7 @@
if (chunk_offset > graph_size - the_hash_algo->rawsz) {
error(_("commit-graph improper chunk offset %08x%08x"), (uint32_t)(chunk_offset >> 32),
(uint32_t)chunk_offset);
- free(graph);
- return NULL;
+ goto free_and_return;
}
switch (chunk_id) {
@@ -274,12 +330,37 @@
chunk_repeated = 1;
else
graph->chunk_base_graphs = data + chunk_offset;
+ break;
+
+ case GRAPH_CHUNKID_BLOOMINDEXES:
+ if (graph->chunk_bloom_indexes)
+ chunk_repeated = 1;
+ else
+ graph->chunk_bloom_indexes = data + chunk_offset;
+ break;
+
+ case GRAPH_CHUNKID_BLOOMDATA:
+ if (graph->chunk_bloom_data)
+ chunk_repeated = 1;
+ else {
+ uint32_t hash_version;
+ graph->chunk_bloom_data = data + chunk_offset;
+ hash_version = get_be32(data + chunk_offset);
+
+ if (hash_version != 1)
+ break;
+
+ graph->bloom_filter_settings = xmalloc(sizeof(struct bloom_filter_settings));
+ graph->bloom_filter_settings->hash_version = hash_version;
+ graph->bloom_filter_settings->num_hashes = get_be32(data + chunk_offset + 4);
+ graph->bloom_filter_settings->bits_per_entry = get_be32(data + chunk_offset + 8);
+ }
+ break;
}
if (chunk_repeated) {
error(_("commit-graph chunk id %08x appears multiple times"), chunk_id);
- free(graph);
- return NULL;
+ goto free_and_return;
}
if (last_chunk_id == GRAPH_CHUNKID_OIDLOOKUP)
@@ -292,14 +373,26 @@
last_chunk_offset = chunk_offset;
}
- hashcpy(graph->oid.hash, graph->data + graph->data_len - graph->hash_len);
-
- if (verify_commit_graph_lite(graph)) {
- free(graph);
- return NULL;
+ if (graph->chunk_bloom_indexes && graph->chunk_bloom_data) {
+ init_bloom_filters();
+ } else {
+ /* We need both the bloom chunks to exist together. Else ignore the data */
+ graph->chunk_bloom_indexes = NULL;
+ graph->chunk_bloom_data = NULL;
+ FREE_AND_NULL(graph->bloom_filter_settings);
}
+ hashcpy(graph->oid.hash, graph->data + graph->data_len - graph->hash_len);
+
+ if (verify_commit_graph_lite(graph))
+ goto free_and_return;
+
return graph;
+
+free_and_return:
+ free(graph->bloom_filter_settings);
+ free(graph);
+ return NULL;
}
static struct commit_graph *load_commit_graph_one(const char *graph_file,
@@ -788,9 +881,12 @@
unsigned append:1,
report_progress:1,
split:1,
- check_oids:1;
+ check_oids:1,
+ changed_paths:1,
+ order_by_pack:1;
const struct split_commit_graph_opts *split_opts;
+ size_t total_bloom_filter_data_size;
};
static void write_graph_chunk_fanout(struct hashfile *f,
@@ -866,7 +962,7 @@
if (edge_value >= 0)
edge_value += ctx->new_num_commits_in_base;
- else {
+ else if (ctx->new_base_graph) {
uint32_t pos;
if (find_commit_in_graph(parent->item,
ctx->new_base_graph,
@@ -897,7 +993,7 @@
if (edge_value >= 0)
edge_value += ctx->new_num_commits_in_base;
- else {
+ else if (ctx->new_base_graph) {
uint32_t pos;
if (find_commit_in_graph(parent->item,
ctx->new_base_graph,
@@ -964,7 +1060,7 @@
if (edge_value >= 0)
edge_value += ctx->new_num_commits_in_base;
- else {
+ else if (ctx->new_base_graph) {
uint32_t pos;
if (find_commit_in_graph(parent->item,
ctx->new_base_graph,
@@ -986,6 +1082,59 @@
}
}
+static void write_graph_chunk_bloom_indexes(struct hashfile *f,
+ struct write_commit_graph_context *ctx)
+{
+ struct commit **list = ctx->commits.list;
+ struct commit **last = ctx->commits.list + ctx->commits.nr;
+ uint32_t cur_pos = 0;
+ struct progress *progress = NULL;
+ int i = 0;
+
+ if (ctx->report_progress)
+ progress = start_delayed_progress(
+ _("Writing changed paths Bloom filters index"),
+ ctx->commits.nr);
+
+ while (list < last) {
+ struct bloom_filter *filter = get_bloom_filter(ctx->r, *list, 0);
+ cur_pos += filter->len;
+ display_progress(progress, ++i);
+ hashwrite_be32(f, cur_pos);
+ list++;
+ }
+
+ stop_progress(&progress);
+}
+
+static void write_graph_chunk_bloom_data(struct hashfile *f,
+ struct write_commit_graph_context *ctx,
+ const struct bloom_filter_settings *settings)
+{
+ struct commit **list = ctx->commits.list;
+ struct commit **last = ctx->commits.list + ctx->commits.nr;
+ struct progress *progress = NULL;
+ int i = 0;
+
+ if (ctx->report_progress)
+ progress = start_delayed_progress(
+ _("Writing changed paths Bloom filters data"),
+ ctx->commits.nr);
+
+ hashwrite_be32(f, settings->hash_version);
+ hashwrite_be32(f, settings->num_hashes);
+ hashwrite_be32(f, settings->bits_per_entry);
+
+ while (list < last) {
+ struct bloom_filter *filter = get_bloom_filter(ctx->r, *list, 0);
+ display_progress(progress, ++i);
+ hashwrite(f, filter->data, filter->len * sizeof(unsigned char));
+ list++;
+ }
+
+ stop_progress(&progress);
+}
+
static int oid_compare(const void *_a, const void *_b)
{
const struct object_id *a = (const struct object_id *)_a;
@@ -1017,6 +1166,8 @@
oidcpy(&(ctx->oids.list[ctx->oids.nr]), oid);
ctx->oids.nr++;
+ set_commit_pos(ctx->r, oid);
+
return 0;
}
@@ -1037,6 +1188,8 @@
{
int i;
struct commit *commit;
+ enum commit_graph_split_flags flags = ctx->split_opts ?
+ ctx->split_opts->flags : COMMIT_GRAPH_SPLIT_UNSPECIFIED;
if (ctx->report_progress)
ctx->progress = start_delayed_progress(
@@ -1066,8 +1219,9 @@
if (!commit)
continue;
if (ctx->split) {
- if (!parse_commit(commit) &&
- commit->graph_pos == COMMIT_NOT_FROM_GRAPH)
+ if ((!parse_commit(commit) &&
+ commit->graph_pos == COMMIT_NOT_FROM_GRAPH) ||
+ flags == COMMIT_GRAPH_SPLIT_REPLACE)
add_missing_parents(ctx, commit);
} else if (!parse_commit_no_graph(commit))
add_missing_parents(ctx, commit);
@@ -1133,13 +1287,45 @@
stop_progress(&ctx->progress);
}
-static int add_ref_to_list(const char *refname,
- const struct object_id *oid,
- int flags, void *cb_data)
+static void compute_bloom_filters(struct write_commit_graph_context *ctx)
{
- struct string_list *list = (struct string_list *)cb_data;
+ int i;
+ struct progress *progress = NULL;
+ struct commit **sorted_commits;
- string_list_append(list, oid_to_hex(oid));
+ init_bloom_filters();
+
+ if (ctx->report_progress)
+ progress = start_delayed_progress(
+ _("Computing commit changed paths Bloom filters"),
+ ctx->commits.nr);
+
+ ALLOC_ARRAY(sorted_commits, ctx->commits.nr);
+ COPY_ARRAY(sorted_commits, ctx->commits.list, ctx->commits.nr);
+
+ if (ctx->order_by_pack)
+ QSORT(sorted_commits, ctx->commits.nr, commit_pos_cmp);
+ else
+ QSORT(sorted_commits, ctx->commits.nr, commit_gen_cmp);
+
+ for (i = 0; i < ctx->commits.nr; i++) {
+ struct commit *c = sorted_commits[i];
+ struct bloom_filter *filter = get_bloom_filter(ctx->r, c, 1);
+ ctx->total_bloom_filter_data_size += sizeof(unsigned char) * filter->len;
+ display_progress(progress, i + 1);
+ }
+
+ free(sorted_commits);
+ stop_progress(&progress);
+}
+
+static int add_ref_to_set(const char *refname,
+ const struct object_id *oid,
+ int flags, void *cb_data)
+{
+ struct oidset *commits = (struct oidset *)cb_data;
+
+ oidset_insert(commits, oid);
return 0;
}
@@ -1147,14 +1333,14 @@
enum commit_graph_write_flags flags,
const struct split_commit_graph_opts *split_opts)
{
- struct string_list list = STRING_LIST_INIT_DUP;
+ struct oidset commits = OIDSET_INIT;
int result;
- for_each_ref(add_ref_to_list, &list);
- result = write_commit_graph(odb, NULL, &list,
+ for_each_ref(add_ref_to_set, &commits);
+ result = write_commit_graph(odb, NULL, &commits,
flags, split_opts);
- string_list_clear(&list, 0);
+ oidset_clear(&commits);
return result;
}
@@ -1203,39 +1389,46 @@
return 0;
}
-static int fill_oids_from_commit_hex(struct write_commit_graph_context *ctx,
- struct string_list *commit_hex)
+static int fill_oids_from_commits(struct write_commit_graph_context *ctx,
+ struct oidset *commits)
{
- uint32_t i;
+ uint32_t i = 0;
struct strbuf progress_title = STRBUF_INIT;
+ struct oidset_iter iter;
+ struct object_id *oid;
+
+ if (!oidset_size(commits))
+ return 0;
if (ctx->report_progress) {
strbuf_addf(&progress_title,
Q_("Finding commits for commit graph from %d ref",
"Finding commits for commit graph from %d refs",
- commit_hex->nr),
- commit_hex->nr);
+ oidset_size(commits)),
+ oidset_size(commits));
ctx->progress = start_delayed_progress(
progress_title.buf,
- commit_hex->nr);
+ oidset_size(commits));
}
- for (i = 0; i < commit_hex->nr; i++) {
- const char *end;
- struct object_id oid;
+
+ oidset_iter_init(commits, &iter);
+ while ((oid = oidset_iter_next(&iter))) {
struct commit *result;
- display_progress(ctx->progress, i + 1);
- if (!parse_oid_hex(commit_hex->items[i].string, &oid, &end) &&
- (result = lookup_commit_reference_gently(ctx->r, &oid, 1))) {
+ display_progress(ctx->progress, ++i);
+
+ result = lookup_commit_reference_gently(ctx->r, oid, 1);
+ if (result) {
ALLOC_GROW(ctx->oids.list, ctx->oids.nr + 1, ctx->oids.alloc);
oidcpy(&ctx->oids.list[ctx->oids.nr], &(result->object.oid));
ctx->oids.nr++;
} else if (ctx->check_oids) {
error(_("invalid commit object id: %s"),
- commit_hex->items[i].string);
+ oid_to_hex(oid));
return -1;
}
}
+
stop_progress(&ctx->progress);
strbuf_release(&progress_title);
@@ -1287,6 +1480,8 @@
static void copy_oids_to_commits(struct write_commit_graph_context *ctx)
{
uint32_t i;
+ enum commit_graph_split_flags flags = ctx->split_opts ?
+ ctx->split_opts->flags : COMMIT_GRAPH_SPLIT_UNSPECIFIED;
ctx->num_extra_edges = 0;
if (ctx->report_progress)
@@ -1303,11 +1498,14 @@
ALLOC_GROW(ctx->commits.list, ctx->commits.nr + 1, ctx->commits.alloc);
ctx->commits.list[ctx->commits.nr] = lookup_commit(ctx->r, &ctx->oids.list[i]);
- if (ctx->split &&
+ if (ctx->split && flags != COMMIT_GRAPH_SPLIT_REPLACE &&
ctx->commits.list[ctx->commits.nr]->graph_pos != COMMIT_NOT_FROM_GRAPH)
continue;
- parse_commit_no_graph(ctx->commits.list[ctx->commits.nr]);
+ if (ctx->split && flags == COMMIT_GRAPH_SPLIT_REPLACE)
+ parse_commit(ctx->commits.list[ctx->commits.nr]);
+ else
+ parse_commit_no_graph(ctx->commits.list[ctx->commits.nr]);
num_parents = commit_list_count(ctx->commits.list[ctx->commits.nr]->parents);
if (num_parents > 2)
@@ -1350,12 +1548,13 @@
int fd;
struct hashfile *f;
struct lock_file lk = LOCK_INIT;
- uint32_t chunk_ids[6];
- uint64_t chunk_offsets[6];
+ uint32_t chunk_ids[MAX_NUM_CHUNKS + 1];
+ uint64_t chunk_offsets[MAX_NUM_CHUNKS + 1];
const unsigned hashsz = the_hash_algo->rawsz;
struct strbuf progress_title = STRBUF_INIT;
int num_chunks = 3;
struct object_id file_hash;
+ const struct bloom_filter_settings bloom_settings = DEFAULT_BLOOM_FILTER_SETTINGS;
if (ctx->split) {
struct strbuf tmp_file = STRBUF_INIT;
@@ -1378,17 +1577,25 @@
if (ctx->split) {
char *lock_name = get_chain_filename(ctx->odb);
- hold_lock_file_for_update(&lk, lock_name, LOCK_DIE_ON_ERROR);
+ hold_lock_file_for_update_mode(&lk, lock_name,
+ LOCK_DIE_ON_ERROR, 0444);
fd = git_mkstemp_mode(ctx->graph_name, 0444);
if (fd < 0) {
- error(_("unable to create '%s'"), ctx->graph_name);
+ error(_("unable to create temporary graph layer"));
+ return -1;
+ }
+
+ if (adjust_shared_perm(ctx->graph_name)) {
+ error(_("unable to adjust shared permissions for '%s'"),
+ ctx->graph_name);
return -1;
}
f = hashfd(fd, ctx->graph_name);
} else {
- hold_lock_file_for_update(&lk, ctx->graph_name, LOCK_DIE_ON_ERROR);
+ hold_lock_file_for_update_mode(&lk, ctx->graph_name,
+ LOCK_DIE_ON_ERROR, 0444);
fd = lk.tempfile->fd;
f = hashfd(lk.tempfile->fd, lk.tempfile->filename.buf);
}
@@ -1400,6 +1607,12 @@
chunk_ids[num_chunks] = GRAPH_CHUNKID_EXTRAEDGES;
num_chunks++;
}
+ if (ctx->changed_paths) {
+ chunk_ids[num_chunks] = GRAPH_CHUNKID_BLOOMINDEXES;
+ num_chunks++;
+ chunk_ids[num_chunks] = GRAPH_CHUNKID_BLOOMDATA;
+ num_chunks++;
+ }
if (ctx->num_commit_graphs_after > 1) {
chunk_ids[num_chunks] = GRAPH_CHUNKID_BASE;
num_chunks++;
@@ -1418,6 +1631,15 @@
4 * ctx->num_extra_edges;
num_chunks++;
}
+ if (ctx->changed_paths) {
+ chunk_offsets[num_chunks + 1] = chunk_offsets[num_chunks] +
+ sizeof(uint32_t) * ctx->commits.nr;
+ num_chunks++;
+
+ chunk_offsets[num_chunks + 1] = chunk_offsets[num_chunks] +
+ sizeof(uint32_t) * 3 + ctx->total_bloom_filter_data_size;
+ num_chunks++;
+ }
if (ctx->num_commit_graphs_after > 1) {
chunk_offsets[num_chunks + 1] = chunk_offsets[num_chunks] +
hashsz * (ctx->num_commit_graphs_after - 1);
@@ -1455,6 +1677,10 @@
write_graph_chunk_data(f, hashsz, ctx);
if (ctx->num_extra_edges)
write_graph_chunk_extra_edges(f, ctx);
+ if (ctx->changed_paths) {
+ write_graph_chunk_bloom_indexes(f, ctx);
+ write_graph_chunk_bloom_data(f, ctx, &bloom_settings);
+ }
if (ctx->num_commit_graphs_after > 1 &&
write_graph_chunk_base(f, ctx)) {
return -1;
@@ -1488,8 +1714,12 @@
}
if (ctx->base_graph_name) {
- const char *dest = ctx->commit_graph_filenames_after[
- ctx->num_commit_graphs_after - 2];
+ const char *dest;
+ int idx = ctx->num_commit_graphs_after - 1;
+ if (ctx->num_commit_graphs_after > 1)
+ idx--;
+
+ dest = ctx->commit_graph_filenames_after[idx];
if (strcmp(ctx->base_graph_name, dest)) {
result = rename(ctx->base_graph_name, dest);
@@ -1529,6 +1759,7 @@
{
struct commit_graph *g;
uint32_t num_commits;
+ enum commit_graph_split_flags flags = COMMIT_GRAPH_SPLIT_UNSPECIFIED;
uint32_t i;
int max_commits = 0;
@@ -1539,24 +1770,36 @@
if (ctx->split_opts->size_multiple)
size_mult = ctx->split_opts->size_multiple;
+
+ flags = ctx->split_opts->flags;
}
g = ctx->r->objects->commit_graph;
num_commits = ctx->commits.nr;
- ctx->num_commit_graphs_after = ctx->num_commit_graphs_before + 1;
+ if (flags == COMMIT_GRAPH_SPLIT_REPLACE)
+ ctx->num_commit_graphs_after = 1;
+ else
+ ctx->num_commit_graphs_after = ctx->num_commit_graphs_before + 1;
- while (g && (g->num_commits <= size_mult * num_commits ||
- (max_commits && num_commits > max_commits))) {
- if (g->odb != ctx->odb)
- break;
+ if (flags != COMMIT_GRAPH_SPLIT_MERGE_PROHIBITED &&
+ flags != COMMIT_GRAPH_SPLIT_REPLACE) {
+ while (g && (g->num_commits <= size_mult * num_commits ||
+ (max_commits && num_commits > max_commits))) {
+ if (g->odb != ctx->odb)
+ break;
- num_commits += g->num_commits;
- g = g->base_graph;
+ num_commits += g->num_commits;
+ g = g->base_graph;
- ctx->num_commit_graphs_after--;
+ ctx->num_commit_graphs_after--;
+ }
}
- ctx->new_base_graph = g;
+ if (flags != COMMIT_GRAPH_SPLIT_REPLACE)
+ ctx->new_base_graph = g;
+ else if (ctx->num_commit_graphs_after != 1)
+ BUG("split_graph_merge_strategy: num_commit_graphs_after "
+ "should be 1 with --split=replace");
if (ctx->num_commit_graphs_after == 2) {
char *old_graph_name = get_commit_graph_filename(g->odb);
@@ -1570,8 +1813,8 @@
free(old_graph_name);
}
- ALLOC_ARRAY(ctx->commit_graph_filenames_after, ctx->num_commit_graphs_after);
- ALLOC_ARRAY(ctx->commit_graph_hash_after, ctx->num_commit_graphs_after);
+ CALLOC_ARRAY(ctx->commit_graph_filenames_after, ctx->num_commit_graphs_after);
+ CALLOC_ARRAY(ctx->commit_graph_hash_after, ctx->num_commit_graphs_after);
for (i = 0; i < ctx->num_commit_graphs_after &&
i < ctx->num_commit_graphs_before; i++)
@@ -1707,7 +1950,7 @@
timestamp_t expire_time = time(NULL);
if (ctx->split_opts && ctx->split_opts->expire_time)
- expire_time -= ctx->split_opts->expire_time;
+ expire_time = ctx->split_opts->expire_time;
if (!ctx->split) {
char *chain_file_name = get_chain_filename(ctx->odb);
unlink(chain_file_name);
@@ -1756,13 +1999,14 @@
int write_commit_graph(struct object_directory *odb,
struct string_list *pack_indexes,
- struct string_list *commit_hex,
+ struct oidset *commits,
enum commit_graph_write_flags flags,
const struct split_commit_graph_opts *split_opts)
{
struct write_commit_graph_context *ctx;
uint32_t i, count_distinct = 0;
int res = 0;
+ int replace = 0;
if (!commit_graph_compatible(the_repository))
return 0;
@@ -1775,6 +2019,8 @@
ctx->split = flags & COMMIT_GRAPH_WRITE_SPLIT ? 1 : 0;
ctx->check_oids = flags & COMMIT_GRAPH_WRITE_CHECK_OIDS ? 1 : 0;
ctx->split_opts = split_opts;
+ ctx->changed_paths = flags & COMMIT_GRAPH_WRITE_BLOOM_FILTERS ? 1 : 0;
+ ctx->total_bloom_filter_data_size = 0;
if (ctx->split) {
struct commit_graph *g;
@@ -1797,6 +2043,9 @@
g = g->base_graph;
}
}
+
+ if (ctx->split_opts)
+ replace = ctx->split_opts->flags & COMMIT_GRAPH_SPLIT_REPLACE;
}
ctx->approx_nr_objects = approximate_object_count();
@@ -1824,17 +2073,20 @@
}
if (pack_indexes) {
+ ctx->order_by_pack = 1;
if ((res = fill_oids_from_packs(ctx, pack_indexes)))
goto cleanup;
}
- if (commit_hex) {
- if ((res = fill_oids_from_commit_hex(ctx, commit_hex)))
+ if (commits) {
+ if ((res = fill_oids_from_commits(ctx, commits)))
goto cleanup;
}
- if (!pack_indexes && !commit_hex)
+ if (!pack_indexes && !commits) {
+ ctx->order_by_pack = 1;
fill_oids_from_all_packs(ctx);
+ }
close_reachable(ctx);
@@ -1857,18 +2109,22 @@
goto cleanup;
}
- if (!ctx->commits.nr)
+ if (!ctx->commits.nr && !replace)
goto cleanup;
if (ctx->split) {
split_graph_merge_strategy(ctx);
- merge_commit_graphs(ctx);
+ if (!replace)
+ merge_commit_graphs(ctx);
} else
ctx->num_commit_graphs_after = 1;
compute_generation_numbers(ctx);
+ if (ctx->changed_paths)
+ compute_bloom_filters(ctx);
+
res = write_commit_graph_file(ctx);
if (ctx->split)
@@ -2088,12 +2344,12 @@
{
if (!g)
return;
- if (g->graph_fd >= 0) {
+ if (g->data) {
munmap((void *)g->data, g->data_len);
g->data = NULL;
- close(g->graph_fd);
}
free(g->filename);
+ free(g->bloom_filter_settings);
free(g);
}
diff --git a/commit-graph.h b/commit-graph.h
index e87a6f6..4212766 100644
--- a/commit-graph.h
+++ b/commit-graph.h
@@ -6,11 +6,23 @@
#include "string-list.h"
#include "cache.h"
#include "object-store.h"
+#include "oidset.h"
#define GIT_TEST_COMMIT_GRAPH "GIT_TEST_COMMIT_GRAPH"
#define GIT_TEST_COMMIT_GRAPH_DIE_ON_LOAD "GIT_TEST_COMMIT_GRAPH_DIE_ON_LOAD"
+#define GIT_TEST_COMMIT_GRAPH_CHANGED_PATHS "GIT_TEST_COMMIT_GRAPH_CHANGED_PATHS"
+
+/*
+ * This method is only used to enhance coverage of the commit-graph
+ * feature in the test suite with the GIT_TEST_COMMIT_GRAPH and
+ * GIT_TEST_COMMIT_GRAPH_CHANGED_PATHS environment variables. Do not
+ * call this method oustide of a builtin, and only if you know what
+ * you are doing!
+ */
+void git_test_write_commit_graph_or_die(void);
struct commit;
+struct bloom_filter_settings;
char *get_commit_graph_filename(struct object_directory *odb);
int open_commit_graph(const char *graph_file, int *fd, struct stat *st);
@@ -39,8 +51,6 @@
const struct commit *c);
struct commit_graph {
- int graph_fd;
-
const unsigned char *data;
size_t data_len;
@@ -59,14 +69,17 @@
const unsigned char *chunk_commit_data;
const unsigned char *chunk_extra_edges;
const unsigned char *chunk_base_graphs;
+ const unsigned char *chunk_bloom_indexes;
+ const unsigned char *chunk_bloom_data;
+
+ struct bloom_filter_settings *bloom_filter_settings;
};
struct commit_graph *load_commit_graph_one_fd_st(int fd, struct stat *st,
struct object_directory *odb);
struct commit_graph *read_commit_graph_one(struct repository *r,
struct object_directory *odb);
-struct commit_graph *parse_commit_graph(void *graph_map, int fd,
- size_t graph_size);
+struct commit_graph *parse_commit_graph(void *graph_map, size_t graph_size);
/*
* Return 1 if and only if the repository has a commit-graph
@@ -79,13 +92,21 @@
COMMIT_GRAPH_WRITE_PROGRESS = (1 << 1),
COMMIT_GRAPH_WRITE_SPLIT = (1 << 2),
/* Make sure that each OID in the input is a valid commit OID. */
- COMMIT_GRAPH_WRITE_CHECK_OIDS = (1 << 3)
+ COMMIT_GRAPH_WRITE_CHECK_OIDS = (1 << 3),
+ COMMIT_GRAPH_WRITE_BLOOM_FILTERS = (1 << 4),
+};
+
+enum commit_graph_split_flags {
+ COMMIT_GRAPH_SPLIT_UNSPECIFIED = 0,
+ COMMIT_GRAPH_SPLIT_MERGE_PROHIBITED = 1,
+ COMMIT_GRAPH_SPLIT_REPLACE = 2
};
struct split_commit_graph_opts {
int size_multiple;
int max_commits;
timestamp_t expire_time;
+ enum commit_graph_split_flags flags;
};
/*
@@ -99,7 +120,7 @@
const struct split_commit_graph_opts *split_opts);
int write_commit_graph(struct object_directory *odb,
struct string_list *pack_indexes,
- struct string_list *commit_hex,
+ struct oidset *commits,
enum commit_graph_write_flags flags,
const struct split_commit_graph_opts *split_opts);
diff --git a/commit-slab-decl.h b/commit-slab-decl.h
index adc7b46..bfbed15 100644
--- a/commit-slab-decl.h
+++ b/commit-slab-decl.h
@@ -1,5 +1,5 @@
-#ifndef COMMIT_SLAB_HDR_H
-#define COMMIT_SLAB_HDR_H
+#ifndef COMMIT_SLAB_DECL_H
+#define COMMIT_SLAB_DECL_H
/* allocate ~512kB at once, allowing for malloc overhead */
#ifndef COMMIT_SLAB_SIZE
@@ -40,4 +40,4 @@
declare_commit_slab(slabname, elemtype); \
declare_commit_slab_prototypes(slabname, elemtype)
-#endif /* COMMIT_SLAB_HDR_H */
+#endif /* COMMIT_SLAB_DECL_H */
diff --git a/commit-slab.h b/commit-slab.h
index 69bf0c8..05b3f28 100644
--- a/commit-slab.h
+++ b/commit-slab.h
@@ -24,7 +24,12 @@
* - int *indegree_peek(struct indegree *, struct commit *);
*
* This function is similar to indegree_at(), but it will return NULL
- * until a call to indegree_at() was made for the commit.
+ * if the location to store the data associated with the given commit
+ * has not been allocated yet.
+ * Note that the location to store the data might have already been
+ * allocated even if no indegree_at() call has been made for that commit
+ * yet; in this case this function returns a pointer to a
+ * zero-initialized location.
*
* - void init_indegree(struct indegree *);
* void init_indegree_with_stride(struct indegree *, int);
diff --git a/commit.c b/commit.c
index a6cfa41..87686a7 100644
--- a/commit.c
+++ b/commit.c
@@ -20,6 +20,7 @@
#include "refs.h"
#include "commit-reach.h"
#include "run-command.h"
+#include "shallow.h"
static struct commit_extra_header *read_commit_extra_header_lines(const char *buf, size_t len, const char **);
@@ -110,7 +111,7 @@
return commit_graft_table[index]->oid.hash;
}
-static int commit_graft_pos(struct repository *r, const unsigned char *sha1)
+int commit_graft_pos(struct repository *r, const unsigned char *sha1)
{
return sha1_pos(sha1, r->parsed_objects->grafts,
r->parsed_objects->grafts_nr,
@@ -245,19 +246,6 @@
return ret;
}
-int unregister_shallow(const struct object_id *oid)
-{
- int pos = commit_graft_pos(the_repository, oid->hash);
- if (pos < 0)
- return -1;
- if (pos + 1 < the_repository->parsed_objects->grafts_nr)
- MOVE_ARRAY(the_repository->parsed_objects->grafts + pos,
- the_repository->parsed_objects->grafts + pos + 1,
- the_repository->parsed_objects->grafts_nr - pos - 1);
- the_repository->parsed_objects->grafts_nr--;
- return 0;
-}
-
struct commit_buffer {
void *buffer;
unsigned long size;
@@ -927,12 +915,22 @@
struct commit_list *bases;
int i;
struct commit *ret = NULL;
+ char *full_refname;
+
+ switch (dwim_ref(refname, strlen(refname), &oid, &full_refname)) {
+ case 0:
+ die("No such ref: '%s'", refname);
+ case 1:
+ break; /* good */
+ default:
+ die("Ambiguous refname: '%s'", refname);
+ }
memset(&revs, 0, sizeof(revs));
revs.initial = 1;
- for_each_reflog_ent(refname, collect_one_reflog_ent, &revs);
+ for_each_reflog_ent(full_refname, collect_one_reflog_ent, &revs);
- if (!revs.nr && !get_oid(refname, &oid))
+ if (!revs.nr)
add_one_commit(&oid, &revs);
for (i = 0; i < revs.nr; i++)
@@ -958,17 +956,26 @@
cleanup_return:
free_commit_list(bases);
+ free(full_refname);
return ret;
}
-static const char gpg_sig_header[] = "gpgsig";
-static const int gpg_sig_header_len = sizeof(gpg_sig_header) - 1;
+/*
+ * Indexed by hash algorithm identifier.
+ */
+static const char *gpg_sig_headers[] = {
+ NULL,
+ "gpgsig",
+ "gpgsig-sha256",
+};
static int do_sign_commit(struct strbuf *buf, const char *keyid)
{
struct strbuf sig = STRBUF_INIT;
int inspos, copypos;
const char *eoh;
+ const char *gpg_sig_header = gpg_sig_headers[hash_algo_by_ptr(the_hash_algo)];
+ int gpg_sig_header_len = strlen(gpg_sig_header);
/* find the end of the header */
eoh = strstr(buf->buf, "\n\n");
@@ -1010,6 +1017,8 @@
const char *buffer = get_commit_buffer(commit, &size);
int in_signature, saw_signature = -1;
const char *line, *tail;
+ const char *gpg_sig_header = gpg_sig_headers[hash_algo_by_ptr(the_hash_algo)];
+ int gpg_sig_header_len = strlen(gpg_sig_header);
line = buffer;
tail = buffer + size;
@@ -1056,11 +1065,17 @@
if (in_signature && line[0] == ' ')
sig_end = next;
- else if (starts_with(line, gpg_sig_header) &&
- line[gpg_sig_header_len] == ' ') {
- sig_start = line;
- sig_end = next;
- in_signature = 1;
+ else if (starts_with(line, "gpgsig")) {
+ int i;
+ for (i = 1; i < GIT_HASH_NALGOS; i++) {
+ const char *p;
+ if (skip_prefix(line, gpg_sig_headers[i], &p) &&
+ *p == ' ') {
+ sig_start = line;
+ sig_end = next;
+ in_signature = 1;
+ }
+ }
} else {
if (*line == '\n')
/* dump the whole remainder of the buffer */
diff --git a/commit.h b/commit.h
index 008a0fa..1b2dea5 100644
--- a/commit.h
+++ b/commit.h
@@ -236,6 +236,8 @@
typedef int (*each_commit_graft_fn)(const struct commit_graft *, void *);
struct commit_graft *read_graft_line(struct strbuf *line);
+/* commit_graft_pos returns an index into r->parsed_objects->grafts. */
+int commit_graft_pos(struct repository *r, const unsigned char *sha1);
int register_commit_graft(struct repository *r, struct commit_graft *, int);
void prepare_commit_graft(struct repository *r);
struct commit_graft *lookup_commit_graft(struct repository *r, const struct object_id *oid);
@@ -247,53 +249,7 @@
struct oid_array;
struct ref;
-int register_shallow(struct repository *r, const struct object_id *oid);
-int unregister_shallow(const struct object_id *oid);
int for_each_commit_graft(each_commit_graft_fn, void *);
-int is_repository_shallow(struct repository *r);
-struct commit_list *get_shallow_commits(struct object_array *heads,
- int depth, int shallow_flag, int not_shallow_flag);
-struct commit_list *get_shallow_commits_by_rev_list(
- int ac, const char **av, int shallow_flag, int not_shallow_flag);
-void set_alternate_shallow_file(struct repository *r, const char *path, int override);
-int write_shallow_commits(struct strbuf *out, int use_pack_protocol,
- const struct oid_array *extra);
-void setup_alternate_shallow(struct lock_file *shallow_lock,
- const char **alternate_shallow_file,
- const struct oid_array *extra);
-const char *setup_temporary_shallow(const struct oid_array *extra);
-void advertise_shallow_grafts(int);
-
-/*
- * Initialize with prepare_shallow_info() or zero-initialize (equivalent to
- * prepare_shallow_info with a NULL oid_array).
- */
-struct shallow_info {
- struct oid_array *shallow;
- int *ours, nr_ours;
- int *theirs, nr_theirs;
- struct oid_array *ref;
-
- /* for receive-pack */
- uint32_t **used_shallow;
- int *need_reachability_test;
- int *reachable;
- int *shallow_ref;
- struct commit **commits;
- int nr_commits;
-};
-
-void prepare_shallow_info(struct shallow_info *, struct oid_array *);
-void clear_shallow_info(struct shallow_info *);
-void remove_nonexistent_theirs_shallow(struct shallow_info *);
-void assign_shallow_commits_to_refs(struct shallow_info *info,
- uint32_t **used,
- int *ref_status);
-int delayed_reachability_test(struct shallow_info *si, int c);
-#define PRUNE_SHOW_ONLY 1
-#define PRUNE_QUICK 2
-void prune_shallow(unsigned options);
-extern struct trace_key trace_shallow;
int interactive_add(int argc, const char **argv, const char *prefix, int patch);
int run_add_interactive(const char *revision, const char *patch_mode,
diff --git a/compat/compiler.h b/compat/compiler.h
new file mode 100644
index 0000000..10dbb65
--- /dev/null
+++ b/compat/compiler.h
@@ -0,0 +1,41 @@
+#ifndef COMPILER_H
+#define COMPILER_H
+
+#include "git-compat-util.h"
+#include "strbuf.h"
+
+#ifdef __GLIBC__
+#include <gnu/libc-version.h>
+#endif
+
+static inline void get_compiler_info(struct strbuf *info)
+{
+ int len = info->len;
+#ifdef __clang__
+ strbuf_addf(info, "clang: %s\n", __clang_version__);
+#elif defined(__GNUC__)
+ strbuf_addf(info, "gnuc: %d.%d\n", __GNUC__, __GNUC_MINOR__);
+#endif
+
+#ifdef _MSC_VER
+ strbuf_addf(info, "MSVC version: %02d.%02d.%05d\n",
+ _MSC_VER / 100, _MSC_VER % 100, _MSC_FULL_VER % 100000);
+#endif
+
+ if (len == info->len)
+ strbuf_addstr(info, _("no compiler information available\n"));
+}
+
+static inline void get_libc_info(struct strbuf *info)
+{
+ int len = info->len;
+
+#ifdef __GLIBC__
+ strbuf_addf(info, "glibc: %s\n", gnu_get_libc_version());
+#endif
+
+ if (len == info->len)
+ strbuf_addstr(info, _("no libc information available\n"));
+}
+
+#endif /* COMPILER_H */
diff --git a/compat/gmtime.c b/compat/gmtime.c
deleted file mode 100644
index e8362dd..0000000
--- a/compat/gmtime.c
+++ /dev/null
@@ -1,29 +0,0 @@
-#include "../git-compat-util.h"
-#undef gmtime
-#undef gmtime_r
-
-struct tm *git_gmtime(const time_t *timep)
-{
- static struct tm result;
- return git_gmtime_r(timep, &result);
-}
-
-struct tm *git_gmtime_r(const time_t *timep, struct tm *result)
-{
- struct tm *ret;
-
- memset(result, 0, sizeof(*result));
- ret = gmtime_r(timep, result);
-
- /*
- * Rather than NULL, FreeBSD gmtime simply leaves the "struct tm"
- * untouched when it encounters overflow. Since "mday" cannot otherwise
- * be zero, we can test this very quickly.
- */
- if (ret && !ret->tm_mday) {
- ret = NULL;
- errno = EOVERFLOW;
- }
-
- return ret;
-}
diff --git a/compat/mingw.c b/compat/mingw.c
index d14065d..8ee0b64 100644
--- a/compat/mingw.c
+++ b/compat/mingw.c
@@ -460,8 +460,21 @@
handle = CreateFileW(wfilename, FILE_APPEND_DATA,
FILE_SHARE_WRITE | FILE_SHARE_READ,
NULL, create, FILE_ATTRIBUTE_NORMAL, NULL);
- if (handle == INVALID_HANDLE_VALUE)
- return errno = err_win_to_posix(GetLastError()), -1;
+ if (handle == INVALID_HANDLE_VALUE) {
+ DWORD err = GetLastError();
+
+ /*
+ * Some network storage solutions (e.g. Isilon) might return
+ * ERROR_INVALID_PARAMETER instead of expected error
+ * ERROR_PATH_NOT_FOUND, which results in an unknown error. If
+ * so, let's turn the error to ERROR_PATH_NOT_FOUND instead.
+ */
+ if (err == ERROR_INVALID_PARAMETER)
+ err = ERROR_PATH_NOT_FOUND;
+
+ errno = err_win_to_posix(err);
+ return -1;
+ }
/*
* No O_APPEND here, because the CRT uses it only to reset the
@@ -964,7 +977,16 @@
size_t mingw_strftime(char *s, size_t max,
const char *format, const struct tm *tm)
{
- size_t ret = strftime(s, max, format, tm);
+ /* 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,
+ const char *, const struct tm *);
+
+ if (INIT_PROC_ADDR(strftime))
+ ret = strftime(s, max, format, tm);
+ else
+ ret = fallback(s, max, format, tm);
if (!ret && errno == EINVAL)
die("invalid strftime format: '%s'", format);
@@ -1479,6 +1501,7 @@
const char *(*quote_arg)(const char *arg) =
is_msys2_sh(cmd ? cmd : *argv) ?
quote_arg_msys2 : quote_arg_msvc;
+ const char *strace_env;
/* Make sure to override previous errors, if any */
errno = 0;
@@ -1562,6 +1585,31 @@
free(quoted);
}
+ strace_env = getenv("GIT_STRACE_COMMANDS");
+ if (strace_env) {
+ char *p = path_lookup("strace.exe", 1);
+ if (!p)
+ return error("strace not found!");
+ if (xutftowcs_path(wcmd, p) < 0) {
+ free(p);
+ return -1;
+ }
+ free(p);
+ if (!strcmp("1", strace_env) ||
+ !strcasecmp("yes", strace_env) ||
+ !strcasecmp("true", strace_env))
+ strbuf_insert(&args, 0, "strace ", 7);
+ else {
+ const char *quoted = quote_arg(strace_env);
+ struct strbuf buf = STRBUF_INIT;
+ strbuf_addf(&buf, "strace -o %s ", quoted);
+ if (quoted != strace_env)
+ free((char *)quoted);
+ strbuf_insert(&args, 0, buf.buf, buf.len);
+ strbuf_release(&buf);
+ }
+ }
+
ALLOC_ARRAY(wargs, st_add(st_mult(2, args.len), 1));
xutftowcs(wargs, args.buf, 2 * args.len + 1);
strbuf_release(&args);
@@ -2581,12 +2629,14 @@
continue;
}
break;
- case 'c': case 'C': /* COM<N>, CON, CONIN$, CONOUT$ */
+ case 'c': case 'C':
+ /* COM1 ... COM9, CON, CONIN$, CONOUT$ */
if ((c = path[++i]) != 'o' && c != 'O')
goto not_a_reserved_name;
c = path[++i];
- if (c == 'm' || c == 'M') { /* COM<N> */
- if (!isdigit(path[++i]))
+ if (c == 'm' || c == 'M') { /* COM1 ... COM9 */
+ c = path[++i];
+ if (c < '1' || c > '9')
goto not_a_reserved_name;
} else if (c == 'n' || c == 'N') { /* CON */
c = path[i + 1];
diff --git a/compat/regex/regex.c b/compat/regex/regex.c
index f3e03a9..e6f4a5d 100644
--- a/compat/regex/regex.c
+++ b/compat/regex/regex.c
@@ -60,6 +60,7 @@
#undefs RE_DUP_MAX and sets it to the right value. */
#include <limits.h>
#include <stdint.h>
+#include <stdlib.h>
#ifdef GAWK
#undef alloca
diff --git a/compat/regex/regex_internal.h b/compat/regex/regex_internal.h
index 3ee8aae..0bad8b8 100644
--- a/compat/regex/regex_internal.h
+++ b/compat/regex/regex_internal.h
@@ -23,7 +23,6 @@
#include <assert.h>
#include <ctype.h>
#include <stdio.h>
-#include <stdlib.h>
#include <string.h>
#if defined HAVE_LANGINFO_H || defined HAVE_LANGINFO_CODESET || defined _LIBC
diff --git a/compat/vcbuild/README b/compat/vcbuild/README
index 1b6dabf..42292e7 100644
--- a/compat/vcbuild/README
+++ b/compat/vcbuild/README
@@ -92,8 +92,8 @@
the git operations.
3. Inside Git's directory run the command:
- make command-list.h
- to generate the command-list.h file needed to compile git.
+ make command-list.h config-list.h
+ to generate the header file needed to compile git.
4. Then either build Git with the GNU Make Makefile in the Git projects
root
diff --git a/compat/win32/path-utils.h b/compat/win32/path-utils.h
index f2e7087..bba2b64 100644
--- a/compat/win32/path-utils.h
+++ b/compat/win32/path-utils.h
@@ -20,6 +20,17 @@
return ret;
}
#define find_last_dir_sep win32_find_last_dir_sep
+static inline int win32_has_dir_sep(const char *path)
+{
+ /*
+ * See how long the non-separator part of the given path is, and
+ * if and only if it covers the whole path (i.e. path[len] is NUL),
+ * there is no separator in the path---otherwise there is a separator.
+ */
+ size_t len = strcspn(path, "/\\");
+ return !!path[len];
+}
+#define has_dir_sep(path) win32_has_dir_sep(path)
int win32_offset_1st_component(const char *path);
#define offset_1st_component win32_offset_1st_component
diff --git a/config.c b/config.c
index d17d2bd..8db9c77 100644
--- a/config.c
+++ b/config.c
@@ -37,6 +37,7 @@
enum config_error_action default_error_action;
int linenr;
int eof;
+ size_t total_len;
struct strbuf value;
struct strbuf var;
unsigned subsection_case_sensitive : 1;
@@ -309,7 +310,7 @@
{
struct config_include_data *inc = data;
const char *cond, *key;
- int cond_len;
+ size_t cond_len;
int ret;
/*
@@ -358,12 +359,13 @@
*
* store_key - pointer to char* which will hold a copy of the key with
* lowercase section and variable name
- * baselen - pointer to int which will hold the length of the
+ * baselen - pointer to size_t which will hold the length of the
* section + subsection part, can be NULL
*/
-static int git_config_parse_key_1(const char *key, char **store_key, int *baselen_, int quiet)
+static int git_config_parse_key_1(const char *key, char **store_key, size_t *baselen_, int quiet)
{
- int i, dot, baselen;
+ size_t i, baselen;
+ int dot;
const char *last_dot = strrchr(key, '.');
/*
@@ -425,7 +427,7 @@
return -CONFIG_INVALID_KEY;
}
-int git_config_parse_key(const char *key, char **store_key, int *baselen)
+int git_config_parse_key(const char *key, char **store_key, size_t *baselen)
{
return git_config_parse_key_1(key, store_key, baselen, 0);
}
@@ -523,6 +525,19 @@
c = '\r';
}
}
+
+ if (c != EOF && ++cf->total_len > INT_MAX) {
+ /*
+ * This is an absurdly long config file; refuse to parse
+ * further in order to protect downstream code from integer
+ * overflows. Note that we can't return an error specifically,
+ * but we can mark EOF and put trash in the return value,
+ * which will trigger a parse error.
+ */
+ cf->eof = 1;
+ return 0;
+ }
+
if (c == '\n')
cf->linenr++;
if (c == EOF) {
@@ -728,7 +743,7 @@
const struct config_options *opts)
{
int comment = 0;
- int baselen = 0;
+ size_t baselen = 0;
struct strbuf *var = &cf->var;
int error_return = 0;
char *error_msg = NULL;
@@ -1539,6 +1554,7 @@
top->prev = cf;
top->linenr = 1;
top->eof = 0;
+ top->total_len = 0;
strbuf_init(&top->value, 1024);
strbuf_init(&top->var, 1024);
cf = top;
@@ -2383,7 +2399,7 @@
*/
struct config_store_data {
- int baselen;
+ size_t baselen;
char *key;
int do_not_match;
regex_t *value_regex;
@@ -2509,7 +2525,7 @@
const struct config_store_data *store)
{
const char *dot;
- int i;
+ size_t i;
struct strbuf sb = STRBUF_INIT;
dot = memchr(key, '.', store->baselen);
@@ -2522,7 +2538,9 @@
}
strbuf_addstr(&sb, "\"]\n");
} else {
- strbuf_addf(&sb, "[%.*s]\n", store->baselen, key);
+ strbuf_addch(&sb, '[');
+ strbuf_add(&sb, key, store->baselen);
+ strbuf_addstr(&sb, "]\n");
}
return sb;
@@ -2545,7 +2563,6 @@
{
int i;
ssize_t ret;
- int length = strlen(key + store->baselen + 1);
const char *quote = "";
struct strbuf sb = STRBUF_INIT;
@@ -2564,8 +2581,7 @@
if (i && value[i - 1] == ' ')
quote = "\"";
- strbuf_addf(&sb, "\t%.*s = %s",
- length, key + store->baselen + 1, quote);
+ strbuf_addf(&sb, "\t%s = %s", key + store->baselen + 1, quote);
for (i = 0; value[i]; i++)
switch (value[i]) {
@@ -3238,7 +3254,7 @@
int parse_config_key(const char *var,
const char *section,
- const char **subsection, int *subsection_len,
+ const char **subsection, size_t *subsection_len,
const char **key)
{
const char *dot;
diff --git a/config.h b/config.h
index 9b3773f..0608744 100644
--- a/config.h
+++ b/config.h
@@ -254,7 +254,7 @@
*/
void git_config_set(const char *, const char *);
-int git_config_parse_key(const char *, char **, int *);
+int git_config_parse_key(const char *, char **, size_t *);
int git_config_key_is_valid(const char *key);
int git_config_set_multivar_gently(const char *, const char *, const char *, int);
void git_config_set_multivar(const char *, const char *, const char *, int);
@@ -359,7 +359,7 @@
*/
int parse_config_key(const char *var,
const char *section,
- const char **subsection, int *subsection_len,
+ const char **subsection, size_t *subsection_len,
const char **key);
/**
diff --git a/config.mak.dev b/config.mak.dev
index 89b218d..cd4a82a 100644
--- a/config.mak.dev
+++ b/config.mak.dev
@@ -16,6 +16,8 @@
DEVELOPER_CFLAGS += -Wunused
DEVELOPER_CFLAGS += -Wvla
+DEVELOPER_CFLAGS += -DENABLE_SHA256
+
ifndef COMPILER_FEATURES
COMPILER_FEATURES := $(shell ./detect-compiler $(CC))
endif
diff --git a/config.mak.uname b/config.mak.uname
index 0ab8e00..c7eba69 100644
--- a/config.mak.uname
+++ b/config.mak.uname
@@ -133,8 +133,17 @@
HAVE_BSD_SYSCTL = YesPlease
FREAD_READS_DIRECTORIES = UnfortunatelyYes
HAVE_NS_GET_EXECUTABLE_PATH = YesPlease
- BASIC_CFLAGS += -I/usr/local/include
- BASIC_LDFLAGS += -L/usr/local/lib
+
+ # Workaround for `gettext` being keg-only and not even being linked via
+ # `brew link --force gettext`, should be obsolete as of
+ # https://github.com/Homebrew/homebrew-core/pull/53489
+ ifeq ($(shell test -d /usr/local/opt/gettext/ && echo y),y)
+ BASIC_CFLAGS += -I/usr/local/include -I/usr/local/opt/gettext/include
+ BASIC_LDFLAGS += -L/usr/local/lib -L/usr/local/opt/gettext/lib
+ ifeq ($(shell test -x /usr/local/opt/gettext/bin/msgfmt && echo y),y)
+ MSGFMT = /usr/local/opt/gettext/bin/msgfmt
+ endif
+ endif
endif
ifeq ($(uname_S),SunOS)
NEEDS_SOCKET = YesPlease
@@ -237,7 +246,6 @@
PYTHON_PATH = /usr/local/bin/python
PERL_PATH = /usr/local/bin/perl
HAVE_PATHS_H = YesPlease
- GMTIME_UNRELIABLE_ERRORS = UnfortunatelyYes
HAVE_BSD_SYSCTL = YesPlease
HAVE_BSD_KERN_PROC_SYSCTL = YesPlease
PAGER_ENV = LESS=FRX LV=-c MORE=FRX
@@ -308,6 +316,7 @@
NO_STRLCPY = YesPlease
HAVE_PATHS_H = YesPlease
LIBC_CONTAINS_LIBINTL = YesPlease
+ FREAD_READS_DIRECTORIES = UnfortunatelyYes
endif
ifeq ($(uname_S),IRIX)
NO_SETENV = YesPlease
@@ -721,9 +730,9 @@
echo '</Project>') >git-remote-http/LinkOrCopyRemoteHttp.targets
git add -f git/LinkOrCopyBuiltins.targets git-remote-http/LinkOrCopyRemoteHttp.targets
- # Add command-list.h
- $(MAKE) MSVC=1 SKIP_VCPKG=1 prefix=/mingw64 command-list.h
- git add -f command-list.h
+ # Add command-list.h and config-list.h
+ $(MAKE) MSVC=1 SKIP_VCPKG=1 prefix=/mingw64 config-list.h command-list.h
+ git add -f config-list.h command-list.h
# Add scripts
rm -f perl/perl.mak
diff --git a/connect.c b/connect.c
index b6451ab..23013c6 100644
--- a/connect.c
+++ b/connect.c
@@ -9,7 +9,7 @@
#include "connect.h"
#include "url.h"
#include "string-list.h"
-#include "sha1-array.h"
+#include "oid-array.h"
#include "transport.h"
#include "strbuf.h"
#include "version.h"
diff --git a/connected.c b/connected.c
index 7e9bd1b..3135b71 100644
--- a/connected.c
+++ b/connected.c
@@ -52,7 +52,7 @@
strbuf_release(&idx_file);
}
- if (opt->check_refs_are_promisor_objects_only) {
+ if (has_promisor_remote()) {
/*
* For partial clones, we don't want to have to do a regular
* connectivity check because we have to enumerate and exclude
@@ -61,7 +61,11 @@
* object is a promisor object. Instead, just make sure we
* received, in a promisor packfile, the objects pointed to by
* each wanted ref.
+ *
+ * Before checking for promisor packs, be sure we have the
+ * latest pack-files loaded into memory.
*/
+ reprepare_packed_git(the_repository);
do {
struct packed_git *p;
@@ -71,13 +75,18 @@
if (find_pack_entry_one(oid.hash, p))
goto promisor_pack_found;
}
- return 1;
+ /*
+ * Fallback to rev-list with oid and the rest of the
+ * object IDs provided by fn.
+ */
+ goto no_promisor_pack_found;
promisor_pack_found:
;
} while (!fn(cb_data, &oid));
return 0;
}
+no_promisor_pack_found:
if (opt->shallow_file) {
argv_array_push(&rev_list.args, "--shallow-file");
argv_array_push(&rev_list.args, opt->shallow_file);
diff --git a/connected.h b/connected.h
index eba5c26..8d5a6b3 100644
--- a/connected.h
+++ b/connected.h
@@ -46,15 +46,6 @@
* during a fetch.
*/
unsigned is_deepening_fetch : 1;
-
- /*
- * If non-zero, only check that the top-level objects referenced by the
- * wanted refs (passed in as cb_data) are promisor objects. This is
- * useful for partial clones, where enumerating and excluding all
- * promisor objects is very slow and the commit-walk itself becomes a
- * no-op.
- */
- unsigned check_refs_are_promisor_objects_only : 1;
};
#define CHECK_CONNECTED_INIT { 0 }
diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash
index c21786f..70ad04e 100644
--- a/contrib/completion/git-completion.bash
+++ b/contrib/completion/git-completion.bash
@@ -504,7 +504,7 @@
{
local root="$2" match="$3"
- __git_ls_files_helper "$root" "$1" "$match" |
+ __git_ls_files_helper "$root" "$1" "${match:-?}" |
awk -F / -v pfx="${2//\\/\\\\}" '{
paths[$1] = 1
}
@@ -1860,6 +1860,7 @@
$merge
$__git_diff_common_options
--pickaxe-all --pickaxe-regex
+ --patch --no-patch
"
return
;;
diff --git a/contrib/completion/git-completion.zsh b/contrib/completion/git-completion.zsh
index eef4eff..ce47e86 100644
--- a/contrib/completion/git-completion.zsh
+++ b/contrib/completion/git-completion.zsh
@@ -150,9 +150,11 @@
push:'update remote refs along with associated objects'
rebase:'forward-port local commits to the updated upstream head'
reset:'reset current HEAD to the specified state'
+ restore:'restore working tree files'
rm:'remove files from the working tree and from the index'
show:'show various types of objects'
status:'show the working tree status'
+ switch:'switch branches'
tag:'create, list, delete or verify a tag object signed with GPG')
_describe -t common-commands 'common commands' list && _ret=0
}
diff --git a/contrib/fast-import/import-tars.perl b/contrib/fast-import/import-tars.perl
index e800d9f..d50ce26 100755
--- a/contrib/fast-import/import-tars.perl
+++ b/contrib/fast-import/import-tars.perl
@@ -139,6 +139,8 @@
print FI "\n";
}
+ next if ($typeflag eq 'g'); # ignore global header
+
my $path;
if ($prefix) {
$path = "$prefix/$name";
diff --git a/contrib/subtree/Makefile b/contrib/subtree/Makefile
index 6906aae..6fa7496 100644
--- a/contrib/subtree/Makefile
+++ b/contrib/subtree/Makefile
@@ -25,14 +25,16 @@
ASCIIDOC_DOCBOOK = docbook
ASCIIDOC_EXTRA =
XMLTO = xmlto
+XMLTO_EXTRA =
ifdef USE_ASCIIDOCTOR
ASCIIDOC = asciidoctor
ASCIIDOC_CONF =
ASCIIDOC_HTML = xhtml5
-ASCIIDOC_DOCBOOK = docbook45
+ASCIIDOC_DOCBOOK = docbook
ASCIIDOC_EXTRA += -I../../Documentation -rasciidoctor-extensions
ASCIIDOC_EXTRA += -alitdd='&\#x2d;&\#x2d;'
+XMLTO_EXTRA += --skip-validation
endif
ifndef SHELL_PATH
@@ -78,7 +80,7 @@
$(INSTALL) -m 644 $^ $(DESTDIR)$(htmldir)
$(GIT_SUBTREE_DOC): $(GIT_SUBTREE_XML)
- $(XMLTO) -m $(MANPAGE_XSL) man $^
+ $(XMLTO) -m $(MANPAGE_XSL) $(XMLTO_EXTRA) man $^
$(GIT_SUBTREE_XML): $(GIT_SUBTREE_TXT)
$(ASCIIDOC) -b $(ASCIIDOC_DOCBOOK) -d manpage $(ASCIIDOC_CONF) \
diff --git a/convert.c b/convert.c
index 5ead3ce..5724498 100644
--- a/convert.c
+++ b/convert.c
@@ -797,6 +797,7 @@
static int apply_multi_file_filter(const char *path, const char *src, size_t len,
int fd, struct strbuf *dst, const char *cmd,
const unsigned int wanted_capability,
+ const struct checkout_metadata *meta,
struct delayed_checkout *dco)
{
int err;
@@ -855,6 +856,24 @@
if (err)
goto done;
+ if (meta && meta->refname) {
+ err = packet_write_fmt_gently(process->in, "ref=%s\n", meta->refname);
+ if (err)
+ goto done;
+ }
+
+ if (meta && !is_null_oid(&meta->treeish)) {
+ err = packet_write_fmt_gently(process->in, "treeish=%s\n", oid_to_hex(&meta->treeish));
+ if (err)
+ goto done;
+ }
+
+ if (meta && !is_null_oid(&meta->blob)) {
+ err = packet_write_fmt_gently(process->in, "blob=%s\n", oid_to_hex(&meta->blob));
+ if (err)
+ goto done;
+ }
+
if ((entry->supported_capabilities & CAP_DELAY) &&
dco && dco->state == CE_CAN_DELAY) {
can_delay = 1;
@@ -971,6 +990,7 @@
static int apply_filter(const char *path, const char *src, size_t len,
int fd, struct strbuf *dst, struct convert_driver *drv,
const unsigned int wanted_capability,
+ const struct checkout_metadata *meta,
struct delayed_checkout *dco)
{
const char *cmd = NULL;
@@ -990,7 +1010,7 @@
return apply_single_file_filter(path, src, len, fd, dst, cmd);
else if (drv->process && *drv->process)
return apply_multi_file_filter(path, src, len, fd, dst,
- drv->process, wanted_capability, dco);
+ drv->process, wanted_capability, meta, dco);
return 0;
}
@@ -998,7 +1018,7 @@
static int read_convert_config(const char *var, const char *value, void *cb)
{
const char *key, *name;
- int namelen;
+ size_t namelen;
struct convert_driver *drv;
/*
@@ -1368,7 +1388,7 @@
if (!ca.drv->required)
return 0;
- return apply_filter(path, NULL, 0, -1, NULL, ca.drv, CAP_CLEAN, NULL);
+ return apply_filter(path, NULL, 0, -1, NULL, ca.drv, CAP_CLEAN, NULL, NULL);
}
const char *get_convert_attr_ascii(const struct index_state *istate, const char *path)
@@ -1406,7 +1426,7 @@
convert_attrs(istate, &ca, path);
- ret |= apply_filter(path, src, len, -1, dst, ca.drv, CAP_CLEAN, NULL);
+ ret |= apply_filter(path, src, len, -1, dst, ca.drv, CAP_CLEAN, NULL, NULL);
if (!ret && ca.drv && ca.drv->required)
die(_("%s: clean filter '%s' failed"), path, ca.drv->name);
@@ -1441,7 +1461,7 @@
assert(ca.drv);
assert(ca.drv->clean || ca.drv->process);
- if (!apply_filter(path, NULL, 0, fd, dst, ca.drv, CAP_CLEAN, NULL))
+ if (!apply_filter(path, NULL, 0, fd, dst, ca.drv, CAP_CLEAN, NULL, NULL))
die(_("%s: clean filter '%s' failed"), path, ca.drv->name);
encode_to_git(path, dst->buf, dst->len, dst, ca.working_tree_encoding, conv_flags);
@@ -1452,7 +1472,9 @@
static int convert_to_working_tree_internal(const struct index_state *istate,
const char *path, const char *src,
size_t len, struct strbuf *dst,
- int normalizing, struct delayed_checkout *dco)
+ int normalizing,
+ const struct checkout_metadata *meta,
+ struct delayed_checkout *dco)
{
int ret = 0, ret_filter = 0;
struct conv_attrs ca;
@@ -1484,7 +1506,7 @@
}
ret_filter = apply_filter(
- path, src, len, -1, dst, ca.drv, CAP_SMUDGE, dco);
+ path, src, len, -1, dst, ca.drv, CAP_SMUDGE, meta, dco);
if (!ret_filter && ca.drv && ca.drv->required)
die(_("%s: smudge filter %s failed"), path, ca.drv->name);
@@ -1494,22 +1516,24 @@
int async_convert_to_working_tree(const struct index_state *istate,
const char *path, const char *src,
size_t len, struct strbuf *dst,
+ const struct checkout_metadata *meta,
void *dco)
{
- return convert_to_working_tree_internal(istate, path, src, len, dst, 0, dco);
+ return convert_to_working_tree_internal(istate, path, src, len, dst, 0, meta, dco);
}
int convert_to_working_tree(const struct index_state *istate,
const char *path, const char *src,
- size_t len, struct strbuf *dst)
+ size_t len, struct strbuf *dst,
+ const struct checkout_metadata *meta)
{
- return convert_to_working_tree_internal(istate, path, src, len, dst, 0, NULL);
+ return convert_to_working_tree_internal(istate, path, src, len, dst, 0, meta, NULL);
}
int renormalize_buffer(const struct index_state *istate, const char *path,
const char *src, size_t len, struct strbuf *dst)
{
- int ret = convert_to_working_tree_internal(istate, path, src, len, dst, 1, NULL);
+ int ret = convert_to_working_tree_internal(istate, path, src, len, dst, 1, NULL, NULL);
if (ret) {
src = dst->buf;
len = dst->len;
@@ -1982,3 +2006,25 @@
{
return filter->vtbl->filter(filter, input, isize_p, output, osize_p);
}
+
+void init_checkout_metadata(struct checkout_metadata *meta, const char *refname,
+ const struct object_id *treeish,
+ const struct object_id *blob)
+{
+ memset(meta, 0, sizeof(*meta));
+ if (refname)
+ meta->refname = refname;
+ if (treeish)
+ oidcpy(&meta->treeish, treeish);
+ if (blob)
+ oidcpy(&meta->blob, blob);
+}
+
+void clone_checkout_metadata(struct checkout_metadata *dst,
+ const struct checkout_metadata *src,
+ const struct object_id *blob)
+{
+ memcpy(dst, src, sizeof(*dst));
+ if (blob)
+ oidcpy(&dst->blob, blob);
+}
diff --git a/convert.h b/convert.h
index 3710969..e29d102 100644
--- a/convert.h
+++ b/convert.h
@@ -4,10 +4,10 @@
#ifndef CONVERT_H
#define CONVERT_H
+#include "hash.h"
#include "string-list.h"
struct index_state;
-struct object_id;
struct strbuf;
#define CONV_EOL_RNDTRP_DIE (1<<0) /* Die if CRLF to LF to CRLF is different */
@@ -57,6 +57,12 @@
struct string_list paths;
};
+struct checkout_metadata {
+ const char *refname;
+ struct object_id treeish;
+ struct object_id blob;
+};
+
extern enum eol core_eol;
extern char *check_roundtrip_encoding;
const char *get_cached_convert_stats_ascii(const struct index_state *istate,
@@ -71,10 +77,12 @@
struct strbuf *dst, int conv_flags);
int convert_to_working_tree(const struct index_state *istate,
const char *path, const char *src,
- size_t len, struct strbuf *dst);
+ size_t len, struct strbuf *dst,
+ const struct checkout_metadata *meta);
int async_convert_to_working_tree(const struct index_state *istate,
const char *path, const char *src,
size_t len, struct strbuf *dst,
+ const struct checkout_metadata *meta,
void *dco);
int async_query_available_blobs(const char *cmd,
struct string_list *available_paths);
@@ -95,6 +103,23 @@
const char *path);
/*
+ * Initialize the checkout metadata with the given values. Any argument may be
+ * NULL if it is not applicable. The treeish should be a commit if that is
+ * available, and a tree otherwise.
+ *
+ * The refname is not copied and must be valid for the lifetime of the struct.
+ * THe object IDs are copied.
+ */
+void init_checkout_metadata(struct checkout_metadata *meta, const char *refname,
+ const struct object_id *treeish,
+ const struct object_id *blob);
+
+/* Copy the metadata from src to dst, updating the blob. */
+void clone_checkout_metadata(struct checkout_metadata *dst,
+ const struct checkout_metadata *src,
+ const struct object_id *blob);
+
+/*
* Reset the internal list of attributes used by convert_to_git and
* convert_to_working_tree.
*/
diff --git a/credential-store.c b/credential-store.c
index c010497..294e771 100644
--- a/credential-store.c
+++ b/credential-store.c
@@ -24,8 +24,8 @@
}
while (strbuf_getline_lf(&line, fh) != EOF) {
- credential_from_url(&entry, line.buf);
- if (entry.username && entry.password &&
+ if (!credential_from_url_gently(&entry, line.buf, 1) &&
+ entry.username && entry.password &&
credential_match(c, &entry)) {
found_credential = 1;
if (match_cb) {
diff --git a/credential.c b/credential.c
index 108d9e1..d8d226b 100644
--- a/credential.c
+++ b/credential.c
@@ -37,6 +37,10 @@
#undef CHECK
}
+
+static int credential_from_potentially_partial_url(struct credential *c,
+ const char *url);
+
static int credential_config_callback(const char *var, const char *value,
void *data)
{
@@ -82,6 +86,22 @@
return 0;
}
+static int match_partial_url(const char *url, void *cb)
+{
+ struct credential *c = cb;
+ struct credential want = CREDENTIAL_INIT;
+ int matches = 0;
+
+ if (credential_from_potentially_partial_url(&want, url) < 0)
+ warning(_("skipping credential lookup for key: credential.%s"),
+ url);
+ else
+ matches = credential_match(&want, c);
+ credential_clear(&want);
+
+ return matches;
+}
+
static void credential_apply_config(struct credential *c)
{
char *normalized_url;
@@ -101,6 +121,7 @@
config.collect_fn = credential_config_callback;
config.cascade_fn = NULL;
config.select_fn = select_all;
+ config.fallback_match_fn = match_partial_url;
config.cb = c;
credential_format(c, &url);
@@ -136,14 +157,14 @@
return;
strbuf_addf(out, "%s://", c->protocol);
if (c->username && *c->username) {
- strbuf_add_percentencode(out, c->username);
+ strbuf_add_percentencode(out, c->username, STRBUF_ENCODE_SLASH);
strbuf_addch(out, '@');
}
if (c->host)
strbuf_addstr(out, c->host);
if (c->path) {
strbuf_addch(out, '/');
- strbuf_add_percentencode(out, c->path);
+ strbuf_add_percentencode(out, c->path, 0);
}
}
@@ -377,8 +398,31 @@
return -1;
}
-int credential_from_url_gently(struct credential *c, const char *url,
- int quiet)
+/*
+ * Potentially-partial URLs can, but do not have to, contain
+ *
+ * - a protocol (or scheme) of the form "<protocol>://"
+ *
+ * - a host name (the part after the protocol and before the first slash after
+ * that, if any)
+ *
+ * - a user name and potentially a password (as "<user>[:<password>]@" part of
+ * the host name)
+ *
+ * - a path (the part after the host name, if any, starting with the slash)
+ *
+ * Missing parts will be left unset in `struct credential`. Thus, `https://`
+ * will have only the `protocol` set, `example.com` only the host name, and
+ * `/git` only the path.
+ *
+ * Note that an empty host name in an otherwise fully-qualified URL (e.g.
+ * `cert:///path/to/cert.pem`) will be treated as unset if we expect the URL to
+ * be potentially partial, and only then (otherwise, the empty string is used).
+ *
+ * The credential_from_url() function does not allow partial URLs.
+ */
+static int credential_from_url_1(struct credential *c, const char *url,
+ int allow_partial_url, int quiet)
{
const char *at, *colon, *cp, *slash, *host, *proto_end;
@@ -391,15 +435,22 @@
* (3) proto://<user>:<pass>@<host>/...
*/
proto_end = strstr(url, "://");
- if (!proto_end || proto_end == url) {
+ if (!allow_partial_url && (!proto_end || proto_end == url)) {
if (!quiet)
warning(_("url has no scheme: %s"), url);
return -1;
}
- cp = proto_end + 3;
+ cp = proto_end ? proto_end + 3 : url;
at = strchr(cp, '@');
colon = strchr(cp, ':');
- slash = strchrnul(cp, '/');
+
+ /*
+ * A query or fragment marker before the slash ends the host portion.
+ * We'll just continue to call this "slash" for simplicity. Notably our
+ * "trim leading slashes" part won't skip over this part of the path,
+ * but that's what we'd want.
+ */
+ slash = cp + strcspn(cp, "/?#");
if (!at || slash <= at) {
/* Case (1) */
@@ -420,8 +471,10 @@
host = at + 1;
}
- c->protocol = xmemdupz(url, proto_end - url);
- c->host = url_decode_mem(host, slash - host);
+ if (proto_end && proto_end - url > 0)
+ c->protocol = xmemdupz(url, proto_end - url);
+ if (!allow_partial_url || slash - host > 0)
+ c->host = url_decode_mem(host, slash - host);
/* Trim leading and trailing slashes from path */
while (*slash == '/')
slash++;
@@ -443,6 +496,17 @@
return 0;
}
+static int credential_from_potentially_partial_url(struct credential *c,
+ const char *url)
+{
+ return credential_from_url_1(c, url, 1, 0);
+}
+
+int credential_from_url_gently(struct credential *c, const char *url, int quiet)
+{
+ return credential_from_url_1(c, url, 0, quiet);
+}
+
void credential_from_url(struct credential *c, const char *url)
{
if (credential_from_url_gently(c, url, 0) < 0)
diff --git a/credential.h b/credential.h
index d99ec42..c0e17e3 100644
--- a/credential.h
+++ b/credential.h
@@ -177,8 +177,8 @@
* Parse a url into a credential struct, replacing any existing contents.
*
* If the url can't be parsed (e.g., a missing "proto://" component), the
- * resulting credential will be empty but we'll still return success from the
- * "gently" form.
+ * resulting credential will be empty and the function will return an
+ * error (even in the "gently" form).
*
* If we encounter a component which cannot be represented as a credential
* value (e.g., because it contains a newline), the "gently" form will return
@@ -189,7 +189,7 @@
void credential_from_url(struct credential *, const char *url);
int credential_from_url_gently(struct credential *, const char *url, int quiet);
-int credential_match(const struct credential *have,
- const struct credential *want);
+int credential_match(const struct credential *want,
+ const struct credential *have);
#endif /* CREDENTIAL_H */
diff --git a/csum-file.c b/csum-file.c
index 53ce37f..0f35fa5 100644
--- a/csum-file.c
+++ b/csum-file.c
@@ -157,7 +157,7 @@
{
hashflush(f);
checkpoint->offset = f->total;
- checkpoint->ctx = f->ctx;
+ the_hash_algo->clone_fn(&checkpoint->ctx, &f->ctx);
}
int hashfile_truncate(struct hashfile *f, struct hashfile_checkpoint *checkpoint)
diff --git a/date.c b/date.c
index b0d9a84..f9ea807 100644
--- a/date.c
+++ b/date.c
@@ -497,7 +497,7 @@
return skip_alpha(date);
}
-static int is_date(int year, int month, int day, struct tm *now_tm, time_t now, struct tm *tm)
+static int set_date(int year, int month, int day, struct tm *now_tm, time_t now, struct tm *tm)
{
if (month > 0 && month < 13 && day > 0 && day < 32) {
struct tm check = *tm;
@@ -518,9 +518,9 @@
else if (year < 38)
r->tm_year = year + 100;
else
- return 0;
+ return -1;
if (!now_tm)
- return 1;
+ return 0;
specified = tm_to_time_t(r);
@@ -529,14 +529,33 @@
* sure it is not later than ten days from now...
*/
if ((specified != -1) && (now + 10*24*3600 < specified))
- return 0;
+ return -1;
tm->tm_mon = r->tm_mon;
tm->tm_mday = r->tm_mday;
if (year != -1)
tm->tm_year = r->tm_year;
- return 1;
+ return 0;
}
- return 0;
+ return -1;
+}
+
+static int set_time(long hour, long minute, long second, struct tm *tm)
+{
+ /* We accept 61st second because of leap second */
+ if (0 <= hour && hour <= 24 &&
+ 0 <= minute && minute < 60 &&
+ 0 <= second && second <= 60) {
+ tm->tm_hour = hour;
+ tm->tm_min = minute;
+ tm->tm_sec = second;
+ return 0;
+ }
+ return -1;
+}
+
+static int is_date_known(struct tm *tm)
+{
+ return tm->tm_year != -1 && tm->tm_mon != -1 && tm->tm_mday != -1;
}
static int match_multi_number(timestamp_t num, char c, const char *date,
@@ -556,10 +575,14 @@
case ':':
if (num3 < 0)
num3 = 0;
- if (num < 25 && num2 >= 0 && num2 < 60 && num3 >= 0 && num3 <= 60) {
- tm->tm_hour = num;
- tm->tm_min = num2;
- tm->tm_sec = num3;
+ if (set_time(num, num2, num3, tm) == 0) {
+ /*
+ * If %H:%M:%S was just parsed followed by: .<num4>
+ * Consider (& discard) it as fractional second
+ * if %Y%m%d is parsed before.
+ */
+ if (*end == '.' && isdigit(end[1]) && is_date_known(tm))
+ strtol(end + 1, &end, 10);
break;
}
return 0;
@@ -575,10 +598,10 @@
if (num > 70) {
/* yyyy-mm-dd? */
- if (is_date(num, num2, num3, NULL, now, tm))
+ if (set_date(num, num2, num3, NULL, now, tm) == 0)
break;
/* yyyy-dd-mm? */
- if (is_date(num, num3, num2, NULL, now, tm))
+ if (set_date(num, num3, num2, NULL, now, tm) == 0)
break;
}
/* Our eastern European friends say dd.mm.yy[yy]
@@ -586,14 +609,14 @@
* mm/dd/yy[yy] form only when separator is not '.'
*/
if (c != '.' &&
- is_date(num3, num, num2, refuse_future, now, tm))
+ set_date(num3, num, num2, refuse_future, now, tm) == 0)
break;
/* European dd.mm.yy[yy] or funny US dd/mm/yy[yy] */
- if (is_date(num3, num2, num, refuse_future, now, tm))
+ if (set_date(num3, num2, num, refuse_future, now, tm) == 0)
break;
/* Funny European mm.dd.yy */
if (c == '.' &&
- is_date(num3, num, num2, refuse_future, now, tm))
+ set_date(num3, num, num2, refuse_future, now, tm) == 0)
break;
return 0;
}
@@ -664,6 +687,20 @@
n++;
} while (isdigit(date[n]));
+ /* 8 digits, compact style of ISO-8601's date: YYYYmmDD */
+ /* 6 digits, compact style of ISO-8601's time: HHMMSS */
+ if (n == 8 || n == 6) {
+ unsigned int num1 = num / 10000;
+ unsigned int num2 = (num % 10000) / 100;
+ unsigned int num3 = num % 100;
+ if (n == 8)
+ set_date(num1, num2, num3, NULL, time(NULL), tm);
+ else if (n == 6 && set_time(num1, num2, num3, tm) == 0 &&
+ *end == '.' && isdigit(end[1]))
+ strtoul(end + 1, &end, 10);
+ return end - date;
+ }
+
/* Four-digit year or a timezone? */
if (n == 4) {
if (num <= 1400 && *offset == -1) {
diff --git a/debian/changelog b/debian/changelog
index 2d9ca65..9c8f878 100644
--- a/debian/changelog
+++ b/debian/changelog
@@ -1,3 +1,27 @@
+git (1:2.27.0-1~bpo10+1) buster-backports; urgency=low
+
+ * upload to buster-backports.
+
+ -- Jonathan Nieder <jrnieder@gmail.com> Mon, 13 Jul 2020 15:25:54 -0700
+
+git (1:2.27.0-1) unstable; urgency=low
+
+ * new upstream release (see RelNotes/2.27.0.txt).
+
+ -- Jonathan Nieder <jrnieder@gmail.com> Mon, 01 Jun 2020 10:05:06 -0700
+
+git (1:2.27.0~rc2-1) unstable; urgency=low
+
+ * new upstream release candidate (closes: #757402).
+
+ -- Jonathan Nieder <jrnieder@gmail.com> Tue, 26 May 2020 14:27:25 -0700
+
+git (1:2.27.0~rc0-1) unstable; urgency=low
+
+ * new upstream release candidate (see RelNotes/2.27.0.txt).
+
+ -- Jonathan Nieder <jrnieder@gmail.com> Mon, 18 May 2020 16:57:41 -0700
+
git (1:2.26.2-1~bpo10+1) buster-backports; urgency=high
* upload to buster-backports.
diff --git a/debian/changelog.upstream b/debian/changelog.upstream
index 17d0ff5..52b7fc8 100644
--- a/debian/changelog.upstream
+++ b/debian/changelog.upstream
@@ -1,3 +1,719 @@
+Version v2.27.0; changes since v2.27.0-rc2:
+-------------------------------------------
+
+Alessandro Menti (2):
+ l10n: it.po: update the Italian translation for Git 2.27.0 round 1
+ l10n: it.po: update for Git 2.27.0 round #2
+
+Alexander Shopov (2):
+ l10n: bg.po: Updated Bulgarian translation (4868t)
+ l10n: bg.po: Updated Bulgarian translation (4875t)
+
+Carlo Marcelo Arenas Belón (1):
+ t: avoid alternation (not POSIX) in grep's BRE
+
+Christopher Diaz Riveros (1):
+ l10n: es: 2.27.0 round 1
+
+Emir Sarı (2):
+ l10n: tr: v2.27.0 round 1
+ l10n: tr: v2.27.0 round 2
+
+Jan Engelhardt (1):
+ l10n: de.po: Fix typo in the German translation of octopus
+
+Jean-Noël Avila (2):
+ l10n: fr v2.27.0 rnd 1
+ l10n: fr.po v2.27.0 rnd 2
+
+Jiang Xin (3):
+ l10n: git.pot: v2.27.0 round 1 (72 new, 37 removed)
+ l10n: git.pot: v2.27.0 round 2 (+1)
+ l10n: zh_CN: for git v2.27.0 l10n round 1~2
+
+Jordi Mas (3):
+ l10n: Update Catalan translation
+ l10n: Update Catalan translation
+ l10n: Update Catalan translation
+
+Junio C Hamano (1):
+ Git 2.27
+
+Martin Ågren (1):
+ rev-list-options.txt: start a list for `show-pulls`
+
+Matthias Rüster (1):
+ l10n: de.po: Update German translation for Git 2.27.0
+
+Peter Krefting (2):
+ l10n: sv.po: Update Swedish translation (4839t0f0u)
+ l10n: sv.po: Update Swedish translation (4875t0f0u)
+
+Toon Claes (1):
+ Documentation: correct hash environment variable
+
+Trần Ngọc Quân (2):
+ l10n: vi(4874t): Updated Vietnamses translation for 2.27.0
+ l10n: vi(4875t): Updated Vietnamses translation for 2.27.0rd2
+
+Yi-Jyun Pan (2):
+ l10n: zh_TW.po: v2.27.0 round 1 (0 untranslated)
+ l10n: zh_TW.po: v2.27.0 round 2 (0 untranslated)
+
+Đoàn Trần Công Danh (1):
+ ci/config: correct instruction for CI preferences
+
+
+Version v2.27.0-rc2; changes since v2.27.0-rc1:
+-----------------------------------------------
+
+Ansgar Röber (1):
+ Subject: git-gui: fix syntax error because of missing semicolon
+
+Carlo Marcelo Arenas Belón (3):
+ t4216: avoid unnecessary subshell in test_bloom_filters_not_used
+ t5520: avoid alternation in grep's BRE (not POSIX)
+ bisect: avoid tailing CR characters from revision in replay
+
+Derrick Stolee (1):
+ fsck: use ERROR_MULTI_PACK_INDEX
+
+Greg Price (1):
+ tests: skip small-stack tests on hppa architecture
+
+Ismael Luceno (1):
+ git-gui: Handle Ctrl + BS/Del in the commit msg
+
+Jonathan Tan (1):
+ t4067: make rename detection test output raw diff
+
+Junio C Hamano (2):
+ Hopefully final batch before 2.27-rc2
+ Git 2.27-rc2
+
+Martin Ågren (5):
+ date-formats.txt: fix list continuation
+ git-commit-graph.txt: fix grammo
+ git-commit-graph.txt: fix list rendering
+ git-credential.txt: use list continuation
+ git-sparse-checkout.txt: add missing '
+
+Philippe Blain (1):
+ command-list.txt: add gitfaq to the list of guides
+
+Todd Zullinger (1):
+ gitfaq: avoid validation error with older asciidoc
+
+Đoàn Trần Công Danh (1):
+ t5703: replace "grep -a" usage by perl
+
+
+Version v2.27.0-rc1; changes since v2.27.0-rc0:
+-----------------------------------------------
+
+Abhishek Kumar (1):
+ commit-slab-decl.h: update include guard
+
+Carlo Marcelo Arenas Belón (1):
+ compat: remove gmtime
+
+Derrick Stolee (1):
+ progress: call trace2_region_leave() only after calling _enter()
+
+Elijah Newren (2):
+ merge-recursive: fix rename/rename(1to2) for working tree with a binary
+ unpack-trees: also allow get_progress() to work on a different index
+
+Johannes Schindelin (1):
+ ci: avoid pounding on the poor ci-artifacts container
+
+Junio C Hamano (4):
+ Revert "t/test_lib: avoid naked bash arrays in file_lineno"
+ Revert "ci: add a problem matcher for GitHub Actions"
+ Revert "tests: when run in Bash, annotate test failures with file name/line number"
+ Git 2.27-rc1
+
+Todd Zullinger (1):
+ git-bugreport.txt: adjust reference to strftime(3)
+
+Đoàn Trần Công Danh (1):
+ t1509: correct i18n test
+
+
+Version v2.27.0-rc0; changes since v2.26.2:
+-------------------------------------------
+
+Alban Gruin (3):
+ sequencer: mark messages for translation
+ sequencer: don't abbreviate a command if it doesn't have a short form
+ t3432: test `--merge' with `rebase.abbreviateCommands = true', too
+
+Alex Henrie (1):
+ pull: warn if the user didn't say whether to rebase or to merge
+
+Alexandr Miloslavskiy (4):
+ set_git_dir: fix crash when used with real_path()
+ real_path: remove unsafe API
+ real_path_if_valid(): remove unsafe API
+ get_superproject_working_tree(): return strbuf
+
+Andras Kucsma (1):
+ run-command: trigger PATH lookup properly on Cygwin
+
+Andrei Rybak (2):
+ t9500: remove spaces after redirect operators
+ t: fix whitespace around &&
+
+Andrew Oakley (1):
+ git-p4: recover from inconsistent perforce history
+
+Ash Holland (1):
+ userdiff: support Markdown
+
+Ben Keene (8):
+ git-p4: change the expansion test from basestring to list
+ git-p4: rewrite prompt to be Windows compatible
+ git-p4: create new function run_git_hook
+ git-p4: add p4-pre-submit exit text
+ git-p4: add --no-verify option
+ git-p4: restructure code in submit
+ git-p4: add p4 submit hooks
+ git-p4: add RCS keyword status message
+
+Carlo Marcelo Arenas Belón (8):
+ credential-store: ignore bogus lines from store file
+ credential: update description for credential_from_url_gently
+ credential: correct order of parameters for credential_match
+ builtin/receive-pack: avoid generic function name hmac()
+ t/t0000-basic: make sure subtests also use TEST_SHELL_PATH
+ t/test_lib: avoid naked bash arrays in file_lineno
+ credential: update gitcredentials documentation
+ credential: document protocol updates
+
+Christian Couder (1):
+ upload-pack: clear filter_options for each v2 fetch command
+
+Christopher Warrington (1):
+ bisect: allow CRLF line endings in "git bisect replay" input
+
+Damien Robert (6):
+ midx.c: fix an integer underflow
+ doc: list all commands affected by submodule.recurse
+ doc: document --recurse-submodules for reset and restore
+ doc: explain how to deactivate submodule.recurse completely
+ doc: be more precise on (fetch|push).recurseSubmodules
+ doc: --recurse-submodules mostly applies to active submodules
+
+Denton Liu (45):
+ Makefile: ASCII-sort += lists
+ t7600: use test_write_lines()
+ Lib-ify fmt-merge-msg
+ Lib-ify prune-packed
+ t5512: don't use `test_must_fail test_cmp`
+ t5512: stop losing git exit code in here-docs
+ t5512: stop losing return codes of git commands
+ t5550: simplify no matching line check
+ t5607: reorder `nongit test_must_fail`
+ t5612: don't use `test_must_fail test_cmp`
+ t5612: stop losing return codes of git commands
+ t5801: teach compare_refs() to accept !
+ wrapper: indent with tabs
+ sequencer: stop leaking buf
+ sequencer: make file exists check more efficient
+ sequencer: make read_oneliner() accept flags
+ sequencer: configurably warn on non-existent files
+ sequencer: make read_oneliner() extern
+ rebase: use read_oneliner()
+ sequencer: make apply_autostash() accept a path
+ sequencer: rename stash_sha1 to stash_oid
+ rebase: use apply_autostash() from sequencer.c
+ rebase: generify reset_head()
+ reset: extract reset_head() from rebase
+ rebase: extract create_autostash()
+ rebase: generify create_autostash()
+ sequencer: extract perform_autostash() from rebase
+ sequencer: unlink autostash in apply_autostash()
+ sequencer: implement save_autostash()
+ sequencer: implement apply_autostash_oid()
+ merge: teach --autostash option
+ t5520: make test_pull_autostash() accept expect_parent_num
+ pull: pass --autostash to merge
+ t6030: use test_path_is_missing()
+ t7408: replace incorrect uses of test_must_fail
+ t7508: don't use `test_must_fail test_cmp`
+ t9141: use test_path_is_missing()
+ t9160: use test_path_is_missing()
+ t9164: use test_must_fail only on git commands
+ t9819: don't use test_must_fail with p4
+ Use OPT_CALLBACK and OPT_CALLBACK_F
+ push: unset PARSE_OPT_OPTARG for --recurse-submodules
+ rebase: save autostash entry into stash reflog on --quit
+ switch: fix errors and comments related to -c and -C
+ Doc: reference the "stash list" in autostash docs
+
+Derrick Stolee (25):
+ connected.c: reprepare packs for corner cases
+ config: set pack.useSparse=true by default
+ pack-objects: flip the use of GIT_TEST_PACK_SPARSE
+ clone: document --filter options
+ diff: halt tree-diff early after max_changes
+ dir: refactor treat_directory to clarify control flow
+ commit-graph: fix buggy --expire-time option
+ t5319: replace 'touch -m' with 'test-tool chmtime'
+ bloom: ignore renames when computing changed paths
+ revision: --show-pulls adds helpful merges
+ log-tree: make ref_filter_match() a helper method
+ log: add log.excludeDecoration config option
+ revision: complicated pathspecs disable filters
+ tests: write commit-graph with Bloom filters
+ blame: use changed-path Bloom filters
+ multi-pack-index: close file descriptor after mmap
+ bloom: fix whitespace around tab length
+ test-bloom: fix usage typo
+ sparse-checkout: stop blocking empty workdirs
+ unpack-trees: avoid array out-of-bounds error
+ multi-pack-index: respect repack.packKeptObjects=false
+ bloom: parse commit before computing filters
+ Documentation: changed-path Bloom filters use byte words
+ bloom: de-duplicate directory entries
+ bloom: use num_changes not nr for limit detection
+
+Elijah Newren (37):
+ oidset: remove unnecessary include
+ unpack-trees: fix minor typo in comment
+ unpack-trees: remove unused error type
+ unpack-trees: simplify verify_absent_sparse()
+ unpack-trees: simplify pattern_list freeing
+ t1091: make some tests a little more defensive against failures
+ unpack-trees: allow check_updates() to work on a different index
+ unpack-trees: do not mark a dirty path with SKIP_WORKTREE
+ unpack-trees: pull sparse-checkout pattern reading into a new function
+ unpack-trees: add a new update_sparsity() function
+ sparse-checkout: use new update_sparsity() function
+ sparse-checkout: use improved unpack_trees porcelain messages
+ unpack-trees: move ERROR_WOULD_LOSE_SUBMODULE earlier
+ unpack-trees: rename ERROR_* fields meant for warnings to WARNING_*
+ unpack-trees: split display_error_msgs() into two
+ unpack-trees: make sparse path messages sound like warnings
+ unpack-trees: provide warnings on sparse updates for unmerged paths too
+ unpack-trees: failure to set SKIP_WORKTREE bits always just a warning
+ sparse-checkout: provide a new reapply subcommand
+ pull: avoid running both merge and rebase
+ t7063: more thorough status checking
+ t3000: add more testcases testing a variety of ls-files issues
+ dir: fix simple typo in comment
+ dir: consolidate treat_path() and treat_one_path()
+ dir: fix broken comment
+ dir: fix confusion based on variable tense
+ dir: replace exponential algorithm with a linear one
+ dir: include DIR_KEEP_UNTRACKED_CONTENTS handling in treat_directory()
+ dir: replace double pathspec matching with single in treat_directory()
+ Fix error-prone fill_directory() API; make it only return matches
+ completion: fix 'git add' on paths under an untracked directory
+ git-rebase.txt: add another hook to the hooks section, and explain more
+ sequencer: honor GIT_REFLOG_ACTION
+ rebase -i: mark commits that begin empty in todo editor
+ rebase: reinstate --no-keep-empty
+ rebase: fix an incompatible-options error message
+ rebase: display an error if --root and --fork-point are both provided
+
+Emily Shaffer (8):
+ help: move list_config_help to builtin/help
+ bugreport: add tool to generate debugging info
+ bugreport: gather git version and build info
+ bugreport: add uname info
+ bugreport: add compiler info
+ bugreport: drop extraneous includes
+ bugreport: collect list of populated hooks
+ trace2: log progress time and throughput
+
+Emma Brooks (3):
+ format-patch: teach --no-encode-email-headers
+ Documentation: explain "mboxrd" pretty format
+ gitweb: Recognize *-to and Closes/Fixes trailers
+
+Eric Sunshine (1):
+ restore: default to HEAD when combining --staged and --worktree
+
+Garima Singh (13):
+ commit-graph: define and use MAX_NUM_CHUNKS
+ bloom.c: add the murmur3 hash implementation
+ bloom.c: introduce core Bloom filter constructs
+ bloom.c: core Bloom filter implementation for changed paths.
+ commit-graph: compute Bloom filters for changed paths
+ commit-graph: examine commits by generation number
+ commit-graph: write Bloom filters to commit graph file
+ commit-graph: reuse existing Bloom filters during write
+ commit-graph: add --changed-paths option to write subcommand
+ revision.c: use Bloom filters to speed up path based revision walks
+ revision.c: add trace2 stats around Bloom filter usage
+ t4216: add end to end tests for git log with Bloom filters
+ commit-graph: add GIT_TEST_COMMIT_GRAPH_CHANGED_PATHS test flag
+
+Hans Jerry Illikainen (2):
+ t: increase test coverage of signature verification output
+ gpg-interface: prefer check_signature() for GPG verification
+
+Heba Waly (4):
+ advice: extract vadvise() from advise()
+ advice: change "setupStreamFailure" to "setUpstreamFailure"
+ advice: revamp advise API
+ tag: use new advice API to check visibility
+
+Jeff King (41):
+ t3419: drop EXPENSIVE tests
+ t/lib-credential.sh: drop shebang line
+ t/lib-*.sh: drop executable bit
+ test-lib-functions: make packetize() more efficient
+ upload-pack: handle unexpected delim packets
+ p5310: stop timing non-bitmap pack-to-disk
+ Makefile: avoid running curl-config multiple times
+ Makefile: use curl-config --cflags
+ test-lib-functions: simplify packetize() stdin code
+ commit-graph: examine changed-path objects in pack order
+ oid_array: use size_t for count and allocation
+ oid_array: use size_t for iteration
+ oid_array: rename source file from sha1-array
+ test-tool: rename sha1-array to oid-array
+ bisect: stop referring to sha1_array
+ ref-filter: stop referring to "sha1 array"
+ oidset: stop referring to sha1-array
+ clone: use "quick" lookup while following tags
+ Makefile: avoid running curl-config unnecessarily
+ fast-import: replace custom hash with hashmap.c
+ repository: mark the "refs" pointer as private
+ remote: drop auto-strlen behavior of make_branch() and make_rewrite()
+ parse_config_key(): return subsection len as size_t
+ config: drop useless length variable in write_pair()
+ git_config_parse_key(): return baselen as size_t
+ config: use size_t to store parsed variable baselen
+ config: reject parsing of files over INT_MAX
+ credential: treat "?" and "#" in URLs as end of host
+ blame: drop unused parameter from maybe_changed_path
+ test-bloom: fix some whitespace issues
+ test-bloom: check that we have expected arguments
+ commit-graph: close descriptors after mmap
+ t0000: disable GIT_TEST_FAIL_PREREQS in sub-tests
+ gitcredentials(7): clarify quoting of helper examples
+ gitcredentials(7): make shell-snippet example more realistic
+ CodingGuidelines: drop arithmetic expansion advice to use "$x"
+ ref-filter: apply --ignore-case to all sorting keys
+ ref-filter: apply fallback refname sort only after all user sorts
+ list-objects-filter: treat NULL filter_options as "disabled"
+ pack-bitmap: pass object filter to fill-in traversal
+ ci: allow per-branch config for GitHub Actions
+
+Jessica Clarke (1):
+ config.mak.uname: Define FREAD_READS_DIRECTORIES for GNU/Hurd
+
+Jiang Xin (5):
+ send-pack: fix inconsistent porcelain output
+ t5543: never report what we do not push
+ send-pack: mark failure of atomic push properly
+ transport-helper: mark failure for atomic push
+ transport-helper: new method reject_atomic_push()
+
+Johannes Schindelin (36):
+ cherry-pick: add test for `--skip` advice in `git commit`
+ tests(junit-xml): avoid invalid XML
+ import-tars: ignore the global PAX header
+ tests(gpg): allow the gpg-agent to start on Windows
+ t/lib-gpg.sh: stop pretending to be a stand-alone script
+ tests: do not let lazy prereqs inside `test_expect_*` turn off tracing
+ tests: turn GPG, GPGSM and RFC1991 into lazy prereqs
+ tests: increase the verbosity of the GPG-related prereqs
+ MyFirstObjectWalk: remove unnecessary conditional statement
+ t0007: fix a typo
+ ci/lib: if CI type is unknown, show the environment variables
+ ci/lib: allow running in GitHub Actions
+ ci: fix the `jobname` of the `GETTEXT_POISON` job
+ subtree: fix build with AsciiDoctor 2
+ mingw: do not treat `COM0` as a reserved file name
+ t3904: fix incorrect demonstration of a bug
+ stash -p: (partially) fix bug concerning split hunks
+ mingw: help debugging by optionally executing bash with strace
+ interactive: refactor code asking the user for interactive input
+ ci: run gem with sudo to install asciidoctor
+ README: add a build badge for the GitHub Actions runs
+ ci: retire the Azure Pipelines definition
+ tests: when run in Bash, annotate test failures with file name/line number
+ ci: add a problem matcher for GitHub Actions
+ ci: let GitHub Actions upload failed tests' directories
+ t: consolidate the `is_hidden` functions
+ mingw: make test_path_is_hidden more robust
+ t: restrict `is_hidden` to be called only on Windows
+ credential: fix grammar
+ credential: optionally allow partial URLs in credential_from_url_gently()
+ credential: optionally allow partial URLs in credential_from_url_gently()
+ credential: handle `credential.<partial-URL>.<key>` again
+ macOS/brew: let the build find gettext headers/libraries/msgfmt
+ push: anonymize URLs in error messages and warnings
+ credential: handle `credential.<partial-URL>.<key>` again
+ rebase --autosquash: fix a potential segfault
+
+Jonathan Nieder (1):
+ Revert "fetch: default to protocol version 2"
+
+Jonathan Tan (11):
+ connected: always use partial clone optimization
+ promisor-remote: accept 0 as oid_nr in function
+ diff: make diff_populate_filespec_options struct
+ diff: refactor object read
+ diff: restrict when prefetching occurs
+ rebase --merge: optionally skip upstreamed commits
+ fetch-pack: return enum from process_acks()
+ fetch-pack: in protocol v2, in_vain only after ACK
+ fetch-pack: in protocol v2, reset in_vain upon ACK
+ commit-graph: avoid memory leaks
+ t5500: count objects through stderr, not trace
+
+Jorge Lopez Silva (2):
+ http: add client cert support for HTTPS proxies
+ http: add environment variable support for HTTPS proxies
+
+Josh Steadmon (1):
+ trace2: teach Git to log environment variables
+
+Julien Moutinho (1):
+ gitweb: fix UTF-8 encoding when using CGI::Fast
+
+Junio C Hamano (26):
+ rebase: --fork-point regression fix
+ describe: force long format for a name based on a mislocated tag
+ update how-to-maintain-git
+ parse-options: teach "git cmd -h" to show alias as alias
+ clone: reorder --recursive/--recurse-submodules
+ log: give --[no-]use-mailmap a more sensible synonym --[no-]mailmap
+ separate tar.* config to its own source file
+ The first batch post 2.26 cycle
+ The second batch post 2.26 cycle
+ t/README: suggest how to leave test early with failure
+ CodingGuidelines: allow ${#posix} == strlen($posix)
+ sha1-name: do not assume that the ref store is initialized
+ receive-pack: compilation fix
+ The third batch
+ The fourth batch
+ credential-store: document the file format a bit more
+ Revert "commit-graph.c: introduce '--[no-]check-oids'"
+ The fifth batch
+ The sixth batch
+ The seventh batch
+ auto-gc: extract a reusable helper from "git fetch"
+ auto-gc: pass --quiet down from am, commit, merge and rebase
+ CodingGuidelines: do not ==/!= compare with 0 or '\0' or NULL
+ The eighth batch
+ The ninth batch
+ Git 2.27-rc0
+
+Li Xuejiang (1):
+ git-submodule.sh: setup uninitialized variables
+
+Martin Ågren (9):
+ Doc: drop support for docbook-xsl before 1.71.1
+ Doc: drop support for docbook-xsl before 1.72.0
+ Doc: drop support for docbook-xsl before 1.73.0
+ manpage-bold-literal.xsl: stop using git.docbook.backslash
+ manpage-normal.xsl: fold in manpage-base.xsl
+ INSTALL: drop support for docbook-xsl before 1.74
+ user-manual.conf: don't specify [listingblock]
+ config.txt: move closing "----" to cover entire listing
+ merge: use skip_prefix to parse config key
+
+Matheus Tavares (3):
+ test-lib: allow short options to be bundled
+ grep: follow conventions for printing paths w/ unusual chars
+ config doc: fix reference to config.worktree info
+
+Matthias Aßhauer (1):
+ mingw: use modern strftime implementation if possible
+
+Michael F. Schönitzer (1):
+ docs: fix minor glitch in illustration
+
+Nathan Sanders (1):
+ mingw: cope with the Isilon network file system
+
+Patrick Steinhardt (9):
+ refs: fix segfault when aborting empty transaction
+ git-update-ref.txt: add missing word
+ strbuf: provide function to append whole lines
+ update-ref: organize commands in an array
+ update-ref: drop unused argument for `parse_refname`
+ update-ref: pass end pointer instead of strbuf
+ update-ref: move transaction handling into `update_refs_stdin()`
+ update-ref: read commands in a line-wise fashion
+ update-ref: implement interactive transaction handling
+
+Philippe Blain (9):
+ t7112: remove mention of KNOWN_FAILURE_SUBMODULE_RECURSIVE_NESTED
+ t/lib-submodule-update: remove outdated test description
+ t/lib-submodule-update: move a test to the right section
+ unpack-trees: remove outdated description for verify_clean_submodule
+ unpack-trees: check for missing submodule directory in merged_entry
+ t/lib-submodule-update: add test removing nested submodules
+ git-rebase.txt: fix typo
+ pull doc: refer to a specific section in 'fetch' doc
+ pull doc: correct outdated description of an example
+
+Phillip Wood (6):
+ t3404: use test_cmp_rev
+ cherry-pick: check commit error messages
+ sequencer: write CHERRY_PICK_HEAD for reword and edit
+ commit: use enum value for multiple cherry-picks
+ commit: encapsulate determine_whence() for sequencer
+ commit: give correct advice for empty commit during a rebase
+
+René Scharfe (4):
+ pull: document more passthru options
+ pull: remove --update-head-ok from documentation
+ pull: pass documented fetch options on
+ fsck: report non-consecutive duplicate names in trees
+
+SZEDER Gábor (4):
+ ci: use python3 in linux-gcc and osx-gcc and python2 elsewhere
+ commit-slab: clarify slabname##_peek()'s return value
+ ci: make MAKEFLAGS available inside the Docker container in the Linux32 job
+ completion: offer '--(no-)patch' among 'git log' options
+
+Shourya Shukla (4):
+ submodule--helper.c: Rename 'cb_foreach' to 'foreach_cb'
+ gitfaq: files in .gitignore are tracked
+ gitfaq: fetching and pulling a repository
+ submodule: port subcommand 'set-url' from shell to C
+
+Son Luong Ngoc (1):
+ midx: teach "git multi-pack-index repack" honor "git repack" configurations
+
+Taylor Blau (24):
+ t/helper/test-read-graph.c: support commit-graph chains
+ builtin/commit-graph.c: support for '--split[=<strategy>]'
+ builtin/commit-graph.c: introduce split strategy 'no-merge'
+ builtin/commit-graph.c: introduce split strategy 'replace'
+ oidset: introduce 'oidset_size'
+ commit-graph.h: replace 'commit_hex' with 'commits'
+ commit-graph.c: introduce '--[no-]check-oids'
+ diff-tree.c: load notes machinery when required
+ commit-graph.c: don't use discarded graph_name in error
+ t/test-lib.sh: make ULIMIT_FILE_DESCRIPTORS available to tests
+ commit-graph.c: gracefully handle file descriptor exhaustion
+ t5537: use test_write_lines and indented heredocs for readability
+ shallow.c: use '{commit,rollback}_shallow_file'
+ tempfile.c: introduce 'create_tempfile_mode'
+ lockfile.c: introduce 'hold_lock_file_for_update_mode'
+ commit-graph.c: write non-split graphs as read-only
+ commit-graph.c: ensure graph layers respect core.sharedRepository
+ commit-graph.c: make 'commit-graph-chain's read-only
+ commit: make 'commit_graft_pos' non-static
+ shallow: extract a header file for shallow-related functions
+ shallow.h: document '{commit,rollback}_shallow_file'
+ shallow: use struct 'shallow_lock' for additional safety
+ pack-bitmap.c: make object filtering functions generic
+ pack-bitmap.c: support 'tree:0' filtering
+
+Terry Moschou (1):
+ complete: zsh: add missing sub cmd completion candidates
+
+Thomas Gummerer (2):
+ stash: get git_stash_config at the top level
+ stash: remove the stash.useBuiltin setting
+
+Vasil Dimov (2):
+ range-diff: fix a crash in parsing git-log output
+ range-diff: avoid negative string precision
+
+Yang Zhao (12):
+ git-p4: make python2.7 the oldest supported version
+ git-p4: remove string type aliasing
+ git-p4: encode/decode communication with p4 for python3
+ git-p4: encode/decode communication with git for python3
+ git-p4: convert path to unicode before processing them
+ git-p4: open .gitp4-usercache.txt in text mode
+ git-p4: use marshal format version 2 when sending to p4
+ git-p4: fix freezing while waiting for fast-import progress
+ git-p4: use functools.reduce instead of reduce
+ git-p4: use dict.items() iteration for python3 compatibility
+ git-p4: simplify regex pattern generation for parsing diff-tree
+ git-p4: use python3's input() everywhere
+
+brian m. carlson (34):
+ builtin/pack-objects: make hash agnostic
+ hash: implement and use a context cloning function
+ hex: introduce parsing variants taking hash algorithms
+ hex: add functions to parse hex object IDs in any algorithm
+ repository: require a build flag to use SHA-256
+ t: use hash-specific lookup tables to define test constants
+ t6300: abstract away SHA-1-specific constants
+ t6300: make hash algorithm independent
+ t/helper/test-dump-split-index: initialize git repository
+ t/helper: initialize repository if necessary
+ t/helper: make repository tests hash independent
+ setup: allow check_repository_format to read repository format
+ builtin/init-db: allow specifying hash algorithm on command line
+ builtin/init-db: add environment variable for new repo hash
+ init-db: move writing repo version into a function
+ worktree: allow repository version 1
+ commit: use expected signature header for SHA-256
+ fast-import: permit reading multiple marks files
+ fast-import: add helper function for inserting mark object entries
+ fast-import: make find_marks work on any mark set
+ fast-import: add a generic function to iterate over marks
+ fast-import: add options for rewriting submodules
+ builtin/checkout: pass branch info down to checkout_worktree
+ convert: permit passing additional metadata to filter processes
+ convert: provi