summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.github/workflows/main.yml97
-rw-r--r--Documentation/CodingGuidelines12
-rw-r--r--Documentation/RelNotes/2.27.0.txt51
-rw-r--r--Documentation/git-bugreport.txt1
-rw-r--r--Documentation/git-credential.txt34
-rw-r--r--Documentation/git-multi-pack-index.txt3
-rw-r--r--Documentation/gitcredentials.txt26
-rw-r--r--Documentation/gitfaq.txt18
-rw-r--r--Documentation/technical/commit-graph-format.txt8
-rwxr-xr-xGIT-VERSION-GEN2
-rw-r--r--Makefile8
-rw-r--r--bloom.c60
-rw-r--r--bloom.h6
-rw-r--r--bugreport.c52
-rw-r--r--builtin/am.c3
-rw-r--r--builtin/commit.c3
-rw-r--r--builtin/fetch.c11
-rw-r--r--builtin/merge.c3
-rw-r--r--builtin/pack-objects.c1
-rw-r--r--builtin/prune.c1
-rw-r--r--builtin/rebase.c3
-rw-r--r--builtin/receive-pack.c3
-rw-r--r--builtin/repack.c1
-rw-r--r--builtin/rev-parse.c1
-rw-r--r--builtin/submodule--helper.c32
-rwxr-xr-xci/config/allow-refs.sample26
-rw-r--r--commit-graph.c1
-rw-r--r--commit.c16
-rw-r--r--commit.h50
-rw-r--r--compat/gmtime.c29
-rw-r--r--config.mak.uname1
-rw-r--r--contrib/completion/git-completion.bash1
-rw-r--r--credential.h8
-rw-r--r--environment.c1
-rw-r--r--fetch-pack.c3
-rw-r--r--fsck.c72
-rwxr-xr-xgit-bisect.sh2
-rw-r--r--git-compat-util.h7
-rwxr-xr-xgit-p4.py43
-rwxr-xr-xgit-submodule.sh22
-rw-r--r--git.c1
-rw-r--r--list-objects-filter.c3
-rw-r--r--merge-recursive.c12
-rw-r--r--midx.c42
-rw-r--r--pack-bitmap.c72
-rw-r--r--run-command.c13
-rw-r--r--run-command.h5
-rw-r--r--send-pack.c1
-rw-r--r--sequencer.c7
-rw-r--r--shallow.c36
-rw-r--r--shallow.h81
-rw-r--r--t/helper/test-bloom.c4
-rwxr-xr-xt/perf/p5310-pack-bitmaps.sh10
-rwxr-xr-xt/t0091-bugreport.sh15
-rwxr-xr-xt/t0095-bloom.sh8
-rwxr-xr-xt/t1450-fsck.sh16
-rwxr-xr-xt/t1509-root-work-tree.sh4
-rwxr-xr-xt/t3415-rebase-autosquash.sh16
-rwxr-xr-xt/t4216-log-bloom.sh2
-rwxr-xr-xt/t5319-multi-pack-index.sh27
-rwxr-xr-xt/t5500-fetch-pack.sh12
-rwxr-xr-xt/t5616-partial-clone.sh7
-rwxr-xr-xt/t6030-bisect-porcelain.sh7
-rwxr-xr-xt/t6042-merge-rename-corner-cases.sh55
-rwxr-xr-xt/t6113-rev-list-bitmap-filters.sh21
-rwxr-xr-xt/t9834-git-p4-file-dir-bug.sh70
-rw-r--r--unpack-trees.c10
-rw-r--r--upload-pack.c35
68 files changed, 1030 insertions, 284 deletions
diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml
index fd4df939b5..84a5dcff7a 100644
--- a/.github/workflows/main.yml
+++ b/.github/workflows/main.yml
@@ -6,13 +6,57 @@ 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: a=git-sdk-64-minimal && mkdir -p $a && curl -# https://wingit.blob.core.windows.net/ci-artifacts/$a.tar.xz | tar -C $a -xJf -
+ 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:
@@ -30,6 +74,11 @@ jobs:
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]
@@ -38,9 +87,6 @@ jobs:
nr: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
steps:
- uses: actions/checkout@v1
- - name: download git-sdk-64-minimal
- shell: bash
- run: a=git-sdk-64-minimal && mkdir -p $a && curl -# https://wingit.blob.core.windows.net/ci-artifacts/$a.tar.xz | tar -C $a -xJf -
- name: download build artifacts
uses: actions/download-artifact@v1
with:
@@ -49,6 +95,11 @@ jobs:
- 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: |
@@ -70,6 +121,8 @@ jobs:
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
@@ -79,7 +132,19 @@ jobs:
- uses: actions/checkout@v1
- name: download git-sdk-64-minimal
shell: bash
- run: a=git-sdk-64-minimal && mkdir -p $a && curl -# https://wingit.blob.core.windows.net/ci-artifacts/$a.tar.xz | tar -C $a -xJf -
+ 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: |
@@ -119,15 +184,17 @@ jobs:
path: artifacts
vs-test:
runs-on: windows-latest
- needs: [vs-build]
+ 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-64-portable
- shell: bash
- run: a=git-64-portable && mkdir -p $a && curl -# https://wingit.blob.core.windows.net/ci-artifacts/$a.tar.xz | tar -C $a -xJf -
+ - 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:
@@ -143,9 +210,9 @@ jobs:
NO_SVN_TESTS: 1
GIT_TEST_SKIP_REBASE_P: 1
run: |
- & git-64-portable\git-cmd.exe --command=usr\bin\bash.exe -lc @"
+ & .\git-sdk-64-minimal\usr\bin\bash.exe -lc @"
# Let Git ignore the SDK and the test-cache
- printf '%s\n' /git-64-portable/ /test-cache/ >>.git/info/exclude
+ printf '%s\n' /git-sdk-64-minimal/ /test-cache/ >>.git/info/exclude
cd t &&
PATH=\"`$PWD/helper:`$PATH\" &&
@@ -154,6 +221,8 @@ jobs:
${{matrix.nr}} 10 t[0-9]*.sh)
"@
regular:
+ needs: ci-config
+ if: needs.ci-config.outputs.enabled == 'yes'
strategy:
matrix:
vector:
@@ -189,6 +258,8 @@ jobs:
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:
@@ -213,6 +284,8 @@ jobs:
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
@@ -221,6 +294,8 @@ jobs:
- 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
diff --git a/Documentation/CodingGuidelines b/Documentation/CodingGuidelines
index a89e8dcfbc..227f46ae40 100644
--- a/Documentation/CodingGuidelines
+++ b/Documentation/CodingGuidelines
@@ -232,6 +232,18 @@ For C programs:
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/RelNotes/2.27.0.txt b/Documentation/RelNotes/2.27.0.txt
index 2a8c6e8f28..e3121d0164 100644
--- a/Documentation/RelNotes/2.27.0.txt
+++ b/Documentation/RelNotes/2.27.0.txt
@@ -108,6 +108,13 @@ UI, Workflows & Features
* "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.
@@ -161,6 +168,16 @@ Performance, Internal Implementation, Development Support etc.
* 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.
+
Fixes since v2.26
-----------------
@@ -420,6 +437,39 @@ Fixes since v2.26
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).
+
* 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).
@@ -448,3 +498,4 @@ Fixes since v2.26
(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/git-bugreport.txt b/Documentation/git-bugreport.txt
index 643d1b2884..7fe9aef34e 100644
--- a/Documentation/git-bugreport.txt
+++ b/Documentation/git-bugreport.txt
@@ -28,6 +28,7 @@ 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
diff --git a/Documentation/git-credential.txt b/Documentation/git-credential.txt
index 6f0c7ca80f..8d990e92fd 100644
--- a/Documentation/git-credential.txt
+++ b/Documentation/git-credential.txt
@@ -103,17 +103,20 @@ INPUT/OUTPUT FORMAT
`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 @@ Git understands the following attributes:
`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 @@ Git understands the following attributes:
`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 @@ Git understands the following attributes:
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-multi-pack-index.txt b/Documentation/git-multi-pack-index.txt
index 642d9ac5b7..0c6619493c 100644
--- a/Documentation/git-multi-pack-index.txt
+++ b/Documentation/git-multi-pack-index.txt
@@ -56,6 +56,9 @@ repack::
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/gitcredentials.txt b/Documentation/gitcredentials.txt
index 0d0f7149bd..9e481aec85 100644
--- a/Documentation/gitcredentials.txt
+++ b/Documentation/gitcredentials.txt
@@ -268,16 +268,26 @@ For a `get` operation, the helper should produce a list of attributes on
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
index 1cf83df118..370d62dae4 100644
--- a/Documentation/gitfaq.txt
+++ b/Documentation/gitfaq.txt
@@ -223,6 +223,24 @@ 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
-----
diff --git a/Documentation/technical/commit-graph-format.txt b/Documentation/technical/commit-graph-format.txt
index de56f9f1ef..1beef17182 100644
--- a/Documentation/technical/commit-graph-format.txt
+++ b/Documentation/technical/commit-graph-format.txt
@@ -97,10 +97,10 @@ CHUNK DATA:
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 8-byte word blocks 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 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]
diff --git a/GIT-VERSION-GEN b/GIT-VERSION-GEN
index 21f929e7ee..03d2243c0d 100755
--- a/GIT-VERSION-GEN
+++ b/GIT-VERSION-GEN
@@ -1,7 +1,7 @@
#!/bin/sh
GVF=GIT-VERSION-FILE
-DEF_VER=v2.26.GIT
+DEF_VER=v2.27.0-rc0
LF='
'
diff --git a/Makefile b/Makefile
index 3d3a39fc19..90aa329eb7 100644
--- a/Makefile
+++ b/Makefile
@@ -399,9 +399,6 @@ all::
# 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.
@@ -1809,11 +1806,6 @@ ifndef NO_MSGFMT_EXTENDED_OPTIONS
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
diff --git a/bloom.c b/bloom.c
index dd9bab9bbd..9b86aa3f59 100644
--- a/bloom.c
+++ b/bloom.c
@@ -9,7 +9,7 @@
define_commit_slab(bloom_filter_slab, struct bloom_filter);
-struct bloom_filter_slab bloom_filters;
+static struct bloom_filter_slab bloom_filters;
struct pathmap_hash_entry {
struct hashmap_entry entry;
@@ -29,8 +29,8 @@ static inline unsigned char get_bitmask(uint32_t pos)
}
static int load_bloom_filter_from_graph(struct commit_graph *g,
- struct bloom_filter *filter,
- struct commit *c)
+ struct bloom_filter *filter,
+ struct commit *c)
{
uint32_t lex_pos, start_index, end_index;
@@ -123,9 +123,9 @@ 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)
+ size_t len,
+ struct bloom_key *key,
+ const struct bloom_filter_settings *settings)
{
int i;
const uint32_t seed0 = 0x293ae76f;
@@ -139,8 +139,8 @@ void fill_bloom_key(const char *data,
}
void add_key_to_filter(const struct bloom_key *key,
- struct bloom_filter *filter,
- const struct bloom_filter_settings *settings)
+ struct bloom_filter *filter,
+ const struct bloom_filter_settings *settings)
{
int i;
uint64_t mod = filter->len * BITS_PER_WORD;
@@ -158,9 +158,22 @@ 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)
+ int compute_if_not_present)
{
struct bloom_filter *filter;
struct bloom_filter_settings settings = DEFAULT_BLOOM_FILTER_SETTINGS;
@@ -193,35 +206,42 @@ struct bloom_filter *get_bloom_filter(struct repository *r,
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 (diff_queued_diff.nr <= max_changes) {
+ if (diffopt.num_changes <= max_changes) {
struct hashmap pathmap;
struct pathmap_hash_entry *e;
struct hashmap_iter iter;
- hashmap_init(&pathmap, NULL, NULL, 0);
+ 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 '/'.
- */
+ * 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));
- hashmap_add(&pathmap, &e->entry);
+
+ if (!hashmap_get(&pathmap, &e->entry, NULL))
+ hashmap_add(&pathmap, &e->entry);
+ else
+ free(e);
if (!last_slash)
last_slash = (char*)path;
@@ -273,4 +293,4 @@ int bloom_filter_contains(const struct bloom_filter *filter,
}
return 1;
-} \ No newline at end of file
+}
diff --git a/bloom.h b/bloom.h
index b935186425..b2a8379a71 100644
--- a/bloom.h
+++ b/bloom.h
@@ -74,8 +74,8 @@ void fill_bloom_key(const char *data,
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);
+ struct bloom_filter *filter,
+ const struct bloom_filter_settings *settings);
void init_bloom_filters(void);
@@ -87,4 +87,4 @@ int bloom_filter_contains(const struct bloom_filter *filter,
const struct bloom_key *key,
const struct bloom_filter_settings *settings);
-#endif \ No newline at end of file
+#endif
diff --git a/bugreport.c b/bugreport.c
index acacca8fef..aa8a489c35 100644
--- a/bugreport.c
+++ b/bugreport.c
@@ -3,6 +3,8 @@
#include "strbuf.h"
#include "help.h"
#include "compat/compiler.h"
+#include "run-command.h"
+
static void get_system_info(struct strbuf *sys_info)
{
@@ -31,6 +33,53 @@ static void get_system_info(struct strbuf *sys_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
@@ -114,6 +163,9 @@ int cmd_main(int argc, const char **argv)
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);
diff --git a/builtin/am.c b/builtin/am.c
index e3dfd93c25..69e50de018 100644
--- a/builtin/am.c
+++ b/builtin/am.c
@@ -1691,7 +1691,6 @@ static int do_interactive(struct am_state *state)
*/
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 @@ next:
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/commit.c b/builtin/commit.c
index a73de0a4c5..d1b7396052 100644
--- a/builtin/commit.c
+++ b/builtin/commit.c
@@ -1494,7 +1494,6 @@ static int git_commit_config(const char *k, const char *v, void *cb)
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")),
@@ -1703,7 +1702,7 @@ int cmd_commit(int argc, const char **argv, const char *prefix)
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);
diff --git a/builtin/fetch.c b/builtin/fetch.c
index 3ae52c015d..b5788c16bf 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)
@@ -1752,7 +1753,6 @@ int cmd_fetch(int argc, const char **argv, const char *prefix)
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");
@@ -1879,13 +1879,8 @@ int cmd_fetch(int argc, const char **argv, const char *prefix)
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/merge.c b/builtin/merge.c
index 923e32acf1..ca6a5dc4bf 100644
--- a/builtin/merge.c
+++ b/builtin/merge.c
@@ -450,7 +450,6 @@ static void finish(struct commit *head_commit,
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);
/*
@@ -458,7 +457,7 @@ static void finish(struct commit *head_commit,
* 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) {
diff --git a/builtin/pack-objects.c b/builtin/pack-objects.c
index 03b85f5166..c5b433a23f 100644
--- a/builtin/pack-objects.c
+++ b/builtin/pack-objects.c
@@ -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)
diff --git a/builtin/prune.c b/builtin/prune.c
index fd9acc7222..02c6ab7cba 100644
--- a/builtin/prune.c
+++ b/builtin/prune.c
@@ -8,6 +8,7 @@
#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/rebase.c b/builtin/rebase.c
index ca6aa0dc7a..37ba76ac3d 100644
--- a/builtin/rebase.c
+++ b/builtin/rebase.c
@@ -722,7 +722,6 @@ static int rebase_write_basic_state(struct rebase_options *opts)
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);
@@ -732,7 +731,7 @@ static int finish_rebase(struct rebase_options *opts)
* 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;
diff --git a/builtin/receive-pack.c b/builtin/receive-pack.c
index d37ab776b3..ea3d0f01af 100644
--- a/builtin/receive-pack.c
+++ b/builtin/receive-pack.c
@@ -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>"),
@@ -876,7 +877,7 @@ static void refuse_unconfigured_deny_delete_current(void)
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);
diff --git a/builtin/repack.c b/builtin/repack.c
index 1b686ee9ce..df287739d9 100644
--- a/builtin/repack.c
+++ b/builtin/repack.c
@@ -13,6 +13,7 @@
#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/rev-parse.c b/builtin/rev-parse.c
index 06056434ed..669dd2fd6f 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
diff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c
index 1a4b391c88..46c03d2a12 100644
--- a/builtin/submodule--helper.c
+++ b/builtin/submodule--helper.c
@@ -2246,6 +2246,37 @@ static int module_config(int argc, const char **argv, const char *prefix)
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 @@ static struct cmd_struct commands[] = {
{"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/ci/config/allow-refs.sample b/ci/config/allow-refs.sample
new file mode 100755
index 0000000000..f157f1945a
--- /dev/null
+++ b/ci/config/allow-refs.sample
@@ -0,0 +1,26 @@
+#!/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-refs.sample allow-refs
+# $EDITOR allow-refs
+# 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/commit-graph.c b/commit-graph.c
index 5ea0c8e15c..e3420ddcbf 100644
--- a/commit-graph.c
+++ b/commit-graph.c
@@ -18,6 +18,7 @@
#include "progress.h"
#include "bloom.h"
#include "commit-slab.h"
+#include "shallow.h"
void git_test_write_commit_graph_or_die(void)
{
diff --git a/commit.c b/commit.c
index c7099daeac..87686a7055 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 @@ static const unsigned char *commit_graft_sha1_access(size_t index, void *table)
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 @@ int for_each_commit_graft(each_commit_graft_fn fn, void *cb_data)
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;
diff --git a/commit.h b/commit.h
index ab91d21131..1b2dea5d85 100644
--- a/commit.h
+++ b/commit.h
@@ -236,6 +236,8 @@ struct commit_graft {
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,55 +249,7 @@ struct commit *get_fork_point(const char *refname, struct commit *commit);
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 commit_shallow_file(struct repository *r, struct lock_file *lk);
-void rollback_shallow_file(struct repository *r, struct lock_file *lk);
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/gmtime.c b/compat/gmtime.c
deleted file mode 100644
index e8362dd2b9..0000000000
--- 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/config.mak.uname b/config.mak.uname
index 5ad43c80b1..c7eba69e54 100644
--- a/config.mak.uname
+++ b/config.mak.uname
@@ -246,7 +246,6 @@ ifeq ($(uname_S),FreeBSD)
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
diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash
index b1d6e5ebed..70ad04e1b2 100644
--- a/contrib/completion/git-completion.bash
+++ b/contrib/completion/git-completion.bash
@@ -1860,6 +1860,7 @@ _git_log ()
$merge
$__git_diff_common_options
--pickaxe-all --pickaxe-regex
+ --patch --no-patch
"
return
;;
diff --git a/credential.h b/credential.h
index d99ec42b2a..c0e17e3554 100644
--- a/credential.h
+++ b/credential.h
@@ -177,8 +177,8 @@ void credential_write(const struct credential *, FILE *);
* 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_write(const struct credential *, FILE *);
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/environment.c b/environment.c
index 10c9061c43..aaca0e91ac 100644
--- a/environment.c
+++ b/environment.c
@@ -17,6 +17,7 @@
#include "argv-array.h"
#include "object-store.h"
#include "chdir-notify.h"
+#include "shallow.h"
int trust_executable_bit = 1;
int trust_ctime = 1;
diff --git a/fetch-pack.c b/fetch-pack.c
index f73a2ce6cb..7eaa19d7c1 100644
--- a/fetch-pack.c
+++ b/fetch-pack.c
@@ -22,6 +22,7 @@
#include "connected.h"
#include "fetch-negotiator.h"
#include "fsck.h"
+#include "shallow.h"
static int transfer_unpack_limit = -1;
static int fetch_unpack_limit = -1;
@@ -34,7 +35,7 @@ static int fetch_fsck_objects = -1;
static int transfer_fsck_objects = -1;
static int agent_supported;
static int server_supports_filtering;
-static struct lock_file shallow_lock;
+static struct shallow_lock shallow_lock;
static const char *alternate_shallow_file;
static struct strbuf fsck_msg_types = STRBUF_INIT;
diff --git a/fsck.c b/fsck.c
index 087a7f1ffc..8bb3ecf282 100644
--- a/fsck.c
+++ b/fsck.c
@@ -523,6 +523,28 @@ int fsck_walk(struct object *obj, void *data, struct fsck_options *options)
}
}
+struct name_stack {
+ const char **names;
+ size_t nr, alloc;
+};
+
+static void name_stack_push(struct name_stack *stack, const char *name)
+{
+ ALLOC_GROW(stack->names, stack->nr + 1, stack->alloc);
+ stack->names[stack->nr++] = name;
+}
+
+static const char *name_stack_pop(struct name_stack *stack)
+{
+ return stack->nr ? stack->names[--stack->nr] : NULL;
+}
+
+static void name_stack_clear(struct name_stack *stack)
+{
+ FREE_AND_NULL(stack->names);
+ stack->nr = stack->alloc = 0;
+}
+
/*
* The entries in a tree are ordered in the _path_ order,
* which means that a directory entry is ordered by adding
@@ -534,7 +556,14 @@ int fsck_walk(struct object *obj, void *data, struct fsck_options *options)
#define TREE_UNORDERED (-1)
#define TREE_HAS_DUPS (-2)
-static int verify_ordered(unsigned mode1, const char *name1, unsigned mode2, const char *name2)
+static int is_less_than_slash(unsigned char c)
+{
+ return '\0' < c && c < '/';
+}
+
+static int verify_ordered(unsigned mode1, const char *name1,
+ unsigned mode2, const char *name2,
+ struct name_stack *candidates)
{
int len1 = strlen(name1);
int len2 = strlen(name2);
@@ -566,6 +595,41 @@ static int verify_ordered(unsigned mode1, const char *name1, unsigned mode2, con
c1 = '/';
if (!c2 && S_ISDIR(mode2))
c2 = '/';
+
+ /*
+ * There can be non-consecutive duplicates due to the implicitly
+ * add slash, e.g.:
+ *
+ * foo
+ * foo.bar
+ * foo.bar.baz
+ * foo.bar/
+ * foo/
+ *
+ * Record non-directory candidates (like "foo" and "foo.bar" in
+ * the example) on a stack and check directory candidates (like
+ * foo/" and "foo.bar/") against that stack.
+ */
+ if (!c1 && is_less_than_slash(c2)) {
+ name_stack_push(candidates, name1);
+ } else if (c2 == '/' && is_less_than_slash(c1)) {
+ for (;;) {
+ const char *p;
+ const char *f_name = name_stack_pop(candidates);
+
+ if (!f_name)
+ break;
+ if (!skip_prefix(name2, f_name, &p))
+ break;
+ if (!*p)
+ return TREE_HAS_DUPS;
+ if (is_less_than_slash(*p)) {
+ name_stack_push(candidates, f_name);
+ break;
+ }
+ }
+ }
+
return c1 < c2 ? 0 : TREE_UNORDERED;
}
@@ -587,6 +651,7 @@ static int fsck_tree(const struct object_id *oid,
struct tree_desc desc;
unsigned o_mode;
const char *o_name;
+ struct name_stack df_dup_candidates = { NULL };
if (init_tree_desc_gently(&desc, buffer, size)) {
retval += report(options, oid, OBJ_TREE, FSCK_MSG_BAD_TREE, "cannot be parsed as a tree");
@@ -666,7 +731,8 @@ static int fsck_tree(const struct object_id *oid,
}
if (o_name) {
- switch (verify_ordered(o_mode, o_name, mode, name)) {
+ switch (verify_ordered(o_mode, o_name, mode, name,
+ &df_dup_candidates)) {
case TREE_UNORDERED:
not_properly_sorted = 1;
break;
@@ -682,6 +748,8 @@ static int fsck_tree(const struct object_id *oid,
o_name = name;
}
+ name_stack_clear(&df_dup_candidates);
+
if (has_null_sha1)
retval += report(options, oid, OBJ_TREE, FSCK_MSG_NULL_SHA1, "contains entries pointing to null sha1");
if (has_full_path)
diff --git a/git-bisect.sh b/git-bisect.sh
index efee12b8b1..71b367a944 100755
--- a/git-bisect.sh
+++ b/git-bisect.sh
@@ -209,6 +209,7 @@ bisect_replay () {
test "$#" -eq 1 || die "$(gettext "No logfile given")"
test -r "$file" || die "$(eval_gettext "cannot read \$file for replaying")"
git bisect--helper --bisect-reset || exit
+ oIFS="$IFS" IFS="$IFS$(printf '\015')"
while read git bisect command rev
do
test "$git $bisect" = "git bisect" || test "$git" = "git-bisect" || continue
@@ -232,6 +233,7 @@ bisect_replay () {
die "$(gettext "?? what are you talking about?")" ;;
esac
done <"$file"
+ IFS="$oIFS"
bisect_auto_next
}
diff --git a/git-compat-util.h b/git-compat-util.h
index 8ba576e81e..a73632e8e4 100644
--- a/git-compat-util.h
+++ b/git-compat-util.h
@@ -1216,13 +1216,6 @@ int access_or_die(const char *path, int mode, unsigned flag);
/* Warn on an inaccessible file if errno indicates this is an error */
int warn_on_fopen_errors(const char *path);
-#ifdef GMTIME_UNRELIABLE_ERRORS
-struct tm *git_gmtime(const time_t *);
-struct tm *git_gmtime_r(const time_t *, struct tm *);
-#define gmtime git_gmtime
-#define gmtime_r git_gmtime_r
-#endif
-
#if !defined(USE_PARENS_AROUND_GETTEXT_N) && defined(__GNUC__)
#define USE_PARENS_AROUND_GETTEXT_N 1
#endif
diff --git a/git-p4.py b/git-p4.py
index b8b2a1679e..d551efb0dd 100755
--- a/git-p4.py
+++ b/git-p4.py
@@ -3214,6 +3214,42 @@ class P4Sync(Command, P4UserMap):
print('Ignoring file outside of prefix: {0}'.format(path))
return hasPrefix
+ def findShadowedFiles(self, files, change):
+ # Perforce allows you commit files and directories with the same name,
+ # so you could have files //depot/foo and //depot/foo/bar both checked
+ # in. A p4 sync of a repository in this state fails. Deleting one of
+ # the files recovers the repository.
+ #
+ # Git will not allow the broken state to exist and only the most recent
+ # of the conflicting names is left in the repository. When one of the
+ # conflicting files is deleted we need to re-add the other one to make
+ # sure the git repository recovers in the same way as perforce.
+ deleted = [f for f in files if f['action'] in self.delete_actions]
+ to_check = set()
+ for f in deleted:
+ path = decode_path(f['path'])
+ to_check.add(path + '/...')
+ while True:
+ path = path.rsplit("/", 1)[0]
+ if path == "/" or path in to_check:
+ break
+ to_check.add(path)
+ to_check = ['%s@%s' % (wildcard_encode(p), change) for p in to_check
+ if self.hasBranchPrefix(p)]
+ if to_check:
+ stat_result = p4CmdList(["-x", "-", "fstat", "-T",
+ "depotFile,headAction,headRev,headType"], stdin=to_check)
+ for record in stat_result:
+ if record['code'] != 'stat':
+ continue
+ if record['headAction'] in self.delete_actions:
+ continue
+ files.append({
+ 'action': 'add',
+ 'path': record['depotFile'],
+ 'rev': record['headRev'],
+ 'type': record['headType']})
+
def commit(self, details, files, branch, parent = "", allow_empty=False):
epoch = details["time"]
author = details["user"]
@@ -3222,11 +3258,14 @@ class P4Sync(Command, P4UserMap):
if self.verbose:
print('commit into {0}'.format(branch))
+ files = [f for f in files
+ if self.hasBranchPrefix(decode_path(f['path']))]
+ self.findShadowedFiles(files, details['change'])
+
if self.clientSpecDirs:
self.clientSpecDirs.update_client_spec_path_cache(files)
- files = [f for (f, path) in ((f, decode_path(f['path'])) for f in files)
- if self.inClientSpec(path) and self.hasBranchPrefix(path)]
+ files = [f for f in files if self.inClientSpec(decode_path(f['path']))]
if gitConfigBool('git-p4.keepEmptyCommits'):
allow_empty = True
diff --git a/git-submodule.sh b/git-submodule.sh
index 08e0439df0..39ebdf25b5 100755
--- a/git-submodule.sh
+++ b/git-submodule.sh
@@ -805,27 +805,7 @@ cmd_set_url() {
shift
done
- if test $# -ne 2
- then
- usage
- fi
-
- # we can't use `git submodule--helper name` here because internally, it
- # hashes the path so a trailing slash could lead to an unintentional no match
- name="$(git submodule--helper list "$1" | cut -f2)"
- if test -z "$name"
- then
- exit 1
- fi
-
- url="$2"
- if test -z "$url"
- then
- exit 1
- fi
-
- git submodule--helper config submodule."$name".url "$url"
- git submodule--helper sync ${GIT_QUIET:+--quiet} "$name"
+ git ${wt_prefix:+-C "$wt_prefix"} ${prefix:+--super-prefix "$prefix"} submodule--helper set-url ${GIT_QUIET:+--quiet} -- "$@"
}
#
diff --git a/git.c b/git.c
index 2e4efb4ff0..a2d337eed7 100644
--- a/git.c
+++ b/git.c
@@ -4,6 +4,7 @@
#include "help.h"
#include "run-command.h"
#include "alias.h"
+#include "shallow.h"
#define RUN_SETUP (1<<0)
#define RUN_SETUP_GENTLY (1<<1)
diff --git a/list-objects-filter.c b/list-objects-filter.c
index 1e8d4e763d..0a3ef3cab3 100644
--- a/list-objects-filter.c
+++ b/list-objects-filter.c
@@ -663,6 +663,9 @@ struct filter *list_objects_filter__init(
assert((sizeof(s_filters) / sizeof(s_filters[0])) == LOFC__COUNT);
+ if (!filter_options)
+ return NULL;
+
if (filter_options->choice >= LOFC__COUNT)
BUG("invalid list-objects filter choice: %d",
filter_options->choice);
diff --git a/merge-recursive.c b/merge-recursive.c
index d92e2acf1e..36948eafb7 100644
--- a/merge-recursive.c
+++ b/merge-recursive.c
@@ -1750,6 +1750,18 @@ static int handle_rename_rename_1to2(struct merge_options *opt,
return -1;
}
+ if (!mfi.clean && mfi.blob.mode == a->mode &&
+ oideq(&mfi.blob.oid, &a->oid)) {
+ /*
+ * Getting here means we were attempting to merge a binary
+ * blob. Since we can't merge binaries, the merge algorithm
+ * just takes one side. But we don't want to copy the
+ * contents of one side to both paths; we'd rather use the
+ * original content at the given path for each path.
+ */
+ oidcpy(&mfi.blob.oid, &b->oid);
+ mfi.blob.mode = b->mode;
+ }
add = &ci->ren2->dst_entry->stages[flip_stage(3)];
if (is_valid(add)) {
add->path = mfi.blob.path = b->path;
diff --git a/midx.c b/midx.c
index 9a61d3b37d..6d1584ca51 100644
--- a/midx.c
+++ b/midx.c
@@ -1293,15 +1293,26 @@ static int compare_by_mtime(const void *a_, const void *b_)
return 0;
}
-static int fill_included_packs_all(struct multi_pack_index *m,
+static int fill_included_packs_all(struct repository *r,
+ struct multi_pack_index *m,
unsigned char *include_pack)
{
- uint32_t i;
+ uint32_t i, count = 0;
+ int pack_kept_objects = 0;
+
+ repo_config_get_bool(r, "repack.packkeptobjects", &pack_kept_objects);
+
+ for (i = 0; i < m->num_packs; i++) {
+ if (prepare_midx_pack(r, m, i))
+ continue;
+ if (!pack_kept_objects && m->packs[i]->pack_keep)
+ continue;
- for (i = 0; i < m->num_packs; i++)
include_pack[i] = 1;
+ count++;
+ }
- return m->num_packs < 2;
+ return count < 2;
}
static int fill_included_packs_batch(struct repository *r,
@@ -1312,6 +1323,9 @@ static int fill_included_packs_batch(struct repository *r,
uint32_t i, packs_to_repack;
size_t total_size;
struct repack_info *pack_info = xcalloc(m->num_packs, sizeof(struct repack_info));
+ int pack_kept_objects = 0;
+
+ repo_config_get_bool(r, "repack.packkeptobjects", &pack_kept_objects);
for (i = 0; i < m->num_packs; i++) {
pack_info[i].pack_int_id = i;
@@ -1338,6 +1352,8 @@ static int fill_included_packs_batch(struct repository *r,
if (!p)
continue;
+ if (!pack_kept_objects && p->pack_keep)
+ continue;
if (open_pack_index(p) || !p->num_objects)
continue;
@@ -1370,6 +1386,14 @@ int midx_repack(struct repository *r, const char *object_dir, size_t batch_size,
struct strbuf base_name = STRBUF_INIT;
struct multi_pack_index *m = load_multi_pack_index(object_dir, 1);
+ /*
+ * When updating the default for these configuration
+ * variables in builtin/repack.c, these must be adjusted
+ * to match.
+ */
+ int delta_base_offset = 1;
+ int use_delta_islands = 0;
+
if (!m)
return 0;
@@ -1378,15 +1402,23 @@ int midx_repack(struct repository *r, const char *object_dir, size_t batch_size,
if (batch_size) {
if (fill_included_packs_batch(r, m, include_pack, batch_size))
goto cleanup;
- } else if (fill_included_packs_all(m, include_pack))
+ } else if (fill_included_packs_all(r, m, include_pack))
goto cleanup;
+ repo_config_get_bool(r, "repack.usedeltabaseoffset", &delta_base_offset);
+ repo_config_get_bool(r, "repack.usedeltaislands", &use_delta_islands);
+
argv_array_push(&cmd.args, "pack-objects");
strbuf_addstr(&base_name, object_dir);
strbuf_addstr(&base_name, "/pack/pack");
argv_array_push(&cmd.args, base_name.buf);
+ if (delta_base_offset)
+ argv_array_push(&cmd.args, "--delta-base-offset");
+ if (use_delta_islands)
+ argv_array_push(&cmd.args, "--delta-islands");
+
if (flags & MIDX_PROGRESS)
argv_array_push(&cmd.args, "--progress");
else
diff --git a/pack-bitmap.c b/pack-bitmap.c
index 49a8d10d0c..4077e731e8 100644
--- a/pack-bitmap.c
+++ b/pack-bitmap.c
@@ -506,7 +506,8 @@ static int should_include(struct commit *commit, void *_data)
static struct bitmap *find_objects(struct bitmap_index *bitmap_git,
struct rev_info *revs,
struct object_list *roots,
- struct bitmap *seen)
+ struct bitmap *seen,
+ struct list_objects_filter_options *filter)
{
struct bitmap *base = NULL;
int needs_walk = 0;
@@ -599,8 +600,9 @@ static struct bitmap *find_objects(struct bitmap_index *bitmap_git,
show_data.bitmap_git = bitmap_git;
show_data.base = base;
- traverse_commit_list(revs, show_commit, show_object,
- &show_data);
+ traverse_commit_list_filtered(filter, revs,
+ show_commit, show_object,
+ &show_data, NULL);
}
return base;
@@ -715,8 +717,9 @@ static int in_bitmapped_pack(struct bitmap_index *bitmap_git,
return 0;
}
-static struct bitmap *find_tip_blobs(struct bitmap_index *bitmap_git,
- struct object_list *tip_objects)
+static struct bitmap *find_tip_objects(struct bitmap_index *bitmap_git,
+ struct object_list *tip_objects,
+ enum object_type type)
{
struct bitmap *result = bitmap_new();
struct object_list *p;
@@ -724,7 +727,7 @@ static struct bitmap *find_tip_blobs(struct bitmap_index *bitmap_git,
for (p = tip_objects; p; p = p->next) {
int pos;
- if (p->item->type != OBJ_BLOB)
+ if (p->item->type != type)
continue;
pos = bitmap_position(bitmap_git, &p->item->oid);
@@ -737,9 +740,10 @@ static struct bitmap *find_tip_blobs(struct bitmap_index *bitmap_git,
return result;
}
-static void filter_bitmap_blob_none(struct bitmap_index *bitmap_git,
- struct object_list *tip_objects,
- struct bitmap *to_filter)
+static void filter_bitmap_exclude_type(struct bitmap_index *bitmap_git,
+ struct object_list *tip_objects,
+ struct bitmap *to_filter,
+ enum object_type type)
{
struct eindex *eindex = &bitmap_git->ext_index;
struct bitmap *tips;
@@ -747,18 +751,21 @@ static void filter_bitmap_blob_none(struct bitmap_index *bitmap_git,
eword_t mask;
uint32_t i;
+ if (type != OBJ_BLOB && type != OBJ_TREE)
+ BUG("filter_bitmap_exclude_type: unsupported type '%d'", type);
+
/*
* The non-bitmap version of this filter never removes
- * blobs which the other side specifically asked for,
+ * objects which the other side specifically asked for,
* so we must match that behavior.
*/
- tips = find_tip_blobs(bitmap_git, tip_objects);
+ tips = find_tip_objects(bitmap_git, tip_objects, type);
/*
* We can use the blob type-bitmap to work in whole words
* for the objects that are actually in the bitmapped packfile.
*/
- for (i = 0, init_type_iterator(&it, bitmap_git, OBJ_BLOB);
+ for (i = 0, init_type_iterator(&it, bitmap_git, type);
i < to_filter->word_alloc && ewah_iterator_next(&mask, &it);
i++) {
if (i < tips->word_alloc)
@@ -773,7 +780,7 @@ static void filter_bitmap_blob_none(struct bitmap_index *bitmap_git,
*/
for (i = 0; i < eindex->count; i++) {
uint32_t pos = i + bitmap_git->pack->num_objects;
- if (eindex->objects[i]->type == OBJ_BLOB &&
+ if (eindex->objects[i]->type == type &&
bitmap_get(to_filter, pos) &&
!bitmap_get(tips, pos))
bitmap_unset(to_filter, pos);
@@ -782,6 +789,14 @@ static void filter_bitmap_blob_none(struct bitmap_index *bitmap_git,
bitmap_free(tips);
}
+static void filter_bitmap_blob_none(struct bitmap_index *bitmap_git,
+ struct object_list *tip_objects,
+ struct bitmap *to_filter)
+{
+ filter_bitmap_exclude_type(bitmap_git, tip_objects, to_filter,
+ OBJ_BLOB);
+}
+
static unsigned long get_size_by_pos(struct bitmap_index *bitmap_git,
uint32_t pos)
{
@@ -820,7 +835,7 @@ static void filter_bitmap_blob_limit(struct bitmap_index *bitmap_git,
eword_t mask;
uint32_t i;
- tips = find_tip_blobs(bitmap_git, tip_objects);
+ tips = find_tip_objects(bitmap_git, tip_objects, OBJ_BLOB);
for (i = 0, init_type_iterator(&it, bitmap_git, OBJ_BLOB);
i < to_filter->word_alloc && ewah_iterator_next(&mask, &it);
@@ -854,6 +869,20 @@ static void filter_bitmap_blob_limit(struct bitmap_index *bitmap_git,
bitmap_free(tips);
}
+static void filter_bitmap_tree_depth(struct bitmap_index *bitmap_git,
+ struct object_list *tip_objects,
+ struct bitmap *to_filter,
+ unsigned long limit)
+{
+ if (limit)
+ BUG("filter_bitmap_tree_depth given non-zero limit");
+
+ filter_bitmap_exclude_type(bitmap_git, tip_objects, to_filter,
+ OBJ_TREE);
+ filter_bitmap_exclude_type(bitmap_git, tip_objects, to_filter,
+ OBJ_BLOB);
+}
+
static int filter_bitmap(struct bitmap_index *bitmap_git,
struct object_list *tip_objects,
struct bitmap *to_filter,
@@ -877,6 +906,15 @@ static int filter_bitmap(struct bitmap_index *bitmap_git,
return 0;
}
+ if (filter->choice == LOFC_TREE_DEPTH &&
+ filter->tree_exclude_depth == 0) {
+ if (bitmap_git)
+ filter_bitmap_tree_depth(bitmap_git, tip_objects,
+ to_filter,
+ filter->tree_exclude_depth);
+ return 0;
+ }
+
/* filter choice not handled */
return -1;
}
@@ -963,7 +1001,8 @@ struct bitmap_index *prepare_bitmap_walk(struct rev_info *revs,
if (haves) {
revs->ignore_missing_links = 1;
- haves_bitmap = find_objects(bitmap_git, revs, haves, NULL);
+ haves_bitmap = find_objects(bitmap_git, revs, haves, NULL,
+ filter);
reset_revision_walk();
revs->ignore_missing_links = 0;
@@ -971,7 +1010,8 @@ struct bitmap_index *prepare_bitmap_walk(struct rev_info *revs,
BUG("failed to perform bitmap walk");
}
- wants_bitmap = find_objects(bitmap_git, revs, wants, haves_bitmap);
+ wants_bitmap = find_objects(bitmap_git, revs, wants, haves_bitmap,
+ filter);
if (!wants_bitmap)
BUG("failed to perform bitmap walk");
diff --git a/run-command.c b/run-command.c
index 0f41af3b55..9b3a57d1e3 100644
--- a/run-command.c
+++ b/run-command.c
@@ -1864,3 +1864,16 @@ int run_processes_parallel_tr2(int n, get_next_task_fn get_next_task,
return result;
}
+
+int run_auto_gc(int quiet)
+{
+ struct argv_array argv_gc_auto = ARGV_ARRAY_INIT;
+ int status;
+
+ argv_array_pushl(&argv_gc_auto, "gc", "--auto", NULL);
+ if (quiet)
+ argv_array_push(&argv_gc_auto, "--quiet");
+ status = run_command_v_opt(argv_gc_auto.argv, RUN_GIT_CMD);
+ argv_array_clear(&argv_gc_auto);
+ return status;
+}
diff --git a/run-command.h b/run-command.h
index 0f3cc73ab6..191dfcdafe 100644
--- a/run-command.h
+++ b/run-command.h
@@ -218,6 +218,11 @@ LAST_ARG_MUST_BE_NULL
int run_hook_le(const char *const *env, const char *name, ...);
int run_hook_ve(const char *const *env, const char *name, va_list args);
+/*
+ * Trigger an auto-gc
+ */
+int run_auto_gc(int quiet);
+
#define RUN_COMMAND_NO_STDIN 1
#define RUN_GIT_CMD 2 /*If this is to be git sub-command */
#define RUN_COMMAND_STDOUT_TO_STDERR 4
diff --git a/send-pack.c b/send-pack.c
index d1b7edc995..0abee22283 100644
--- a/send-pack.c
+++ b/send-pack.c
@@ -15,6 +15,7 @@
#include "oid-array.h"
#include "gpg-interface.h"
#include "cache.h"
+#include "shallow.h"
int option_parse_push_signed(const struct option *opt,
const char *arg, int unset)
diff --git a/sequencer.c b/sequencer.c
index 9d1b3e7d4f..fd7701c88a 100644
--- a/sequencer.c
+++ b/sequencer.c
@@ -5385,10 +5385,13 @@ int todo_list_rearrange_squash(struct todo_list *todo_list)
todo_list->items[i].command =
starts_with(subject, "fixup!") ?
TODO_FIXUP : TODO_SQUASH;
- if (next[i2] < 0)
+ if (tail[i2] < 0) {
+ next[i] = next[i2];
next[i2] = i;
- else
+ } else {
+ next[i] = next[tail[i2]];
next[tail[i2]] = i;
+ }
tail[i2] = i;
} else if (!hashmap_get_from_hash(&subject2item,
strhash(subject), subject)) {
diff --git a/shallow.c b/shallow.c
index 321a27670f..b826de9b67 100644
--- a/shallow.c
+++ b/shallow.c
@@ -14,6 +14,7 @@
#include "commit-slab.h"
#include "list-objects.h"
#include "commit-reach.h"
+#include "shallow.h"
void set_alternate_shallow_file(struct repository *r, const char *path, int override)
{
@@ -38,6 +39,19 @@ int register_shallow(struct repository *r, const struct object_id *oid)
return register_commit_graft(r, graft, 0);
}
+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;
+}
+
int is_repository_shallow(struct repository *r)
{
FILE *fp;
@@ -78,16 +92,16 @@ static void reset_repository_shallow(struct repository *r)
stat_validity_clear(r->parsed_objects->shallow_stat);
}
-int commit_shallow_file(struct repository *r, struct lock_file *lk)
+int commit_shallow_file(struct repository *r, struct shallow_lock *lk)
{
- int res = commit_lock_file(lk);
+ int res = commit_lock_file(&lk->lock);
reset_repository_shallow(r);
return res;
}
-void rollback_shallow_file(struct repository *r, struct lock_file *lk)
+void rollback_shallow_file(struct repository *r, struct shallow_lock *lk)
{
- rollback_lock_file(lk);
+ rollback_lock_file(&lk->lock);
reset_repository_shallow(r);
}
@@ -352,22 +366,22 @@ const char *setup_temporary_shallow(const struct oid_array *extra)
return "";
}
-void setup_alternate_shallow(struct lock_file *shallow_lock,
+void setup_alternate_shallow(struct shallow_lock *shallow_lock,
const char **alternate_shallow_file,
const struct oid_array *extra)
{
struct strbuf sb = STRBUF_INIT;
int fd;
- fd = hold_lock_file_for_update(shallow_lock,
+ fd = hold_lock_file_for_update(&shallow_lock->lock,
git_path_shallow(the_repository),
LOCK_DIE_ON_ERROR);
check_shallow_file_for_update(the_repository);
if (write_shallow_commits(&sb, 0, extra)) {
if (write_in_full(fd, sb.buf, sb.len) < 0)
die_errno("failed to write to %s",
- get_lock_file_path(shallow_lock));
- *alternate_shallow_file = get_lock_file_path(shallow_lock);
+ get_lock_file_path(&shallow_lock->lock));
+ *alternate_shallow_file = get_lock_file_path(&shallow_lock->lock);
} else
/*
* is_repository_shallow() sees empty string as "no
@@ -400,7 +414,7 @@ void advertise_shallow_grafts(int fd)
*/
void prune_shallow(unsigned options)
{
- struct lock_file shallow_lock = LOCK_INIT;
+ struct shallow_lock shallow_lock = SHALLOW_LOCK_INIT;
struct strbuf sb = STRBUF_INIT;
unsigned flags = SEEN_ONLY;
int fd;
@@ -414,14 +428,14 @@ void prune_shallow(unsigned options)
strbuf_release(&sb);
return;
}
- fd = hold_lock_file_for_update(&shallow_lock,
+ fd = hold_lock_file_for_update(&shallow_lock.lock,
git_path_shallow(the_repository),
LOCK_DIE_ON_ERROR);
check_shallow_file_for_update(the_repository);
if (write_shallow_commits_1(&sb, 0, NULL, flags)) {
if (write_in_full(fd, sb.buf, sb.len) < 0)
die_errno("failed to write to %s",
- get_lock_file_path(&shallow_lock));
+ get_lock_file_path(&shallow_lock.lock));
commit_shallow_file(the_repository, &shallow_lock);
} else {
unlink(git_path_shallow(the_repository));
diff --git a/shallow.h b/shallow.h
new file mode 100644
index 0000000000..5b4a96dcd6
--- /dev/null
+++ b/shallow.h
@@ -0,0 +1,81 @@
+#ifndef SHALLOW_H
+#define SHALLOW_H
+
+#include "lockfile.h"
+#include "object.h"
+#include "repository.h"
+#include "strbuf.h"
+
+void set_alternate_shallow_file(struct repository *r, const char *path, int override);
+int register_shallow(struct repository *r, const struct object_id *oid);
+int unregister_shallow(const struct object_id *oid);
+int is_repository_shallow(struct repository *r);
+
+/*
+ * Lock for updating the $GIT_DIR/shallow file.
+ *
+ * Use `commit_shallow_file()` to commit an update, or
+ * `rollback_shallow_file()` to roll it back. In either case, any
+ * in-memory cached information about which commits are shallow will be
+ * appropriately invalidated so that future operations reflect the new
+ * state.
+ */
+struct shallow_lock {
+ struct lock_file lock;
+};
+#define SHALLOW_LOCK_INIT { LOCK_INIT }
+
+/* commit $GIT_DIR/shallow and reset stat-validity checks */
+int commit_shallow_file(struct repository *r, struct shallow_lock *lk);
+/* rollback $GIT_DIR/shallow and reset stat-validity checks */
+void rollback_shallow_file(struct repository *r, struct shallow_lock *lk);
+
+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);
+int write_shallow_commits(struct strbuf *out, int use_pack_protocol,
+ const struct oid_array *extra);
+
+void setup_alternate_shallow(struct shallow_lock *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);
+
+#define PRUNE_SHOW_ONLY 1
+#define PRUNE_QUICK 2
+void prune_shallow(unsigned options);
+
+/*
+ * 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);
+
+extern struct trace_key trace_shallow;
+
+#endif /* SHALLOW_H */
diff --git a/t/helper/test-bloom.c b/t/helper/test-bloom.c
index 77eb27adac..f0aa80b98e 100644
--- a/t/helper/test-bloom.c
+++ b/t/helper/test-bloom.c
@@ -3,7 +3,7 @@
#include "test-tool.h"
#include "commit.h"
-struct bloom_filter_settings settings = DEFAULT_BLOOM_FILTER_SETTINGS;
+static struct bloom_filter_settings settings = DEFAULT_BLOOM_FILTER_SETTINGS;
static void add_string_to_filter(const char *data, struct bloom_filter *filter) {
struct bloom_key key;
@@ -44,7 +44,7 @@ static void get_bloom_filter_for_commit(const struct object_id *commit_oid)
}
static const char *bloom_usage = "\n"
-" test-tool bloom get_murmer3 <string>\n"
+" test-tool bloom get_murmur3 <string>\n"
" test-tool bloom generate_filter <string> [<string>...]\n"
" test-tool get_filter_for_commit <commit-hex>\n";
diff --git a/t/perf/p5310-pack-bitmaps.sh b/t/perf/p5310-pack-bitmaps.sh
index 80c53edca7..b3e725f031 100755
--- a/t/perf/p5310-pack-bitmaps.sh
+++ b/t/perf/p5310-pack-bitmaps.sh
@@ -53,6 +53,11 @@ test_perf 'rev-list count with blob:limit=1k' '
--filter=blob:limit=1k >/dev/null
'
+test_perf 'rev-list count with tree:0' '
+ git rev-list --use-bitmap-index --count --objects --all \
+ --filter=tree:0 >/dev/null
+'
+
test_perf 'simulated partial clone' '
git pack-objects --stdout --all --filter=blob:none </dev/null >/dev/null
'
@@ -86,4 +91,9 @@ test_perf 'pack to file (partial bitmap)' '
git pack-objects --use-bitmap-index --all pack2b </dev/null >/dev/null
'
+test_perf 'rev-list with tree filter (partial bitmap)' '
+ git rev-list --use-bitmap-index --count --objects --all \
+ --filter=tree:0 >/dev/null
+'
+
test_done
diff --git a/t/t0091-bugreport.sh b/t/t0091-bugreport.sh
index 2e73658a5c..526304ff95 100755
--- a/t/t0091-bugreport.sh
+++ b/t/t0091-bugreport.sh
@@ -57,5 +57,20 @@ test_expect_success 'can create leading directories outside of a git dir' '
nongit git bugreport -o foo/bar/baz
'
+test_expect_success 'indicates populated hooks' '
+ test_when_finished rm git-bugreport-hooks.txt &&
+ test_when_finished rm -fr .git/hooks &&
+ rm -fr .git/hooks &&
+ mkdir .git/hooks &&
+ for hook in applypatch-msg prepare-commit-msg.sample
+ do
+ write_script ".git/hooks/$hook" <<-EOF || return 1
+ echo "hook $hook exists"
+ EOF
+ done &&
+ git bugreport -s hooks &&
+ grep applypatch-msg git-bugreport-hooks.txt &&
+ ! grep prepare-commit-msg git-bugreport-hooks.txt
+'
test_done
diff --git a/t/t0095-bloom.sh b/t/t0095-bloom.sh
index 8f9eef116d..232ba2c485 100755
--- a/t/t0095-bloom.sh
+++ b/t/t0095-bloom.sh
@@ -89,8 +89,8 @@ test_expect_success 'get bloom filter for commit with 10 changes' '
git add smallDir &&
git commit -m "commit with 10 changes" &&
cat >expect <<-\EOF &&
- Filter_Length:25
- Filter_Data:82|a0|65|47|0c|92|90|c0|a1|40|02|a0|e2|40|e0|04|0a|9a|66|cf|80|19|85|42|23|
+ Filter_Length:14
+ Filter_Data:02|b3|c4|a0|34|e7|fe|eb|cb|47|fe|a0|e8|72|
EOF
test-tool bloom get_filter_for_commit "$(git rev-parse HEAD)" >actual &&
test_cmp expect actual
@@ -100,7 +100,7 @@ test_expect_success EXPENSIVE 'get bloom filter for commit with 513 changes' '
rm actual &&
rm expect &&
mkdir bigDir &&
- for i in $(test_seq 0 512)
+ for i in $(test_seq 0 511)
do
echo $i >bigDir/$i
done &&
@@ -114,4 +114,4 @@ test_expect_success EXPENSIVE 'get bloom filter for commit with 513 changes' '
test_cmp expect actual
'
-test_done \ No newline at end of file
+test_done
diff --git a/t/t1450-fsck.sh b/t/t1450-fsck.sh
index 449ebc5657..91a6e34f38 100755
--- a/t/t1450-fsck.sh
+++ b/t/t1450-fsck.sh
@@ -257,6 +257,22 @@ test_expect_success 'tree object with duplicate entries' '
test_i18ngrep "error in tree .*contains duplicate file entries" out
'
+test_expect_success 'tree object with dublicate names' '
+ test_when_finished "remove_object \$blob" &&
+ test_when_finished "remove_object \$tree" &&
+ test_when_finished "remove_object \$badtree" &&
+ blob=$(echo blob | git hash-object -w --stdin) &&
+ printf "100644 blob %s\t%s\n" $blob x.2 >tree &&
+ tree=$(git mktree <tree) &&
+ printf "100644 blob %s\t%s\n" $blob x.1 >badtree &&
+ printf "100644 blob %s\t%s\n" $blob x >>badtree &&
+ printf "040000 tree %s\t%s\n" $tree x >>badtree &&
+ badtree=$(git mktree <badtree) &&
+ test_must_fail git fsck 2>out &&
+ test_i18ngrep "$badtree" out &&
+ test_i18ngrep "error in tree .*contains duplicate file entries" out
+'
+
test_expect_success 'unparseable tree object' '
test_oid_cache <<-\EOF &&
junk sha1:twenty-bytes-of-junk
diff --git a/t/t1509-root-work-tree.sh b/t/t1509-root-work-tree.sh
index 553a3f601b..fd2f7abf1c 100755
--- a/t/t1509-root-work-tree.sh
+++ b/t/t1509-root-work-tree.sh
@@ -221,7 +221,7 @@ test_expect_success 'setup' '
rm -rf /.git &&
echo "Initialized empty Git repository in /.git/" > expected &&
git init > result &&
- test_cmp expected result
+ test_i18ncmp expected result
'
test_vars 'auto gitdir, root' ".git" "/" ""
@@ -246,7 +246,7 @@ test_expect_success 'setup' '
cd / &&
echo "Initialized empty Git repository in /" > expected &&
git init --bare > result &&
- test_cmp expected result
+ test_i18ncmp expected result
'
test_vars 'auto gitdir, root' "." "" ""
diff --git a/t/t3415-rebase-autosquash.sh b/t/t3415-rebase-autosquash.sh
index 093de9005b..7bab6000dc 100755
--- a/t/t3415-rebase-autosquash.sh
+++ b/t/t3415-rebase-autosquash.sh
@@ -424,4 +424,20 @@ test_expect_success 'abort last squash' '
! grep first actual
'
+test_expect_success 'fixup a fixup' '
+ echo 0to-fixup >file0 &&
+ test_tick &&
+ git commit -m "to-fixup" file0 &&
+ test_tick &&
+ git commit --squash HEAD -m X --allow-empty &&
+ test_tick &&
+ git commit --squash HEAD^ -m Y --allow-empty &&
+ test_tick &&
+ git commit -m "squash! $(git rev-parse HEAD^)" -m Z --allow-empty &&
+ test_tick &&
+ git commit -m "squash! $(git rev-parse HEAD^^)" -m W --allow-empty &&
+ git rebase -ki --autosquash HEAD~5 &&
+ test XZWY = $(git show | tr -cd W-Z)
+'
+
test_done
diff --git a/t/t4216-log-bloom.sh b/t/t4216-log-bloom.sh
index c7011f33e2..21b68dd6c8 100755
--- a/t/t4216-log-bloom.sh
+++ b/t/t4216-log-bloom.sh
@@ -152,4 +152,4 @@ test_expect_success 'Use Bloom filters if they exist in the latest but not all c
test_bloom_filters_used_when_some_filters_are_missing "-- A/B"
'
-test_done \ No newline at end of file
+test_done
diff --git a/t/t5319-multi-pack-index.sh b/t/t5319-multi-pack-index.sh
index 030a7222b2..7214cab36c 100755
--- a/t/t5319-multi-pack-index.sh
+++ b/t/t5319-multi-pack-index.sh
@@ -538,6 +538,33 @@ test_expect_success 'repack with minimum size does not alter existing packs' '
)
'
+test_expect_success 'repack respects repack.packKeptObjects=false' '
+ test_when_finished rm -f dup/.git/objects/pack/*keep &&
+ (
+ cd dup &&
+ ls .git/objects/pack/*idx >idx-list &&
+ test_line_count = 5 idx-list &&
+ ls .git/objects/pack/*.pack | sed "s/\.pack/.keep/" >keep-list &&
+ test_line_count = 5 keep-list &&
+ for keep in $(cat keep-list)
+ do
+ touch $keep || return 1
+ done &&
+ git multi-pack-index repack --batch-size=0 &&
+ ls .git/objects/pack/*idx >idx-list &&
+ test_line_count = 5 idx-list &&
+ test-tool read-midx .git/objects | grep idx >midx-list &&
+ test_line_count = 5 midx-list &&
+ THIRD_SMALLEST_SIZE=$(test-tool path-utils file-size .git/objects/pack/*pack | sort -n | sed -n 3p) &&
+ BATCH_SIZE=$((THIRD_SMALLEST_SIZE + 1)) &&
+ git multi-pack-index repack --batch-size=$BATCH_SIZE &&
+ ls .git/objects/pack/*idx >idx-list &&
+ test_line_count = 5 idx-list &&
+ test-tool read-midx .git/objects | grep idx >midx-list &&
+ test_line_count = 5 midx-list
+ )
+'
+
test_expect_success 'repack creates a new pack' '
(
cd dup &&
diff --git a/t/t5500-fetch-pack.sh b/t/t5500-fetch-pack.sh
index 52dd1a688c..8c54e34ef1 100755
--- a/t/t5500-fetch-pack.sh
+++ b/t/t5500-fetch-pack.sh
@@ -386,7 +386,7 @@ test_expect_success 'clone shallow with packed refs' '
'
test_expect_success 'in_vain not triggered before first ACK' '
- rm -rf myserver myclient trace &&
+ rm -rf myserver myclient &&
git init myserver &&
test_commit -C myserver foo &&
git clone "file://$(pwd)/myserver" myclient &&
@@ -399,12 +399,12 @@ test_expect_success 'in_vain not triggered before first ACK' '
# The new commit that the client wants to fetch.
test_commit -C myserver bar &&
- GIT_TRACE_PACKET="$(pwd)/trace" git -C myclient fetch --progress origin &&
- test_i18ngrep "Total 3 " trace
+ git -C myclient fetch --progress origin 2>log &&
+ test_i18ngrep "remote: Total 3 " log
'
test_expect_success 'in_vain resetted upon ACK' '
- rm -rf myserver myclient trace &&
+ rm -rf myserver myclient &&
git init myserver &&
# Linked list of commits on master. The first is common; the rest are
@@ -429,8 +429,8 @@ test_expect_success 'in_vain resetted upon ACK' '
# first. The 256th commit is common between the client and the server,
# and should reset in_vain. This allows negotiation to continue until
# the client reports that first_anotherbranch_commit is common.
- GIT_TRACE_PACKET="$(pwd)/trace" git -C myclient fetch --progress origin master &&
- test_i18ngrep "Total 3 " trace
+ git -C myclient fetch --progress origin master 2>log &&
+ test_i18ngrep "Total 3 " log
'
test_expect_success 'fetch in shallow repo unreachable shallow objects' '
diff --git a/t/t5616-partial-clone.sh b/t/t5616-partial-clone.sh
index 88002b24af..8a27452a51 100755
--- a/t/t5616-partial-clone.sh
+++ b/t/t5616-partial-clone.sh
@@ -384,12 +384,11 @@ test_expect_success 'fetch lazy-fetches only to resolve deltas, protocol v2' '
grep "want $(cat hash)" trace
'
-# The following two tests must be in this order, or else
-# the first will not fail. It is important that the srv.bare
-# repository did not have tags during clone, but has tags
+# The following two tests must be in this order. It is important that
+# the srv.bare repository did not have tags during clone, but has tags
# in the fetch.
-test_expect_failure 'verify fetch succeeds when asking for new tags' '
+test_expect_success 'verify fetch succeeds when asking for new tags' '
git clone --filter=blob:none "file://$(pwd)/srv.bare" tag-test &&
for i in I J K
do
diff --git a/t/t6030-bisect-porcelain.sh b/t/t6030-bisect-porcelain.sh
index 1313142564..ac31faefa1 100755
--- a/t/t6030-bisect-porcelain.sh
+++ b/t/t6030-bisect-porcelain.sh
@@ -792,6 +792,13 @@ test_expect_success 'bisect replay with old and new' '
git bisect reset
'
+test_expect_success 'bisect replay with CRLF log' '
+ append_cr <log_to_replay.txt >log_to_replay_crlf.txt &&
+ git bisect replay log_to_replay_crlf.txt >bisect_result_crlf &&
+ grep "$HASH2 is the first new commit" bisect_result_crlf &&
+ git bisect reset
+'
+
test_expect_success 'bisect cannot mix old/new and good/bad' '
git bisect start &&
git bisect bad $HASH4 &&
diff --git a/t/t6042-merge-rename-corner-cases.sh b/t/t6042-merge-rename-corner-cases.sh
index b047cf1c1c..f163893ff9 100755
--- a/t/t6042-merge-rename-corner-cases.sh
+++ b/t/t6042-merge-rename-corner-cases.sh
@@ -1379,4 +1379,59 @@ test_expect_success 'check nested conflicts from rename/rename(2to1)' '
)
'
+# Testcase rename/rename(1to2) of a binary file
+# Commit O: orig
+# Commit A: orig-A
+# Commit B: orig-B
+# Expected: CONFLICT(rename/rename) message, three unstaged entries in the
+# index, and contents of orig-[AB] at path orig-[AB]
+test_setup_rename_rename_1_to_2_binary () {
+ test_create_repo rename_rename_1_to_2_binary &&
+ (
+ cd rename_rename_1_to_2_binary &&
+
+ echo '* binary' >.gitattributes &&
+ git add .gitattributes &&
+
+ test_seq 1 10 >orig &&
+ git add orig &&
+ git commit -m orig &&
+
+ git branch A &&
+ git branch B &&
+
+ git checkout A &&
+ git mv orig orig-A &&
+ test_seq 1 11 >orig-A &&
+ git add orig-A &&
+ git commit -m orig-A &&
+
+ git checkout B &&
+ git mv orig orig-B &&
+ test_seq 0 10 >orig-B &&
+ git add orig-B &&
+ git commit -m orig-B
+
+ )
+}
+
+test_expect_success 'rename/rename(1to2) with a binary file' '
+ test_setup_rename_rename_1_to_2_binary &&
+ (
+ cd rename_rename_1_to_2_binary &&
+
+ git checkout A^0 &&
+
+ test_must_fail git merge -s recursive B^0 &&
+
+ # Make sure the index has the right number of entries
+ git ls-files -s >actual &&
+ test_line_count = 4 actual &&
+
+ git rev-parse A:orig-A B:orig-B >expect &&
+ git hash-object orig-A orig-B >actual &&
+ test_cmp expect actual
+ )
+'
+
test_done
diff --git a/t/t6113-rev-list-bitmap-filters.sh b/t/t6113-rev-list-bitmap-filters.sh
index 145603f124..2b551e6fd0 100755
--- a/t/t6113-rev-list-bitmap-filters.sh
+++ b/t/t6113-rev-list-bitmap-filters.sh
@@ -53,4 +53,25 @@ test_expect_success 'blob:limit filter with specified blob' '
test_bitmap_traversal expect actual
'
+test_expect_success 'tree:0 filter' '
+ git rev-list --objects --filter=tree:0 HEAD >expect &&
+ git rev-list --use-bitmap-index \
+ --objects --filter=tree:0 HEAD >actual &&
+ test_bitmap_traversal expect actual
+'
+
+test_expect_success 'tree:0 filter with specified blob, tree' '
+ git rev-list --objects --filter=tree:0 HEAD HEAD:two.t >expect &&
+ git rev-list --use-bitmap-index \
+ --objects --filter=tree:0 HEAD HEAD:two.t >actual &&
+ test_bitmap_traversal expect actual
+'
+
+test_expect_success 'tree:1 filter' '
+ git rev-list --objects --filter=tree:1 HEAD >expect &&
+ git rev-list --use-bitmap-index \
+ --objects --filter=tree:1 HEAD >actual &&
+ test_cmp expect actual
+'
+
test_done
diff --git a/t/t9834-git-p4-file-dir-bug.sh b/t/t9834-git-p4-file-dir-bug.sh
new file mode 100755
index 0000000000..031e1f8668
--- /dev/null
+++ b/t/t9834-git-p4-file-dir-bug.sh
@@ -0,0 +1,70 @@
+#!/bin/sh
+
+test_description='git p4 directory/file bug handling
+
+This test creates files and directories with the same name in perforce and
+checks that git-p4 recovers from the error at the same time as the perforce
+repository.'
+
+. ./lib-git-p4.sh
+
+test_expect_success 'start p4d' '
+ start_p4d &&
+ test_might_fail p4 configure set submit.collision.check=0
+'
+
+test_expect_success 'init depot' '
+ (
+ cd "$cli" &&
+
+ touch add_file_add_dir_del_file add_file_add_dir_del_dir &&
+ p4 add add_file_add_dir_del_file add_file_add_dir_del_dir &&
+ mkdir add_dir_add_file_del_file add_dir_add_file_del_dir &&
+ touch add_dir_add_file_del_file/file add_dir_add_file_del_dir/file &&
+ p4 add add_dir_add_file_del_file/file add_dir_add_file_del_dir/file &&
+ p4 submit -d "add initial" &&
+
+ rm -f add_file_add_dir_del_file add_file_add_dir_del_dir &&
+ mkdir add_file_add_dir_del_file add_file_add_dir_del_dir &&
+ touch add_file_add_dir_del_file/file add_file_add_dir_del_dir/file &&
+ p4 add add_file_add_dir_del_file/file add_file_add_dir_del_dir/file &&
+ rm -rf add_dir_add_file_del_file add_dir_add_file_del_dir &&
+ touch add_dir_add_file_del_file add_dir_add_file_del_dir &&
+ p4 add add_dir_add_file_del_file add_dir_add_file_del_dir &&
+ p4 submit -d "add conflicting" &&
+
+ p4 delete -k add_file_add_dir_del_file &&
+ p4 delete -k add_file_add_dir_del_dir/file &&
+ p4 delete -k add_dir_add_file_del_file &&
+ p4 delete -k add_dir_add_file_del_dir/file &&
+ p4 submit -d "delete conflicting" &&
+
+ p4 delete -k "add_file_add_dir_del_file/file" &&
+ p4 delete -k "add_file_add_dir_del_dir" &&
+ p4 delete -k "add_dir_add_file_del_file/file" &&
+ p4 delete -k "add_dir_add_file_del_dir" &&
+ p4 submit -d "delete remaining"
+ )
+'
+
+test_expect_success 'clone with git-p4' '
+ git p4 clone --dest="$git" //depot/@1,3
+'
+
+test_expect_success 'check contents' '
+ test_path_is_dir "$git/add_file_add_dir_del_file" &&
+ test_path_is_file "$git/add_file_add_dir_del_dir" &&
+ test_path_is_dir "$git/add_dir_add_file_del_file" &&
+ test_path_is_file "$git/add_dir_add_file_del_dir"
+'
+
+test_expect_success 'rebase and check empty' '
+ git -C "$git" p4 rebase &&
+
+ test_path_is_missing "$git/add_file_add_dir_del_file" &&
+ test_path_is_missing "$git/add_file_add_dir_del_dir" &&
+ test_path_is_missing "$git/add_dir_add_file_del_file" &&
+ test_path_is_missing "$git/add_dir_add_file_del_dir"
+'
+
+test_done
diff --git a/unpack-trees.c b/unpack-trees.c
index 1fe3764f2b..02048dfdac 100644
--- a/unpack-trees.c
+++ b/unpack-trees.c
@@ -562,11 +562,11 @@ static int warn_conflicted_path(struct index_state *istate,
add_rejected_path(o, WARNING_SPARSE_UNMERGED_FILE, conflicting_path);
- /* Find out how many higher stage entries at same path */
- while (++count < istate->cache_nr &&
- !strcmp(conflicting_path,
- istate->cache[i+count]->name))
- /* do nothing */;
+ /* Find out how many higher stage entries are at same path */
+ while ((++count) + i < istate->cache_nr &&
+ !strcmp(conflicting_path, istate->cache[count + i]->name))
+ ; /* do nothing */
+
return count;
}
diff --git a/upload-pack.c b/upload-pack.c
index 902d0ad5e1..0478bff3e7 100644
--- a/upload-pack.c
+++ b/upload-pack.c
@@ -26,6 +26,7 @@
#include "serve.h"
#include "commit-graph.h"
#include "commit-reach.h"
+#include "shallow.h"
/* Remember to update object flag allocation in object.h */
#define THEY_HAVE (1u << 11)
@@ -68,7 +69,6 @@ static const char *pack_objects_hook;
static int filter_capability_requested;
static int allow_filter;
static int allow_ref_in_want;
-static struct list_objects_filter_options filter_options;
static int allow_sideband_all;
@@ -103,7 +103,8 @@ static int write_one_shallow(const struct commit_graft *graft, void *cb_data)
}
static void create_pack_file(const struct object_array *have_obj,
- const struct object_array *want_obj)
+ const struct object_array *want_obj,
+ struct list_objects_filter_options *filter_options)
{
struct child_process pack_objects = CHILD_PROCESS_INIT;
char data[8193], progress[128];
@@ -140,9 +141,9 @@ static void create_pack_file(const struct object_array *have_obj,
argv_array_push(&pack_objects.args, "--delta-base-offset");
if (use_include_tag)
argv_array_push(&pack_objects.args, "--include-tag");
- if (filter_options.choice) {
+ if (filter_options->choice) {
const char *spec =
- expand_list_objects_filter_spec(&filter_options);
+ expand_list_objects_filter_spec(filter_options);
if (pack_objects.use_shell) {
struct strbuf buf = STRBUF_INIT;
sq_quote_buf(&buf, spec);
@@ -848,7 +849,9 @@ static int process_deepen_not(const char *line, struct string_list *deepen_not,
return 0;
}
-static void receive_needs(struct packet_reader *reader, struct object_array *want_obj)
+static void receive_needs(struct packet_reader *reader,
+ struct object_array *want_obj,
+ struct list_objects_filter_options *filter_options)
{
struct object_array shallows = OBJECT_ARRAY_INIT;
struct string_list deepen_not = STRING_LIST_INIT_DUP;
@@ -883,8 +886,8 @@ static void receive_needs(struct packet_reader *reader, struct object_array *wan
if (skip_prefix(reader->line, "filter ", &arg)) {
if (!filter_capability_requested)
die("git upload-pack: filtering capability not negotiated");
- list_objects_filter_die_if_populated(&filter_options);
- parse_list_objects_filter(&filter_options, arg);
+ list_objects_filter_die_if_populated(filter_options);
+ parse_list_objects_filter(filter_options, arg);
continue;
}
@@ -1087,11 +1090,14 @@ void upload_pack(struct upload_pack_options *options)
struct string_list symref = STRING_LIST_INIT_DUP;
struct object_array want_obj = OBJECT_ARRAY_INIT;
struct packet_reader reader;
+ struct list_objects_filter_options filter_options;
stateless_rpc = options->stateless_rpc;
timeout = options->timeout;
daemon_mode = options->daemon_mode;
+ memset(&filter_options, 0, sizeof(filter_options));
+
git_config(upload_pack_config, NULL);
head_ref_namespaced(find_symref, &symref);
@@ -1114,12 +1120,14 @@ void upload_pack(struct upload_pack_options *options)
PACKET_READ_CHOMP_NEWLINE |
PACKET_READ_DIE_ON_ERR_PACKET);
- receive_needs(&reader, &want_obj);
+ receive_needs(&reader, &want_obj, &filter_options);
if (want_obj.nr) {
struct object_array have_obj = OBJECT_ARRAY_INIT;
get_common_commits(&reader, &have_obj, &want_obj);
- create_pack_file(&have_obj, &want_obj);
+ create_pack_file(&have_obj, &want_obj, &filter_options);
}
+
+ list_objects_filter_release(&filter_options);
}
struct upload_pack_data {
@@ -1134,6 +1142,8 @@ struct upload_pack_data {
int deepen_rev_list;
int deepen_relative;
+ struct list_objects_filter_options filter_options;
+
struct packet_writer writer;
unsigned stateless_rpc : 1;
@@ -1169,6 +1179,7 @@ static void upload_pack_data_clear(struct upload_pack_data *data)
oid_array_clear(&data->haves);
object_array_clear(&data->shallows);
string_list_clear(&data->deepen_not, 0);
+ list_objects_filter_release(&data->filter_options);
}
static int parse_want(struct packet_writer *writer, const char *line,
@@ -1306,8 +1317,8 @@ static void process_args(struct packet_reader *request,
}
if (allow_filter && skip_prefix(arg, "filter ", &p)) {
- list_objects_filter_die_if_populated(&filter_options);
- parse_list_objects_filter(&filter_options, p);
+ list_objects_filter_die_if_populated(&data->filter_options);
+ parse_list_objects_filter(&data->filter_options, p);
continue;
}
@@ -1514,7 +1525,7 @@ int upload_pack_v2(struct repository *r, struct argv_array *keys,
send_shallow_info(&data, &want_obj);
packet_writer_write(&data.writer, "packfile\n");
- create_pack_file(&have_obj, &want_obj);
+ create_pack_file(&have_obj, &want_obj, &data.filter_options);
state = FETCH_DONE;
break;
case FETCH_DONE: