diff --git a/.gitattributes b/.gitattributes index 21a8a6960ba..c3e94f4dbf1 100644 --- a/.gitattributes +++ b/.gitattributes @@ -1,5 +1,8 @@ **/generated-archives/*.tar.xz filter=lfs-disabled diff=lfs merge=lfs -text -# assure line feeds don't interfere with our working copy hash +# assure line feeds don't interfere with our working copy hash **/tests/fixtures/**/*.sh text crlf=input eol=lf /justfile text crlf=input eol=lf + +# have GitHub treat the gix-packetline-blocking src copy as auto-generated +gix-packetline-blocking/src/ linguist-generated=true diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 00000000000..ce30635aa48 --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,9 @@ +version: 2 +updates: +- package-ecosystem: github-actions + directory: "/" + schedule: + interval: weekly + groups: + github-actions: + patterns: ["*"] diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 4ce6e9e5b7f..640f112c6ef 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -36,7 +36,7 @@ jobs: runs-on: ubuntu-latest container: debian:buster steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Prerequisites run: apt-get update && apt-get install --no-install-recommends -y ca-certificates curl gcc libc-dev # gcc is required as OS abstraction - name: install Rust via Rustup @@ -47,18 +47,18 @@ jobs: test: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 - - uses: dtolnay/rust-toolchain@stable - - uses: Swatinem/rust-cache@v2 - - name: Setup dependencies - run: - sudo apt-get install tree - - uses: extractions/setup-just@v1 - - name: test - env: - CI: true - GITOXIDE_TEST_IGNORE_ARCHIVES: 1 - run: just ci-test + - uses: actions/checkout@v4 + - uses: dtolnay/rust-toolchain@stable + - uses: Swatinem/rust-cache@v2 + - name: Setup dependencies + run: + sudo apt-get install tree + - uses: extractions/setup-just@v2 + - name: test + env: + CI: true + GIX_TEST_IGNORE_ARCHIVES: 1 + run: just ci-test test-fast: strategy: @@ -69,7 +69,7 @@ jobs: - ubuntu-latest runs-on: ${{ matrix.os }} steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - uses: dtolnay/rust-toolchain@stable - uses: Swatinem/rust-cache@v2 - name: Setup dependencies (macos) @@ -79,10 +79,7 @@ jobs: - name: "cargo check default features" if: startsWith(matrix.os, 'windows') run: cargo check --all --bins --examples - - run: | - # it should never be a failure not to get the caches, as they can be regenerated. - git lfs fetch && git lfs checkout || true - - uses: taiki-e/install-action@v1 + - uses: taiki-e/install-action@v2 with: tool: nextest - name: "Test (nextest)" @@ -94,7 +91,7 @@ jobs: matrix: target: [ armv7-linux-androideabi ] steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - uses: dtolnay/rust-toolchain@stable - uses: Swatinem/rust-cache@v2 - name: Install Rust @@ -102,7 +99,7 @@ jobs: with: toolchain: stable targets: ${{ matrix.target }} - - uses: taiki-e/install-action@v1 + - uses: taiki-e/install-action@v2 with: tool: cross - name: "check" @@ -135,7 +132,7 @@ jobs: target: i686-pc-windows-gnu runs-on: ${{ matrix.os }} steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Install Rust uses: dtolnay/rust-toolchain@master with: @@ -150,20 +147,20 @@ jobs: - name: "Install prerequisites" run: vcpkg install zlib:x64-windows-static-md - name: "Installation from crates.io: gitoxide" - run: cargo +${{ matrix.rust }} install --target ${{ matrix.target }} --target-dir install-artifacts --debug --force gitoxide + run: cargo +${{ matrix.rust }} install --target ${{ matrix.target }} --no-default-features --features max-pure --target-dir install-artifacts --debug --force gitoxide shell: msys2 {0} lint: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - uses: dtolnay/rust-toolchain@master with: toolchain: stable components: clippy,rustfmt - - uses: extractions/setup-just@v1 + - uses: extractions/setup-just@v2 - name: Run cargo clippy - run: just clippy -D warnings + run: just clippy -D warnings -A unknown-lints - name: Run cargo doc run: just doc - name: Run cargo fmt @@ -176,7 +173,7 @@ jobs: # Let's not fail CI for this, it will fail locally often enough, and a crate a little bigger # than allows is no problem either if it comes to that. just check-size || true - + cargo-deny: runs-on: ubuntu-latest strategy: @@ -189,10 +186,11 @@ jobs: continue-on-error: ${{ matrix.checks == 'advisories' }} steps: - - uses: actions/checkout@v3 - - uses: EmbarkStudios/cargo-deny-action@v1 - with: - command: check ${{ matrix.checks }} + - uses: actions/checkout@v4 + - uses: EmbarkStudios/cargo-deny-action@v1 + with: + command: check ${{ matrix.checks }} + wasm: name: WebAssembly runs-on: ubuntu-latest @@ -213,3 +211,32 @@ jobs: name: crates with 'wasm' feature - run: cd gix-pack && cargo build --all-features --target ${{ matrix.target }} name: gix-pack with all features (including wasm) + + check-packetline: + strategy: + fail-fast: false + matrix: + os: + - ubuntu-latest + # We consider this script read-only and its effect is the same everywhere. + # However, when changes are made to `etc/copy-packetline.sh`, re-enable the other platforms for testing. + # - macos-latest + # - windows-latest + runs-on: ${{ matrix.os }} + defaults: + run: + shell: bash + steps: + - uses: actions/checkout@v4 + - name: Check that working tree is initially clean + run: | + set -x + git status + git diff --exit-code + - name: Regenerate gix-packetline-blocking/src + run: etc/copy-packetline.sh + - name: Check that gix-packetline-blocking/src was already up to date + run: | + set -x + git status + git diff --exit-code diff --git a/.github/workflows/cifuzz.yml b/.github/workflows/cifuzz.yml index 0fd6950846c..350029cec1f 100644 --- a/.github/workflows/cifuzz.yml +++ b/.github/workflows/cifuzz.yml @@ -17,7 +17,7 @@ jobs: language: rust fuzz-seconds: 600 - name: Upload Crash - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 if: failure() && steps.build.outcome == 'success' with: name: artifacts diff --git a/.github/workflows/cron.yml b/.github/workflows/cron.yml index 812b3ec7cf0..c4479841d8a 100644 --- a/.github/workflows/cron.yml +++ b/.github/workflows/cron.yml @@ -9,7 +9,7 @@ jobs: stress: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - uses: Swatinem/rust-cache@v2 - name: stress run: make stress diff --git a/.github/workflows/msrv.yml b/.github/workflows/msrv.yml index 1e77eaa241d..4d2e98cbfd7 100644 --- a/.github/workflows/msrv.yml +++ b/.github/workflows/msrv.yml @@ -18,8 +18,13 @@ jobs: - windows-2019 - ubuntu-latest runs-on: ${{ matrix.os }} + env: + # dictated by `firefox` to support the `helix` editor, but now driven by the `time` crate. IMPORTANT: adjust etc/msrv-badge.svg as well + rust_version: 1.67.0 steps: - - uses: actions/checkout@v3 - - uses: dtolnay/rust-toolchain@1.67.0 # dictated by `firefox` to support the `helix` editor, but now driven by the `time` crate. IMPORTANT: adjust etc/msrv-badge.svg as well - - uses: extractions/setup-just@v1 + - uses: actions/checkout@v4 + - uses: extractions/setup-just@v2 + - run: | + rustup toolchain install ${{ env.rust_version }} --profile minimal --no-self-update + rustup default ${{ env.rust_version }} - run: just ci-check-msrv diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 3cb6d99c6f0..06e73353cfd 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -60,7 +60,7 @@ jobs: run: echo "${{ env.ARTIFACT_VERSION }}" > artifacts/release-version - name: Upload artifacts - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 with: name: artifacts path: artifacts @@ -129,7 +129,7 @@ jobs: runs-on: ${{ matrix.os }} steps: - name: Checkout repository - uses: actions/checkout@v3 + uses: actions/checkout@v4 with: fetch-depth: 1 @@ -161,7 +161,7 @@ jobs: echo "target dir is: ${{ env.TARGET_DIR }}" - name: Get release download URL - uses: actions/download-artifact@v3 + uses: actions/download-artifact@v4 with: name: artifacts path: artifacts @@ -211,7 +211,7 @@ jobs: fi - name: Upload release archive - uses: actions/upload-release-asset@v1.0.1 + uses: actions/upload-release-asset@v1.0.2 env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} with: diff --git a/.gitignore b/.gitignore index 33cedd75f8d..78cd84318a8 100644 --- a/.gitignore +++ b/.gitignore @@ -18,3 +18,6 @@ $/tests/fixtures/commit-graphs/ # newer Git sees these as precious, older Git falls through to the pattern above $**/fuzz/Cargo.lock + +# Instead of adding more environment-specific ignores here, like for the IDE in use, prefer Git's user-global +# `core.excludesFile` mechanism, see https://git-scm.com/docs/git-config#Documentation/git-config.txt-coreexcludesFile. diff --git a/.gov/info.yml b/.gov/info.yml index 2adfce84eee..5c835210b13 100644 --- a/.gov/info.yml +++ b/.gov/info.yml @@ -8,6 +8,6 @@ maintainers: - push directly to 'main' branch - reviews or suggestions by other maintainers if PRs are used - co-ownership of contributed crates - - reveive sponsorship via GitHub from project page + - receive sponsorship via GitHub from project page diff --git a/CHANGELOG.md b/CHANGELOG.md index 228c6207b8e..a4046ac2cc1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,123 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## 0.36.0 (2024-05-22) + +### New Features + + - checkout respects options for `core.protectHFS` and `core.protectNTFS`. + This also adds `gitoxide.core.protectWindows` as a way to enforce + additional restrictions that are usually only available on Windows. + + Note that `core.protectNFS` is always enabled by default, just like + [it is in Git](https://github.com/git/git/commit/9102f958ee5254b10c0be72672aa3305bf4f4704). + +### Bug Fixes + + - more robustness in the face of a trampling-herd of threads loading a single index. + The motivating example is here: https://github.com/praetorian-inc/noseyparker/issues/179 + + Previously, it was possible for a trampling herd of threads to consolidate the + disk state. Most of them would be 'needs-init' threads which could notice that + the initialization already happened, and just use that. + + But a thread might be late for the party and somehow manages to not get any + newly loaded index, and thus tries to consolidate with what's on disk again. + Then it would again determine no change, and return nothing, causing the caller + to abort and not find objects it should find because it wouldn't see the index + that it should have seen. + + The reason the thread got into this mess is that the 'is-load-ongoing' flagging + was racy itself, so it would not wait for ongoing loads and just conclude nothing + happened. An extra delay (by yielding) now assures it either seees the loading state + and waits for it, sees the newly loaded indices. + + Note that this issue can be reproduced with: + + ``` + './target/release/gix -r repo-with-one-pack -t10 --trace odb stats --extra-header-lookup' + ``` + +### Commit Statistics + + + + - 6 commits contributed to the release over the course of 10 calendar days. + - 38 days passed between releases. + - 2 commits were understood as [conventional](https://www.conventionalcommits.org). + - 0 issues like '(#ID)' were seen in commit messages + +### Commit Details + + + +
view details + + * **Uncategorized** + - Merge branch 'various-fixes' ([`d6cd449`](https://github.com/Byron/gitoxide/commit/d6cd44930fb204b06e2b70fc6965e7705530c47a)) + - Merge pull request from GHSA-7w47-3wg8-547c ([`79dce79`](https://github.com/Byron/gitoxide/commit/79dce79c62f6072aa2653780d590dc3993dfa401)) + - Checkout respects options for `core.protectHFS` and `core.protectNTFS`. ([`886d6b5`](https://github.com/Byron/gitoxide/commit/886d6b58e4612ac21cc660ea4ddf1dd0b49d1c6e)) + - Mark safety-related core-flags as planned ([`f3d5a69`](https://github.com/Byron/gitoxide/commit/f3d5a69bbe0ad14502ce617dc580cc2aa481bb0a)) + - Merge branch 'status' ([`04ef31e`](https://github.com/Byron/gitoxide/commit/04ef31e9d6f5332d49037a5a4c248ebbb5aaf92b)) + - More robustness in the face of a trampling-herd of threads loading a single index. ([`addf446`](https://github.com/Byron/gitoxide/commit/addf446f052ff74edcdb083f2b2968b313daa940)) +
+ +## 0.35.0 (2024-04-13) + +### New Features + + - add `gix status --ignored` support + - add `gix status --index-worktree-renames` + This enables rename-tracking between worktree and index, something + that Git also doesn't do or doesn't do by default. + It is, however, available in `git2`. + - `gix status` with submodule and rewrite support. + Submodule changes are now picked up as long as the submodule is + in the index. + Further, it's possible to enable rename-tracking between + index and worktree separately. + - add `gix is-clean|is-changed` + It's a good way to compare the time it takes to run a full status + compared to a quick is-dirty check. + - `gix submodules list --dirty-suffix` for dirty-information + This is a submodule-centric and greatly simplified way of obtaining + describe information with dirty-suffix. + + Note that `status` information is also possible, but it seems + hard to display nicely, which this command isn't great at + in the first place. + - add `gix commit describe --dirty-suffix` + That way a suffix will be added depending on the dirty-state of the repository. + +### Commit Statistics + + + + - 11 commits contributed to the release over the course of 34 calendar days. + - 47 days passed between releases. + - 6 commits were understood as [conventional](https://www.conventionalcommits.org). + - 0 issues like '(#ID)' were seen in commit messages + +### Commit Details + + + +
view details + + * **Uncategorized** + - Merge pull request #1341 from szepeviktor/typos ([`55f379b`](https://github.com/Byron/gitoxide/commit/55f379bc47065822d078393d83d30c0835a89782)) + - Fix typos ([`f72ecce`](https://github.com/Byron/gitoxide/commit/f72ecce45babcad2a0c9b73c79d01ff502907a57)) + - Merge branch 'status' ([`3e5c974`](https://github.com/Byron/gitoxide/commit/3e5c974dd62ac134711c6c2f5a5490187a6ea55e)) + - Add `gix status --format` to communicate the current format is very simple. ([`23bea36`](https://github.com/Byron/gitoxide/commit/23bea36f046a6f652cd1e06885ae132c85bb4f05)) + - Add `gix status --ignored` support ([`84c74ff`](https://github.com/Byron/gitoxide/commit/84c74ffa698d35f8395c63db6acd3d0e6700d07f)) + - Add `gix status --index-worktree-renames` ([`66e87cd`](https://github.com/Byron/gitoxide/commit/66e87cd31c060c3f97ac685ee0541c408f600362)) + - Fix lints for nightly, and clippy ([`f8ce3d0`](https://github.com/Byron/gitoxide/commit/f8ce3d0721b6a53713a9392f2451874f520bc44c)) + - `gix status` with submodule and rewrite support. ([`61c002b`](https://github.com/Byron/gitoxide/commit/61c002bc4ca5b5345c411e561fdcb492e7ae1d97)) + - Add `gix is-clean|is-changed` ([`98b3680`](https://github.com/Byron/gitoxide/commit/98b368095ec99d1bc287da7f9294a9fce424deed)) + - `gix submodules list --dirty-suffix` for dirty-information ([`afd20ca`](https://github.com/Byron/gitoxide/commit/afd20caadb40b6b793f2099b7232669f9a8f9086)) + - Add `gix commit describe --dirty-suffix` ([`58231b4`](https://github.com/Byron/gitoxide/commit/58231b418fa39ea122ef41bb7691289f5b0be855)) +
+ ## 0.34.0 (2024-02-25) ### New Features diff --git a/Cargo.lock b/Cargo.lock index deac531b0d6..7e986db4455 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -62,23 +62,24 @@ dependencies = [ [[package]] name = "anstream" -version = "0.6.5" +version = "0.6.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d664a92ecae85fd0a7392615844904654d1d5f5514837f471ddef4a057aba1b6" +checksum = "418c75fa768af9c03be99d17643f93f79bbba589895012a80e3452a19ddda15b" dependencies = [ "anstyle", "anstyle-parse", "anstyle-query", "anstyle-wincon", "colorchoice", + "is_terminal_polyfill", "utf8parse", ] [[package]] name = "anstyle" -version = "1.0.4" +version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7079075b41f533b8c61d2a4d073c4676e1f8b249ff94a393b0595db304e0dd87" +checksum = "038dfcf04a5feb68e9c60b21c9625a54c2c0616e79b72b0fd87075a056ae1d1b" [[package]] name = "anstyle-parse" @@ -114,6 +115,15 @@ version = "1.0.77" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c9d19de80eff169429ac1e9f48fffb163916b448a44e8e046186232046d9e1f9" +[[package]] +name = "arbitrary" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7d5a26814d8dcb93b0e5a0ff3c6d80a8843bafb21b39e8e18a6f05471870e110" +dependencies = [ + "derive_arbitrary", +] + [[package]] name = "arc-swap" version = "1.6.0" @@ -175,7 +185,7 @@ dependencies = [ "async-lock 3.2.0", "async-task", "concurrent-queue", - "fastrand 2.0.1", + "fastrand 2.1.0", "futures-lite 2.1.0", "slab", ] @@ -348,6 +358,12 @@ version = "0.21.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "35636a1494ede3b646cc98f74f8e62c773a38a659ebc777a2cf26b9b74171df9" +[[package]] +name = "base64" +version = "0.22.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" + [[package]] name = "basic-toml" version = "0.1.7" @@ -390,7 +406,7 @@ dependencies = [ "async-channel 2.1.1", "async-lock 3.2.0", "async-task", - "fastrand 2.0.1", + "fastrand 2.1.0", "futures-io", "futures-lite 2.1.0", "piper", @@ -419,15 +435,9 @@ dependencies = [ [[package]] name = "bumpalo" -version = "3.14.0" +version = "3.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f30e7476521f6f8af1a1c4c0b8cc94f0bee37d91763d0ca2665f299b6cd8aec" - -[[package]] -name = "byteorder" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" +checksum = "79296716171880943b8470b5f8d03aa55eb2e645a4874bdbb28adb49162e012c" [[package]] name = "bytes" @@ -640,9 +650,9 @@ checksum = "19d374276b40fb8bbdee95aef7c7fa6b5316ec764510eb64b8dd0e2ed0d7e7f5" [[package]] name = "crc32fast" -version = "1.3.2" +version = "1.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b540bd8bc810d3885c6ea91e2018302f68baba2129ab3e88f32389ee9370880d" +checksum = "a97769d94ddab943e4510d138150169a2758b5ef3eb191a9ee688de3e23ef7b3" dependencies = [ "cfg-if", ] @@ -699,11 +709,10 @@ dependencies = [ [[package]] name = "crossbeam-channel" -version = "0.5.10" +version = "0.5.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "82a9b73a36529d9c47029b9fb3a6f0ea3cc916a261195352ba19e770fc1748b2" +checksum = "33480d6946193aa8033910124896ca395333cae7e2d1113d1fef6c3272217df2" dependencies = [ - "cfg-if", "crossbeam-utils", ] @@ -741,12 +750,9 @@ dependencies = [ [[package]] name = "crossbeam-utils" -version = "0.8.18" +version = "0.8.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c3a430a770ebd84726f584a90ee7f020d28db52c6d02138900f22341f866d39c" -dependencies = [ - "cfg-if", -] +checksum = "22ec99545bb0ed0ea7bb9b8e1e9122ea386ff8a48c0922e43f36d45ab09e0e80" [[package]] name = "crossterm" @@ -802,24 +808,24 @@ dependencies = [ [[package]] name = "curl" -version = "0.4.44" +version = "0.4.46" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "509bd11746c7ac09ebd19f0b17782eae80aadee26237658a6b4808afb5c11a22" +checksum = "1e2161dd6eba090ff1594084e95fd67aeccf04382ffea77999ea94ed42ec67b6" dependencies = [ "curl-sys", "libc", "openssl-probe", "openssl-sys", "schannel", - "socket2 0.4.10", - "winapi", + "socket2 0.5.5", + "windows-sys 0.52.0", ] [[package]] name = "curl-sys" -version = "0.4.70+curl-8.5.0" +version = "0.4.72+curl-8.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c0333d8849afe78a4c8102a429a446bfdd055832af071945520e835ae2d841e" +checksum = "29cbdc8314c447d11e8fd156dcdd031d9e02a7a976163e396b548c03153bc9ea" dependencies = [ "cc", "libc", @@ -828,7 +834,7 @@ dependencies = [ "pkg-config", "rustls-ffi", "vcpkg", - "windows-sys 0.48.0", + "windows-sys 0.52.0", ] [[package]] @@ -844,17 +850,11 @@ dependencies = [ "parking_lot_core", ] -[[package]] -name = "data-encoding" -version = "2.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7e962a19be5cfc3f3bf6dd8f61eb50107f356ad6270fbb3ed41476571db78be5" - [[package]] name = "defer" -version = "0.1.0" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "647605a6345d5e89c3950a36a638c56478af9b414c55c6f2477c73b115f9acde" +checksum = "930c7171c8df9fb1782bdf9b918ed9ed2d33d1d22300abb754f9085bc48bf8e8" [[package]] name = "deranged" @@ -865,6 +865,17 @@ dependencies = [ "powerfmt", ] +[[package]] +name = "derive_arbitrary" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67e77553c4162a157adbf834ebae5b415acbecbeafc7a74b0e886657506a7611" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.47", +] + [[package]] name = "diff" version = "0.1.13" @@ -881,6 +892,17 @@ dependencies = [ "crypto-common", ] +[[package]] +name = "displaydoc" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "487585f4d0c6655fe74905e2504d8ad6908e4db67f744eb140876906c2f3175d" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.47", +] + [[package]] name = "document-features" version = "0.2.8" @@ -911,23 +933,11 @@ dependencies = [ "cfg-if", ] -[[package]] -name = "enum-as-inner" -version = "0.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5ffccbb6966c05b32ef8fbac435df276c4ae4d3dc55a8cd0eb9745e6c12f546a" -dependencies = [ - "heck", - "proc-macro2", - "quote", - "syn 2.0.47", -] - [[package]] name = "env_logger" -version = "0.10.1" +version = "0.10.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "95b3f3e67048839cb0d0781f445682a35113da7121f7c949db0e2be96a4fbece" +checksum = "4cd405aab171cb85d6735e5c8d9db038c17d3ca007a4d2c25f337935c3d90580" dependencies = [ "humantime", "is-terminal", @@ -1022,9 +1032,9 @@ dependencies = [ [[package]] name = "fastrand" -version = "2.0.1" +version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "25cbce373ec4653f1a01a31e8a5e5ec0c622dc27ff9c4e6606eefef5cbbed4a5" +checksum = "9fc0510504f03c51ada170672ac806f1f105a88aa97a5281117e1ddc3368e51a" [[package]] name = "filetime" @@ -1164,7 +1174,7 @@ version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "aeee267a1883f7ebef3700f262d2d54de95dfaf38189015a74fdc4e0c7ad8143" dependencies = [ - "fastrand 2.0.1", + "fastrand 2.1.0", "futures-core", "futures-io", "parking", @@ -1231,7 +1241,7 @@ checksum = "4271d37baee1b8c7e4b708028c57d816cf9d2434acb33a549475f78c181f6253" [[package]] name = "gitoxide" -version = "0.34.0" +version = "0.36.1" dependencies = [ "anyhow", "clap", @@ -1242,7 +1252,7 @@ dependencies = [ "futures-lite 2.1.0", "gitoxide-core", "gix", - "gix-features 0.38.0", + "gix-features 0.38.2", "is-terminal", "once_cell", "prodash 28.0.0", @@ -1256,7 +1266,7 @@ dependencies = [ [[package]] name = "gitoxide-core" -version = "0.36.0" +version = "0.38.0" dependencies = [ "anyhow", "async-io 2.2.2", @@ -1294,66 +1304,65 @@ dependencies = [ [[package]] name = "gix" -version = "0.59.0" +version = "0.63.0" dependencies = [ "anyhow", "async-std", - "curl", "document-features", - "gix-actor 0.30.1", + "gix-actor 0.31.2", "gix-archive", - "gix-attributes 0.22.1", + "gix-attributes 0.22.2", "gix-command", - "gix-commitgraph 0.24.1", + "gix-commitgraph 0.24.2", "gix-config", "gix-credentials", - "gix-date 0.8.4", + "gix-date 0.8.6", "gix-diff", "gix-dir", - "gix-discover 0.30.0", - "gix-features 0.38.0", + "gix-discover 0.32.0", + "gix-features 0.38.2", "gix-filter", - "gix-fs 0.10.0", - "gix-glob 0.16.1", - "gix-hash 0.14.1", - "gix-hashtable 0.5.1", - "gix-ignore 0.11.1", - "gix-index 0.30.0", - "gix-lock 13.1.0", + "gix-fs 0.11.1", + "gix-glob 0.16.3", + "gix-hash 0.14.2", + "gix-hashtable 0.5.2", + "gix-ignore 0.11.2", + "gix-index 0.33.0", + "gix-lock 14.0.0", "gix-macros", "gix-mailmap", "gix-negotiate", - "gix-object 0.41.1", + "gix-object 0.42.2", "gix-odb", "gix-pack", - "gix-path 0.10.6", + "gix-path 0.10.7", "gix-pathspec", "gix-prompt", "gix-protocol", - "gix-ref 0.42.0", + "gix-ref 0.44.1", "gix-refspec", "gix-revision", - "gix-revwalk 0.12.0", - "gix-sec 0.10.5", + "gix-revwalk 0.13.1", + "gix-sec 0.10.6", "gix-status", "gix-submodule", - "gix-tempfile 13.1.0", + "gix-tempfile 14.0.0", "gix-testtools", - "gix-trace 0.1.7", + "gix-trace 0.1.9", "gix-transport", - "gix-traverse 0.37.0", + "gix-traverse 0.39.1", "gix-url", - "gix-utils 0.1.10", - "gix-validate 0.8.3", - "gix-worktree 0.31.0", + "gix-utils 0.1.12", + "gix-validate 0.8.5", + "gix-worktree 0.34.0", "gix-worktree-state", "gix-worktree-stream", "is_ci", "once_cell", "parking_lot", + "pretty_assertions", "prodash 28.0.0", "regex", - "reqwest", "serde", "serial_test", "signal-hook", @@ -1392,15 +1401,15 @@ dependencies = [ [[package]] name = "gix-actor" -version = "0.30.1" +version = "0.31.2" dependencies = [ "bstr", "document-features", - "gix-date 0.8.4", - "gix-features 0.38.0", - "gix-hash 0.14.1", + "gix-date 0.8.6", + "gix-features 0.38.2", + "gix-hash 0.14.2", "gix-testtools", - "gix-utils 0.1.10", + "gix-utils 0.1.12", "itoa", "pretty_assertions", "serde", @@ -1410,20 +1419,20 @@ dependencies = [ [[package]] name = "gix-archive" -version = "0.10.0" +version = "0.13.1" dependencies = [ "bstr", "document-features", "flate2", - "gix-attributes 0.22.1", - "gix-date 0.8.4", + "gix-attributes 0.22.2", + "gix-date 0.8.6", "gix-filter", - "gix-hash 0.14.1", - "gix-object 0.41.1", + "gix-hash 0.14.2", + "gix-object 0.42.2", "gix-odb", - "gix-path 0.10.6", + "gix-path 0.10.7", "gix-testtools", - "gix-worktree 0.31.0", + "gix-worktree 0.34.0", "gix-worktree-stream", "tar", "thiserror", @@ -1450,16 +1459,16 @@ dependencies = [ [[package]] name = "gix-attributes" -version = "0.22.1" +version = "0.22.2" dependencies = [ "bstr", "document-features", - "gix-fs 0.10.0", - "gix-glob 0.16.1", - "gix-path 0.10.6", - "gix-quote 0.4.11", + "gix-fs 0.11.1", + "gix-glob 0.16.3", + "gix-path 0.10.7", + "gix-quote 0.4.12", "gix-testtools", - "gix-trace 0.1.7", + "gix-trace 0.1.9", "kstring", "serde", "smallvec", @@ -1478,7 +1487,7 @@ dependencies = [ [[package]] name = "gix-bitmap" -version = "0.2.10" +version = "0.2.11" dependencies = [ "gix-testtools", "thiserror", @@ -1495,19 +1504,19 @@ dependencies = [ [[package]] name = "gix-chunk" -version = "0.4.7" +version = "0.4.8" dependencies = [ "thiserror", ] [[package]] name = "gix-command" -version = "0.3.5" +version = "0.3.7" dependencies = [ "bstr", - "gix-path 0.10.6", + "gix-path 0.10.7", "gix-testtools", - "gix-trace 0.1.7", + "gix-trace 0.1.9", "shell-words", ] @@ -1527,14 +1536,14 @@ dependencies = [ [[package]] name = "gix-commitgraph" -version = "0.24.1" +version = "0.24.2" dependencies = [ "bstr", "document-features", - "gix-chunk 0.4.7", - "gix-date 0.8.4", - "gix-features 0.38.0", - "gix-hash 0.14.1", + "gix-chunk 0.4.8", + "gix-date 0.8.6", + "gix-features 0.38.2", + "gix-hash 0.14.2", "gix-testtools", "memmap2 0.9.3", "serde", @@ -1543,17 +1552,17 @@ dependencies = [ [[package]] name = "gix-config" -version = "0.35.0" +version = "0.37.0" dependencies = [ "bstr", "criterion", "document-features", "gix-config-value", - "gix-features 0.38.0", - "gix-glob 0.16.1", - "gix-path 0.10.6", - "gix-ref 0.42.0", - "gix-sec 0.10.5", + "gix-features 0.38.2", + "gix-glob 0.16.3", + "gix-path 0.10.7", + "gix-ref 0.44.1", + "gix-sec 0.10.6", "memchr", "once_cell", "serde", @@ -1572,21 +1581,21 @@ dependencies = [ "cap", "gix", "gix-config", - "gix-path 0.10.6", - "gix-ref 0.42.0", - "gix-sec 0.10.5", + "gix-path 0.10.7", + "gix-ref 0.44.1", + "gix-sec 0.10.6", "gix-testtools", "serial_test", ] [[package]] name = "gix-config-value" -version = "0.14.5" +version = "0.14.6" dependencies = [ "bitflags 2.4.1", "bstr", "document-features", - "gix-path 0.10.6", + "gix-path 0.10.7", "libc", "serde", "thiserror", @@ -1594,17 +1603,17 @@ dependencies = [ [[package]] name = "gix-credentials" -version = "0.24.1" +version = "0.24.2" dependencies = [ "bstr", "document-features", "gix-command", "gix-config-value", - "gix-path 0.10.6", + "gix-path 0.10.7", "gix-prompt", - "gix-sec 0.10.5", + "gix-sec 0.10.6", "gix-testtools", - "gix-trace 0.1.7", + "gix-trace 0.1.9", "gix-url", "serde", "thiserror", @@ -1624,11 +1633,11 @@ dependencies = [ [[package]] name = "gix-date" -version = "0.8.4" +version = "0.8.6" dependencies = [ "bstr", "document-features", - "gix-hash 0.14.1", + "gix-hash 0.14.2", "gix-testtools", "itoa", "once_cell", @@ -1639,20 +1648,20 @@ dependencies = [ [[package]] name = "gix-diff" -version = "0.41.0" +version = "0.44.0" dependencies = [ "bstr", "document-features", "getrandom", "gix-command", "gix-filter", - "gix-fs 0.10.0", - "gix-hash 0.14.1", - "gix-object 0.41.1", - "gix-path 0.10.6", - "gix-tempfile 13.1.0", - "gix-trace 0.1.7", - "gix-worktree 0.31.0", + "gix-fs 0.11.1", + "gix-hash 0.14.2", + "gix-object 0.42.2", + "gix-path 0.10.7", + "gix-tempfile 14.0.0", + "gix-trace 0.1.9", + "gix-worktree 0.34.0", "imara-diff", "serde", "thiserror", @@ -1664,32 +1673,33 @@ version = "0.0.0" dependencies = [ "gix-diff", "gix-filter", - "gix-fs 0.10.0", - "gix-hash 0.14.1", - "gix-object 0.41.1", + "gix-fs 0.11.1", + "gix-hash 0.14.2", + "gix-object 0.42.2", "gix-odb", "gix-testtools", - "gix-traverse 0.37.0", - "gix-worktree 0.31.0", + "gix-traverse 0.39.1", + "gix-worktree 0.34.0", + "pretty_assertions", "shell-words", ] [[package]] name = "gix-dir" -version = "0.1.0" +version = "0.5.0" dependencies = [ "bstr", - "gix-discover 0.30.0", - "gix-fs 0.10.0", - "gix-ignore 0.11.1", - "gix-index 0.30.0", - "gix-object 0.41.1", - "gix-path 0.10.6", + "gix-discover 0.32.0", + "gix-fs 0.11.1", + "gix-ignore 0.11.2", + "gix-index 0.33.0", + "gix-object 0.42.2", + "gix-path 0.10.7", "gix-pathspec", "gix-testtools", - "gix-trace 0.1.7", - "gix-utils 0.1.10", - "gix-worktree 0.31.0", + "gix-trace 0.1.9", + "gix-utils 0.1.12", + "gix-worktree 0.34.0", "pretty_assertions", "thiserror", ] @@ -1711,16 +1721,16 @@ dependencies = [ [[package]] name = "gix-discover" -version = "0.30.0" +version = "0.32.0" dependencies = [ "bstr", "defer", "dunce", - "gix-fs 0.10.0", - "gix-hash 0.14.1", - "gix-path 0.10.6", - "gix-ref 0.42.0", - "gix-sec 0.10.5", + "gix-fs 0.11.1", + "gix-hash 0.14.2", + "gix-path 0.10.7", + "gix-ref 0.44.1", + "gix-sec 0.10.6", "gix-testtools", "is_ci", "serial_test", @@ -1757,7 +1767,7 @@ dependencies = [ [[package]] name = "gix-features" -version = "0.38.0" +version = "0.38.2" dependencies = [ "bstr", "bytes", @@ -1766,9 +1776,9 @@ dependencies = [ "crossbeam-channel", "document-features", "flate2", - "gix-hash 0.14.1", - "gix-trace 0.1.7", - "gix-utils 0.1.10", + "gix-hash 0.14.2", + "gix-trace 0.1.9", + "gix-utils 0.1.12", "jwalk", "libc", "once_cell", @@ -1786,21 +1796,21 @@ version = "0.0.0" [[package]] name = "gix-filter" -version = "0.10.0" +version = "0.11.2" dependencies = [ "bstr", "encoding_rs", - "gix-attributes 0.22.1", + "gix-attributes 0.22.2", "gix-command", - "gix-hash 0.14.1", - "gix-object 0.41.1", + "gix-hash 0.14.2", + "gix-object 0.42.2", "gix-packetline-blocking", - "gix-path 0.10.6", - "gix-quote 0.4.11", + "gix-path 0.10.7", + "gix-quote 0.4.12", "gix-testtools", - "gix-trace 0.1.7", - "gix-utils 0.1.10", - "gix-worktree 0.31.0", + "gix-trace 0.1.9", + "gix-utils 0.1.12", + "gix-worktree 0.34.0", "serial_test", "smallvec", "thiserror", @@ -1826,21 +1836,23 @@ dependencies = [ [[package]] name = "gix-fs" -version = "0.10.0" +version = "0.11.1" dependencies = [ - "gix-features 0.38.0", - "gix-utils 0.1.10", + "crossbeam-channel", + "fastrand 2.1.0", + "gix-features 0.38.2", + "gix-utils 0.1.12", "serde", "tempfile", ] [[package]] name = "gix-fsck" -version = "0.3.0" +version = "0.4.0" dependencies = [ - "gix-hash 0.14.1", - "gix-hashtable 0.5.1", - "gix-object 0.41.1", + "gix-hash 0.14.2", + "gix-hashtable 0.5.2", + "gix-object 0.42.2", "gix-odb", "gix-testtools", ] @@ -1859,13 +1871,13 @@ dependencies = [ [[package]] name = "gix-glob" -version = "0.16.1" +version = "0.16.3" dependencies = [ "bitflags 2.4.1", "bstr", "document-features", - "gix-features 0.38.0", - "gix-path 0.10.6", + "gix-features 0.38.2", + "gix-path 0.10.7", "gix-testtools", "serde", ] @@ -1882,11 +1894,11 @@ dependencies = [ [[package]] name = "gix-hash" -version = "0.14.1" +version = "0.14.2" dependencies = [ "document-features", "faster-hex", - "gix-features 0.38.0", + "gix-features 0.38.2", "gix-testtools", "serde", "thiserror", @@ -1905,9 +1917,9 @@ dependencies = [ [[package]] name = "gix-hashtable" -version = "0.5.1" +version = "0.5.2" dependencies = [ - "gix-hash 0.14.1", + "gix-hash 0.14.2", "hashbrown 0.14.3", "parking_lot", ] @@ -1926,15 +1938,15 @@ dependencies = [ [[package]] name = "gix-ignore" -version = "0.11.1" +version = "0.11.2" dependencies = [ "bstr", "document-features", - "gix-fs 0.10.0", - "gix-glob 0.16.1", - "gix-path 0.10.6", + "gix-fs 0.11.1", + "gix-glob 0.16.3", + "gix-path 0.10.7", "gix-testtools", - "gix-trace 0.1.7", + "gix-trace 0.1.9", "serde", "unicode-bom", ] @@ -1964,21 +1976,22 @@ dependencies = [ [[package]] name = "gix-index" -version = "0.30.0" +version = "0.33.0" dependencies = [ "bitflags 2.4.1", "bstr", "document-features", "filetime", "fnv", - "gix-bitmap 0.2.10", - "gix-features 0.38.0", - "gix-fs 0.10.0", - "gix-hash 0.14.1", - "gix-lock 13.1.0", - "gix-object 0.41.1", - "gix-traverse 0.37.0", - "gix-utils 0.1.10", + "gix-bitmap 0.2.11", + "gix-features 0.38.2", + "gix-fs 0.11.1", + "gix-hash 0.14.2", + "gix-lock 14.0.0", + "gix-object 0.42.2", + "gix-traverse 0.39.1", + "gix-utils 0.1.12", + "gix-validate 0.8.5", "hashbrown 0.14.3", "itoa", "libc", @@ -1995,10 +2008,11 @@ version = "0.0.0" dependencies = [ "bstr", "filetime", - "gix", - "gix-features 0.38.0", - "gix-hash 0.14.1", - "gix-index 0.30.0", + "gix-features 0.38.2", + "gix-hash 0.14.2", + "gix-index 0.33.0", + "gix-object 0.42.2", + "gix-odb", "gix-testtools", ] @@ -2030,17 +2044,17 @@ dependencies = [ [[package]] name = "gix-lock" -version = "13.1.0" +version = "14.0.0" dependencies = [ - "gix-tempfile 13.1.0", - "gix-utils 0.1.10", + "gix-tempfile 14.0.0", + "gix-utils 0.1.12", "tempfile", "thiserror", ] [[package]] name = "gix-macros" -version = "0.1.3" +version = "0.1.5" dependencies = [ "proc-macro2", "quote", @@ -2050,12 +2064,12 @@ dependencies = [ [[package]] name = "gix-mailmap" -version = "0.22.0" +version = "0.23.1" dependencies = [ "bstr", "document-features", - "gix-actor 0.30.1", - "gix-date 0.8.4", + "gix-actor 0.31.2", + "gix-date 0.8.6", "gix-testtools", "serde", "thiserror", @@ -2063,16 +2077,16 @@ dependencies = [ [[package]] name = "gix-negotiate" -version = "0.12.0" +version = "0.13.1" dependencies = [ "bitflags 2.4.1", - "gix-commitgraph 0.24.1", - "gix-date 0.8.4", - "gix-hash 0.14.1", - "gix-object 0.41.1", + "gix-commitgraph 0.24.2", + "gix-date 0.8.6", + "gix-hash 0.14.2", + "gix-object 0.42.2", "gix-odb", - "gix-ref 0.42.0", - "gix-revwalk 0.12.0", + "gix-ref 0.44.1", + "gix-revwalk 0.13.1", "gix-testtools", "smallvec", "thiserror", @@ -2122,18 +2136,18 @@ dependencies = [ [[package]] name = "gix-object" -version = "0.41.1" +version = "0.42.2" dependencies = [ "bstr", "criterion", "document-features", - "gix-actor 0.30.1", - "gix-date 0.8.4", - "gix-features 0.38.0", - "gix-hash 0.14.1", + "gix-actor 0.31.2", + "gix-date 0.8.6", + "gix-features 0.38.2", + "gix-hash 0.14.2", "gix-testtools", - "gix-utils 0.1.10", - "gix-validate 0.8.3", + "gix-utils 0.1.12", + "gix-validate 0.8.5", "itoa", "pretty_assertions", "serde", @@ -2144,18 +2158,18 @@ dependencies = [ [[package]] name = "gix-odb" -version = "0.58.0" +version = "0.61.0" dependencies = [ "arc-swap", "document-features", - "gix-date 0.8.4", - "gix-features 0.38.0", - "gix-fs 0.10.0", - "gix-hash 0.14.1", - "gix-object 0.41.1", + "gix-date 0.8.6", + "gix-features 0.38.2", + "gix-fs 0.11.1", + "gix-hash 0.14.2", + "gix-object 0.42.2", "gix-pack", - "gix-path 0.10.6", - "gix-quote 0.4.11", + "gix-path 0.10.7", + "gix-quote 0.4.12", "parking_lot", "serde", "tempfile", @@ -2166,12 +2180,13 @@ dependencies = [ name = "gix-odb-tests" version = "0.0.0" dependencies = [ + "crossbeam-channel", "filetime", - "gix-actor 0.30.1", - "gix-date 0.8.4", - "gix-features 0.38.0", - "gix-hash 0.14.1", - "gix-object 0.41.1", + "gix-actor 0.31.2", + "gix-date 0.8.6", + "gix-features 0.38.2", + "gix-hash 0.14.2", + "gix-object 0.42.2", "gix-odb", "gix-pack", "gix-testtools", @@ -2181,20 +2196,20 @@ dependencies = [ [[package]] name = "gix-pack" -version = "0.48.0" +version = "0.51.0" dependencies = [ "clru", "document-features", - "gix-chunk 0.4.7", + "gix-chunk 0.4.8", "gix-diff", - "gix-features 0.38.0", - "gix-hash 0.14.1", - "gix-hashtable 0.5.1", - "gix-object 0.41.1", - "gix-path 0.10.6", - "gix-tempfile 13.1.0", + "gix-features 0.38.2", + "gix-hash 0.14.2", + "gix-hashtable 0.5.2", + "gix-object 0.42.2", + "gix-path 0.10.7", + "gix-tempfile 14.0.0", "gix-testtools", - "gix-traverse 0.37.0", + "gix-traverse 0.39.1", "memmap2 0.9.3", "parking_lot", "serde", @@ -2208,20 +2223,20 @@ name = "gix-pack-tests" version = "0.0.0" dependencies = [ "bstr", - "gix-features 0.38.0", - "gix-hash 0.14.1", - "gix-object 0.41.1", + "gix-features 0.38.2", + "gix-hash 0.14.2", + "gix-object 0.42.2", "gix-odb", "gix-pack", "gix-testtools", - "gix-traverse 0.37.0", + "gix-traverse 0.39.1", "maplit", "memmap2 0.9.3", ] [[package]] name = "gix-packetline" -version = "0.17.3" +version = "0.17.5" dependencies = [ "async-std", "bstr", @@ -2229,10 +2244,10 @@ dependencies = [ "faster-hex", "futures-io", "futures-lite 2.1.0", - "gix-hash 0.14.1", + "gix-hash 0.14.2", "gix-odb", "gix-pack", - "gix-trace 0.1.7", + "gix-trace 0.1.9", "maybe-async", "pin-project-lite", "serde", @@ -2241,12 +2256,12 @@ dependencies = [ [[package]] name = "gix-packetline-blocking" -version = "0.17.3" +version = "0.17.4" dependencies = [ "bstr", "document-features", "faster-hex", - "gix-trace 0.1.7", + "gix-trace 0.1.9", "serde", "thiserror", ] @@ -2266,10 +2281,10 @@ dependencies = [ [[package]] name = "gix-path" -version = "0.10.6" +version = "0.10.7" dependencies = [ "bstr", - "gix-trace 0.1.7", + "gix-trace 0.1.9", "home", "once_cell", "tempfile", @@ -2278,14 +2293,14 @@ dependencies = [ [[package]] name = "gix-pathspec" -version = "0.7.0" +version = "0.7.5" dependencies = [ "bitflags 2.4.1", "bstr", - "gix-attributes 0.22.1", + "gix-attributes 0.22.2", "gix-config-value", - "gix-glob 0.16.1", - "gix-path 0.10.6", + "gix-glob 0.16.3", + "gix-path 0.10.7", "gix-testtools", "once_cell", "serial_test", @@ -2294,7 +2309,7 @@ dependencies = [ [[package]] name = "gix-prompt" -version = "0.8.3" +version = "0.8.5" dependencies = [ "expectrl", "gix-command", @@ -2308,7 +2323,7 @@ dependencies = [ [[package]] name = "gix-protocol" -version = "0.44.1" +version = "0.45.1" dependencies = [ "async-std", "async-trait", @@ -2317,13 +2332,13 @@ dependencies = [ "futures-io", "futures-lite 2.1.0", "gix-credentials", - "gix-date 0.8.4", - "gix-features 0.38.0", - "gix-hash 0.14.1", + "gix-date 0.8.6", + "gix-features 0.38.2", + "gix-hash 0.14.2", "gix-packetline", "gix-testtools", "gix-transport", - "gix-utils 0.1.10", + "gix-utils 0.1.12", "maybe-async", "serde", "thiserror", @@ -2343,10 +2358,10 @@ dependencies = [ [[package]] name = "gix-quote" -version = "0.4.11" +version = "0.4.12" dependencies = [ "bstr", - "gix-utils 0.1.10", + "gix-utils 0.1.12", "thiserror", ] @@ -2377,21 +2392,21 @@ dependencies = [ [[package]] name = "gix-ref" -version = "0.42.0" +version = "0.44.1" dependencies = [ "document-features", - "gix-actor 0.30.1", - "gix-date 0.8.4", - "gix-features 0.38.0", - "gix-fs 0.10.0", - "gix-hash 0.14.1", - "gix-lock 13.1.0", - "gix-object 0.41.1", - "gix-path 0.10.6", - "gix-tempfile 13.1.0", + "gix-actor 0.31.2", + "gix-date 0.8.6", + "gix-features 0.38.2", + "gix-fs 0.11.1", + "gix-hash 0.14.2", + "gix-lock 14.0.0", + "gix-object 0.42.2", + "gix-path 0.10.7", + "gix-tempfile 14.0.0", "gix-testtools", - "gix-utils 0.1.10", - "gix-validate 0.8.3", + "gix-utils 0.1.12", + "gix-validate 0.8.5", "memmap2 0.9.3", "serde", "thiserror", @@ -2402,48 +2417,48 @@ dependencies = [ name = "gix-ref-tests" version = "0.0.0" dependencies = [ - "gix-actor 0.30.1", - "gix-date 0.8.4", - "gix-discover 0.30.0", - "gix-features 0.38.0", - "gix-fs 0.10.0", - "gix-hash 0.14.1", - "gix-lock 13.1.0", - "gix-object 0.41.1", + "gix-actor 0.31.2", + "gix-date 0.8.6", + "gix-discover 0.32.0", + "gix-features 0.38.2", + "gix-fs 0.11.1", + "gix-hash 0.14.2", + "gix-lock 14.0.0", + "gix-object 0.42.2", "gix-odb", - "gix-ref 0.42.0", + "gix-ref 0.44.1", "gix-testtools", - "gix-validate 0.8.3", + "gix-validate 0.8.5", ] [[package]] name = "gix-refspec" -version = "0.22.1" +version = "0.23.0" dependencies = [ "bstr", - "gix-hash 0.14.1", + "gix-hash 0.14.2", "gix-revision", "gix-testtools", - "gix-validate 0.8.3", + "gix-validate 0.8.5", "smallvec", "thiserror", ] [[package]] name = "gix-revision" -version = "0.26.1" +version = "0.27.1" dependencies = [ "bstr", "document-features", - "gix-commitgraph 0.24.1", - "gix-date 0.8.4", - "gix-hash 0.14.1", - "gix-hashtable 0.5.1", - "gix-object 0.41.1", + "gix-commitgraph 0.24.2", + "gix-date 0.8.6", + "gix-hash 0.14.2", + "gix-hashtable 0.5.2", + "gix-object 0.42.2", "gix-odb", - "gix-revwalk 0.12.0", + "gix-revwalk 0.13.1", "gix-testtools", - "gix-trace 0.1.7", + "gix-trace 0.1.9", "serde", "thiserror", ] @@ -2465,13 +2480,13 @@ dependencies = [ [[package]] name = "gix-revwalk" -version = "0.12.0" +version = "0.13.1" dependencies = [ - "gix-commitgraph 0.24.1", - "gix-date 0.8.4", - "gix-hash 0.14.1", - "gix-hashtable 0.5.1", - "gix-object 0.41.1", + "gix-commitgraph 0.24.2", + "gix-date 0.8.6", + "gix-hash 0.14.2", + "gix-hashtable 0.5.2", + "gix-object 0.42.2", "smallvec", "thiserror", ] @@ -2490,11 +2505,11 @@ dependencies = [ [[package]] name = "gix-sec" -version = "0.10.5" +version = "0.10.6" dependencies = [ "bitflags 2.4.1", "document-features", - "gix-path 0.10.6", + "gix-path 0.10.7", "libc", "serde", "tempfile", @@ -2507,19 +2522,22 @@ version = "0.0.0" [[package]] name = "gix-status" -version = "0.6.0" +version = "0.10.0" dependencies = [ "bstr", + "document-features", "filetime", - "gix-features 0.38.0", + "gix-diff", + "gix-dir", + "gix-features 0.38.2", "gix-filter", - "gix-fs 0.10.0", - "gix-hash 0.14.1", - "gix-index 0.30.0", - "gix-object 0.41.1", - "gix-path 0.10.6", + "gix-fs 0.11.1", + "gix-hash 0.14.2", + "gix-index 0.33.0", + "gix-object 0.42.2", + "gix-path 0.10.7", "gix-pathspec", - "gix-worktree 0.31.0", + "gix-worktree 0.34.0", "thiserror", ] @@ -2529,25 +2547,31 @@ version = "0.0.0" dependencies = [ "bstr", "filetime", - "gix-features 0.38.0", - "gix-fs 0.10.0", - "gix-hash 0.14.1", - "gix-index 0.30.0", - "gix-object 0.41.1", + "gix-diff", + "gix-dir", + "gix-features 0.38.2", + "gix-filter", + "gix-fs 0.11.1", + "gix-hash 0.14.2", + "gix-index 0.33.0", + "gix-object 0.42.2", + "gix-odb", + "gix-path 0.10.7", "gix-pathspec", "gix-status", "gix-testtools", - "gix-worktree 0.31.0", + "gix-worktree 0.34.0", + "pretty_assertions", ] [[package]] name = "gix-submodule" -version = "0.9.0" +version = "0.11.0" dependencies = [ "bstr", "gix-config", - "gix-features 0.38.0", - "gix-path 0.10.6", + "gix-features 0.38.2", + "gix-path 0.10.7", "gix-pathspec", "gix-refspec", "gix-testtools", @@ -2583,11 +2607,11 @@ dependencies = [ [[package]] name = "gix-tempfile" -version = "13.1.0" +version = "14.0.0" dependencies = [ "dashmap", "document-features", - "gix-fs 0.10.0", + "gix-fs 0.11.1", "libc", "once_cell", "parking_lot", @@ -2602,13 +2626,13 @@ version = "0.14.0" dependencies = [ "bstr", "crc", - "fastrand 2.0.1", + "fastrand 2.1.0", "fs_extra", "gix-discover 0.26.0", - "gix-fs 0.10.0", + "gix-fs 0.11.1", "gix-ignore 0.8.0", "gix-lock 10.0.0", - "gix-tempfile 13.1.0", + "gix-tempfile 14.0.0", "gix-worktree 0.26.0", "io-close", "is_ci", @@ -2632,7 +2656,7 @@ checksum = "b686a35799b53a9825575ca3f06481d0a053a409c4d97ffcf5ddd67a8760b497" [[package]] name = "gix-trace" -version = "0.1.7" +version = "0.1.9" dependencies = [ "document-features", "tracing-core", @@ -2640,11 +2664,11 @@ dependencies = [ [[package]] name = "gix-transport" -version = "0.41.1" +version = "0.42.1" dependencies = [ "async-std", "async-trait", - "base64 0.21.5", + "base64 0.22.1", "blocking", "bstr", "curl", @@ -2653,12 +2677,12 @@ dependencies = [ "futures-lite 2.1.0", "gix-command", "gix-credentials", - "gix-features 0.38.0", - "gix-hash 0.14.1", + "gix-features 0.38.2", + "gix-hash 0.14.2", "gix-pack", "gix-packetline", - "gix-quote 0.4.11", - "gix-sec 0.10.5", + "gix-quote 0.4.12", + "gix-sec 0.10.6", "gix-url", "maybe-async", "pin-project-lite", @@ -2685,14 +2709,15 @@ dependencies = [ [[package]] name = "gix-traverse" -version = "0.37.0" +version = "0.39.1" dependencies = [ - "gix-commitgraph 0.24.1", - "gix-date 0.8.4", - "gix-hash 0.14.1", - "gix-hashtable 0.5.1", - "gix-object 0.41.1", - "gix-revwalk 0.12.0", + "bitflags 2.4.1", + "gix-commitgraph 0.24.2", + "gix-date 0.8.6", + "gix-hash 0.14.2", + "gix-hashtable 0.5.2", + "gix-object 0.42.2", + "gix-revwalk 0.13.1", "smallvec", "thiserror", ] @@ -2701,12 +2726,12 @@ dependencies = [ name = "gix-traverse-tests" version = "0.0.0" dependencies = [ - "gix-commitgraph 0.24.1", - "gix-hash 0.14.1", - "gix-object 0.41.1", + "gix-commitgraph 0.24.2", + "gix-hash 0.14.2", + "gix-object 0.42.2", "gix-odb", "gix-testtools", - "gix-traverse 0.37.0", + "gix-traverse 0.39.1", ] [[package]] @@ -2715,13 +2740,13 @@ version = "0.0.0" [[package]] name = "gix-url" -version = "0.27.1" +version = "0.27.3" dependencies = [ "assert_matches", "bstr", "document-features", - "gix-features 0.38.0", - "gix-path 0.10.6", + "gix-features 0.38.2", + "gix-path 0.10.7", "gix-testtools", "home", "serde", @@ -2735,15 +2760,15 @@ version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9f82c41937f00e15a1f6cb0b55307f0ca1f77f4407ff2bf440be35aa688c6a3e" dependencies = [ - "fastrand 2.0.1", + "fastrand 2.1.0", ] [[package]] name = "gix-utils" -version = "0.1.10" +version = "0.1.12" dependencies = [ "bstr", - "fastrand 2.0.1", + "fastrand 2.1.0", "unicode-normalization", ] @@ -2759,7 +2784,7 @@ dependencies = [ [[package]] name = "gix-validate" -version = "0.8.3" +version = "0.8.5" dependencies = [ "bstr", "gix-testtools", @@ -2786,36 +2811,37 @@ dependencies = [ [[package]] name = "gix-worktree" -version = "0.31.0" +version = "0.34.0" dependencies = [ "bstr", "document-features", - "gix-attributes 0.22.1", - "gix-features 0.38.0", - "gix-fs 0.10.0", - "gix-glob 0.16.1", - "gix-hash 0.14.1", - "gix-ignore 0.11.1", - "gix-index 0.30.0", - "gix-object 0.41.1", - "gix-path 0.10.6", + "gix-attributes 0.22.2", + "gix-features 0.38.2", + "gix-fs 0.11.1", + "gix-glob 0.16.3", + "gix-hash 0.14.2", + "gix-ignore 0.11.2", + "gix-index 0.33.0", + "gix-object 0.42.2", + "gix-path 0.10.7", + "gix-validate 0.8.5", "serde", ] [[package]] name = "gix-worktree-state" -version = "0.8.0" +version = "0.11.0" dependencies = [ "bstr", - "gix-features 0.38.0", + "gix-features 0.38.2", "gix-filter", - "gix-fs 0.10.0", - "gix-glob 0.16.1", - "gix-hash 0.14.1", - "gix-index 0.30.0", - "gix-object 0.41.1", - "gix-path 0.10.6", - "gix-worktree 0.31.0", + "gix-fs 0.11.1", + "gix-glob 0.16.3", + "gix-hash 0.14.2", + "gix-index 0.33.0", + "gix-object 0.42.2", + "gix-path 0.10.7", + "gix-worktree 0.34.0", "io-close", "thiserror", ] @@ -2824,13 +2850,13 @@ dependencies = [ name = "gix-worktree-state-tests" version = "0.0.0" dependencies = [ - "gix-discover 0.30.0", - "gix-features 0.38.0", + "gix-discover 0.32.0", + "gix-features 0.38.2", "gix-filter", - "gix-fs 0.10.0", - "gix-hash 0.14.1", - "gix-index 0.30.0", - "gix-object 0.41.1", + "gix-fs 0.11.1", + "gix-hash 0.14.2", + "gix-index 0.33.0", + "gix-object 0.42.2", "gix-odb", "gix-testtools", "gix-worktree-state", @@ -2841,19 +2867,19 @@ dependencies = [ [[package]] name = "gix-worktree-stream" -version = "0.10.0" +version = "0.13.0" dependencies = [ - "gix-attributes 0.22.1", - "gix-features 0.38.0", + "gix-attributes 0.22.2", + "gix-features 0.38.2", "gix-filter", - "gix-fs 0.10.0", - "gix-hash 0.14.1", - "gix-object 0.41.1", + "gix-fs 0.11.1", + "gix-hash 0.14.2", + "gix-object 0.42.2", "gix-odb", - "gix-path 0.10.6", + "gix-path 0.10.7", "gix-testtools", - "gix-traverse 0.37.0", - "gix-worktree 0.31.0", + "gix-traverse 0.39.1", + "gix-worktree 0.34.0", "parking_lot", "thiserror", ] @@ -2863,19 +2889,19 @@ name = "gix-worktree-tests" version = "0.0.0" dependencies = [ "bstr", - "gix-attributes 0.22.1", - "gix-discover 0.30.0", - "gix-features 0.38.0", - "gix-fs 0.10.0", - "gix-glob 0.16.1", - "gix-hash 0.14.1", - "gix-ignore 0.11.1", - "gix-index 0.30.0", - "gix-object 0.41.1", + "gix-attributes 0.22.2", + "gix-discover 0.32.0", + "gix-features 0.38.2", + "gix-fs 0.11.1", + "gix-glob 0.16.3", + "gix-hash 0.14.2", + "gix-ignore 0.11.2", + "gix-index 0.33.0", + "gix-object 0.42.2", "gix-odb", - "gix-path 0.10.6", + "gix-path 0.10.7", "gix-testtools", - "gix-worktree 0.31.0", + "gix-worktree 0.34.0", "symlink", ] @@ -2899,9 +2925,9 @@ dependencies = [ [[package]] name = "h2" -version = "0.3.24" +version = "0.4.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bb2c4422095b67ee78da96fbb51a4cc413b3b25883c7717ff7ca1ab31022c9c9" +checksum = "816ec7294445779408f36fe57bc5b7fc1cf59664059096c65f905c1c61f58069" dependencies = [ "bytes", "fnv", @@ -2940,9 +2966,9 @@ dependencies = [ [[package]] name = "hashlink" -version = "0.8.4" +version = "0.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e8094feaf31ff591f651a2664fb9cfd92bba7a60ce3197265e9482ebe753c8f7" +checksum = "6ba4ff7128dee98c7dc9794b6a411377e1404dba1c97deb8d1a55297bd25d8af" dependencies = [ "hashbrown 0.14.3", ] @@ -2969,35 +2995,36 @@ dependencies = [ ] [[package]] -name = "hostname" -version = "0.3.1" +name = "http" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c731c3e10504cc8ed35cfe2f1db4c9274c3d35fa486e3b31df46f068ef3e867" +checksum = "21b9ddb458710bc376481b842f5da65cdf31522de232c1ca8146abce2a358258" dependencies = [ - "libc", - "match_cfg", - "winapi", + "bytes", + "fnv", + "itoa", ] [[package]] -name = "http" -version = "0.2.11" +name = "http-body" +version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8947b1a6fad4393052c7ba1f4cd97bed3e953a95c79c92ad9b051a04611d9fbb" +checksum = "1cac85db508abc24a2e48553ba12a996e87244a0395ce011e62b37158745d643" dependencies = [ "bytes", - "fnv", - "itoa", + "http", ] [[package]] -name = "http-body" -version = "0.4.6" +name = "http-body-util" +version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ceab25649e9960c0311ea418d17bee82c0dcec1bd053b5f9a66e265a693bed2" +checksum = "0475f8b2ac86659c21b64320d5d653f9efe42acd2a4e560073ec61a155a34f1d" dependencies = [ "bytes", + "futures-core", "http", + "http-body", "pin-project-lite", ] @@ -3007,12 +3034,6 @@ version = "1.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d897f394bad6a705d5f4104762e116a75639e470d80901eed05a860a95cb1904" -[[package]] -name = "httpdate" -version = "1.0.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9" - [[package]] name = "human_format" version = "1.0.3" @@ -3027,63 +3048,75 @@ checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" [[package]] name = "hyper" -version = "0.14.28" +version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bf96e135eb83a2a8ddf766e426a841d8ddd7449d5f00d34ea02b41d2f19eef80" +checksum = "186548d73ac615b32a73aafe38fb4f56c0d340e110e5a200bcadbaf2e199263a" dependencies = [ "bytes", "futures-channel", - "futures-core", "futures-util", "h2", "http", "http-body", "httparse", - "httpdate", "itoa", "pin-project-lite", - "socket2 0.5.5", + "smallvec", "tokio", - "tower-service", - "tracing", "want", ] [[package]] name = "hyper-rustls" -version = "0.24.2" +version = "0.26.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec3efd23720e2049821a693cbc7e65ea87c72f1c58ff2f9522ff332b1491e590" +checksum = "a0bea761b46ae2b24eb4aef630d8d1c398157b6fc29e6350ecf090a0b70c952c" dependencies = [ "futures-util", "http", "hyper", - "rustls 0.21.10", + "hyper-util", + "rustls 0.22.4", + "rustls-pki-types", "tokio", "tokio-rustls", + "tower-service", ] [[package]] name = "hyper-tls" -version = "0.5.0" +version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d6183ddfa99b85da61a140bea0efc93fdf56ceaa041b37d553518030827f9905" +checksum = "70206fc6890eaca9fde8a0bf71caa2ddfc9fe045ac9e5c70df101a7dbde866e0" dependencies = [ "bytes", + "http-body-util", "hyper", + "hyper-util", "native-tls", "tokio", "tokio-native-tls", + "tower-service", ] [[package]] -name = "idna" -version = "0.4.0" +name = "hyper-util" +version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7d20d6b07bfbc108882d88ed8e37d39636dcc260e15e30c45e6ba089610b917c" +checksum = "ca38ef113da30126bbff9cd1705f9273e15d45498615d138b0c20279ac7a76aa" dependencies = [ - "unicode-bidi", - "unicode-normalization", + "bytes", + "futures-channel", + "futures-util", + "http", + "http-body", + "hyper", + "pin-project-lite", + "socket2 0.5.5", + "tokio", + "tower", + "tower-service", + "tracing", ] [[package]] @@ -3152,18 +3185,6 @@ dependencies = [ "windows-sys 0.48.0", ] -[[package]] -name = "ipconfig" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b58db92f96b720de98181bbbe63c831e87005ab460c1bf306eb2622b4707997f" -dependencies = [ - "socket2 0.5.5", - "widestring", - "windows-sys 0.48.0", - "winreg", -] - [[package]] name = "ipnet" version = "2.9.0" @@ -3206,6 +3227,12 @@ version = "1.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "616cde7c720bb2bb5824a224687d8f77bfd38922027f01d825cd7453be5099fb" +[[package]] +name = "is_terminal_polyfill" +version = "1.70.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8478577c03552c21db0e2724ffb8986a5ce7af88107e6be5d2ee6e158c12800" + [[package]] name = "itertools" version = "0.10.5" @@ -3288,9 +3315,9 @@ checksum = "9c198f91728a82281a64e1f4f9eeb25d82cb32a5de251c6bd1b5154d63a8e7bd" [[package]] name = "libsqlite3-sys" -version = "0.27.0" +version = "0.28.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf4e226dcd58b4be396f7bd3c20da8fdee2911400705297ba7d2d7cc2c30f716" +checksum = "0c10584274047cb335c23d3e61bcef8e323adae7c5c8c760540f73610177fc3f" dependencies = [ "cc", "pkg-config", @@ -3320,12 +3347,6 @@ dependencies = [ "vcpkg", ] -[[package]] -name = "linked-hash-map" -version = "0.5.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0717cef1bc8b636c6e1c1bbdefc09e6322da8a9321966e8928ef80d20f7f770f" - [[package]] name = "linux-raw-sys" version = "0.3.8" @@ -3354,11 +3375,17 @@ dependencies = [ "scopeguard", ] +[[package]] +name = "lockfree-object-pool" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9374ef4228402d4b7e403e5838cb880d9ee663314b0a900d5a6aabf0c213552e" + [[package]] name = "log" -version = "0.4.20" +version = "0.4.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f" +checksum = "90ed8c1e510134f979dbc4f070f87d4313098b704861a105fe34231c70a3901c" dependencies = [ "value-bag", ] @@ -3372,15 +3399,6 @@ dependencies = [ "hashbrown 0.14.3", ] -[[package]] -name = "lru-cache" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "31e24f1ad8321ca0e8a1e0ac13f23cb668e6f5466c2c57319f6a5cf1cc8e3b1c" -dependencies = [ - "linked-hash-map", -] - [[package]] name = "lzma-sys" version = "0.1.20" @@ -3398,12 +3416,6 @@ version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3e2e65a1a2e43cfcb47a895c4c8b10d1f4a61097f9f254f183aee60cad9c651d" -[[package]] -name = "match_cfg" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ffbee8634e0d45d258acb448e7eaab3fce7a0a467395d4d9f228e3c1f01fb2e4" - [[package]] name = "maybe-async" version = "0.2.7" @@ -3417,9 +3429,9 @@ dependencies = [ [[package]] name = "memchr" -version = "2.7.1" +version = "2.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "523dc4f511e55ab87b694dc30d0f820d60906ef06413f93d4d7a1385599cc149" +checksum = "6c8640c5d730cb13ebd907d8d04b52f55ac9a2eec55b440c8892f40d56c76c1d" [[package]] name = "memmap2" @@ -3465,9 +3477,9 @@ dependencies = [ [[package]] name = "mio" -version = "0.8.10" +version = "0.8.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f3d0b296e374a4e6f3c7b0a1f5a51d748a0d34c85e7dc48fc3fa9a87657fe09" +checksum = "a4a650543ca06a924e8b371db273b2756685faae30f8487da1b56505a8f78b0c" dependencies = [ "libc", "log", @@ -3525,6 +3537,12 @@ dependencies = [ "winapi", ] +[[package]] +name = "num-conv" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9" + [[package]] name = "num-traits" version = "0.2.17" @@ -3703,6 +3721,26 @@ version = "2.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" +[[package]] +name = "pin-project" +version = "1.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6bf43b791c5b9e34c3d182969b4abb522f9343702850a2e57f460d00d09b4b3" +dependencies = [ + "pin-project-internal", +] + +[[package]] +name = "pin-project-internal" +version = "1.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2f38a4412a78282e09a2cf38d195ea5420d15ba0602cb375210efbc877243965" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.47", +] + [[package]] name = "pin-project-lite" version = "0.2.13" @@ -3722,7 +3760,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "668d31b1c4eba19242f2088b2bf3316b82ca31082a8335764db4e083db7485d4" dependencies = [ "atomic-waker", - "fastrand 2.0.1", + "fastrand 2.1.0", "futures-io", ] @@ -3796,12 +3834,6 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391" -[[package]] -name = "ppv-lite86" -version = "0.2.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" - [[package]] name = "pretty_assertions" version = "1.4.0" @@ -3870,12 +3902,6 @@ dependencies = [ "nix", ] -[[package]] -name = "quick-error" -version = "1.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1d01941d82fa2ab50be1e79e6714289dd7cde78eba4c074bc5a4374f650dfe0" - [[package]] name = "quote" version = "1.0.35" @@ -3885,36 +3911,6 @@ dependencies = [ "proc-macro2", ] -[[package]] -name = "rand" -version = "0.8.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" -dependencies = [ - "libc", - "rand_chacha", - "rand_core", -] - -[[package]] -name = "rand_chacha" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" -dependencies = [ - "ppv-lite86", - "rand_core", -] - -[[package]] -name = "rand_core" -version = "0.6.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" -dependencies = [ - "getrandom", -] - [[package]] name = "ratatui" version = "0.25.0" @@ -3994,21 +3990,24 @@ checksum = "c08c74e62047bb2de4ff487b251e4a92e24f48745648451635cec7d591162d9f" [[package]] name = "reqwest" -version = "0.11.23" +version = "0.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "37b1ae8d9ac08420c66222fb9096fc5de435c3c48542bc5336c51892cffafb41" +checksum = "58b48d98d932f4ee75e541614d32a7f44c889b72bd9c2e04d95edd135989df88" dependencies = [ "base64 0.21.5", "bytes", "encoding_rs", + "futures-channel", "futures-core", "futures-util", "h2", "http", "http-body", + "http-body-util", "hyper", "hyper-rustls", "hyper-tls", + "hyper-util", "ipnet", "js-sys", "log", @@ -4017,17 +4016,18 @@ dependencies = [ "once_cell", "percent-encoding", "pin-project-lite", - "rustls 0.21.10", + "rustls 0.22.4", "rustls-pemfile 1.0.4", + "rustls-pki-types", "serde", "serde_json", "serde_urlencoded", + "sync_wrapper", "system-configuration", "tokio", "tokio-native-tls", "tokio-rustls", "tower-service", - "trust-dns-resolver", "url", "wasm-bindgen", "wasm-bindgen-futures", @@ -4036,16 +4036,6 @@ dependencies = [ "winreg", ] -[[package]] -name = "resolv-conf" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "52e44394d2086d010551b14b53b1f24e31647570cd1deb0379e2c21b329aba00" -dependencies = [ - "hostname", - "quick-error", -] - [[package]] name = "ring" version = "0.16.20" @@ -4077,9 +4067,9 @@ dependencies = [ [[package]] name = "rusqlite" -version = "0.30.0" +version = "0.31.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a78046161564f5e7cd9008aff3b2990b3850dc8e0349119b98e8f251e099f24d" +checksum = "b838eba278d213a8beaf485bd313fd580ca4505a00d5871caeb1457c55322cae" dependencies = [ "bitflags 2.4.1", "fallible-iterator", @@ -4136,14 +4126,16 @@ dependencies = [ [[package]] name = "rustls" -version = "0.21.10" +version = "0.22.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f9d5a6813c0759e4609cd494e8e725babae6a2ca7b62a5536a13daaec6fcb7ba" +checksum = "bf4ef73721ac7bcd79b2b315da7779d8fc09718c6b3d2d1b2d94850eb8c18432" dependencies = [ "log", "ring 0.17.7", + "rustls-pki-types", "rustls-webpki", - "sct", + "subtle", + "zeroize", ] [[package]] @@ -4179,13 +4171,20 @@ dependencies = [ "base64 0.21.5", ] +[[package]] +name = "rustls-pki-types" +version = "1.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5ede67b28608b4c60685c7d54122d4400d90f62b40caee7700e700380a390fa8" + [[package]] name = "rustls-webpki" -version = "0.101.7" +version = "0.102.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b6275d1ee7a1cd780b64aca7726599a1dbc893b1e64144529e55c3c2f745765" +checksum = "faaa0a62740bedb9b2ef5afa303da42764c012f743917351dc9a237ea1663610" dependencies = [ "ring 0.17.7", + "rustls-pki-types", "untrusted 0.9.0", ] @@ -4210,6 +4209,15 @@ dependencies = [ "winapi-util", ] +[[package]] +name = "scc" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76ad2bbb0ae5100a07b7a6f2ed7ab5fd0045551a4c507989b7a620046ea3efdc" +dependencies = [ + "sdd", +] + [[package]] name = "schannel" version = "0.1.23" @@ -4235,6 +4243,12 @@ dependencies = [ "untrusted 0.9.0", ] +[[package]] +name = "sdd" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b84345e4c9bd703274a082fb80caaa99b7612be48dfaa1dd9266577ec412309d" + [[package]] name = "security-framework" version = "2.9.2" @@ -4303,23 +4317,23 @@ dependencies = [ [[package]] name = "serial_test" -version = "2.0.0" +version = "3.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0e56dd856803e253c8f298af3f4d7eb0ae5e23a737252cd90bb4f3b435033b2d" +checksum = "4b4b487fe2acf240a021cf57c6b2b4903b1e78ca0ecd862a71b71d2a51fed77d" dependencies = [ - "dashmap", "futures", - "lazy_static", "log", + "once_cell", "parking_lot", + "scc", "serial_test_derive", ] [[package]] name = "serial_test_derive" -version = "2.0.0" +version = "3.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "91d129178576168c589c9ec973feedf7d3126c01ac2bf08795109aa35b69fb8f" +checksum = "82fe9db325bcef1fbcde82e078a5cc4efdf787e96b3b9cf45b50b529f2083d67" dependencies = [ "proc-macro2", "quote", @@ -4398,6 +4412,12 @@ dependencies = [ "libc", ] +[[package]] +name = "simd-adler32" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d66dc143e6b11c1eddc06d5c423cfc97062865baf299914ab64caa38182078fe" + [[package]] name = "slab" version = "0.4.9" @@ -4409,9 +4429,9 @@ dependencies = [ [[package]] name = "smallvec" -version = "1.11.2" +version = "1.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4dccd0940a2dcdf68d092b8cbab7dc0ad8fa938bf95787e1b916b0e3d0e8e970" +checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67" dependencies = [ "serde", ] @@ -4492,6 +4512,12 @@ dependencies = [ "syn 2.0.47", ] +[[package]] +name = "subtle" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "81cdd64d312baedb58e21336b31bc043b77e01cc99033ce76ef539f78e965ebc" + [[package]] name = "symlink" version = "0.1.0" @@ -4520,6 +4546,12 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "sync_wrapper" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2047c6ded9c721764247e62cd3b03c09ffc529b2ba5b10ec482ae507a4a70160" + [[package]] name = "sysinfo" version = "0.30.2" @@ -4573,7 +4605,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a365e8cd18e44762ef95d87f284f4b5cd04107fec2ff3052bd6a3e6069669e67" dependencies = [ "cfg-if", - "fastrand 2.0.1", + "fastrand 2.1.0", "rustix 0.38.31", "windows-sys 0.52.0", ] @@ -4629,13 +4661,14 @@ dependencies = [ [[package]] name = "time" -version = "0.3.31" +version = "0.3.36" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f657ba42c3f86e7680e53c8cd3af8abbe56b5491790b46e22e19c0d57463583e" +checksum = "5dfd88e563464686c916c7e46e623e520ddc6d79fa6641390f2e3fa86e83e885" dependencies = [ "deranged", "itoa", "libc", + "num-conv", "num_threads", "powerfmt", "serde", @@ -4651,10 +4684,11 @@ checksum = "ef927ca75afb808a4d64dd374f00a2adf8d0fcff8e7b184af886c3c87ec4a3f3" [[package]] name = "time-macros" -version = "0.2.16" +version = "0.2.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26197e33420244aeb70c3e8c78376ca46571bc4e701e4791c2cd9f57dcb3a43f" +checksum = "3f252a68540fde3a3877aeea552b832b40ab9a69e318efd078774a01ddee1ccf" dependencies = [ + "num-conv", "time-core", ] @@ -4711,11 +4745,12 @@ dependencies = [ [[package]] name = "tokio-rustls" -version = "0.24.1" +version = "0.25.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c28327cf380ac148141087fbfb9de9d7bd4e84ab5d2c28fbc911d753de8a7081" +checksum = "775e0c0f0adb3a2f22a00c4745d728b479985fc15ee7ca6a2608388c5569860f" dependencies = [ - "rustls 0.21.10", + "rustls 0.22.4", + "rustls-pki-types", "tokio", ] @@ -4735,9 +4770,9 @@ dependencies = [ [[package]] name = "toml_datetime" -version = "0.6.5" +version = "0.6.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3550f4e9685620ac18a50ed434eb3aec30db8ba93b0287467bca5826ea25baf1" +checksum = "4badfd56924ae69bcc9039335b2e017639ce3f9b001c393c1b2d1ef846ce2cbf" [[package]] name = "toml_edit" @@ -4750,6 +4785,28 @@ dependencies = [ "winnow 0.5.40", ] +[[package]] +name = "tower" +version = "0.4.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8fa9be0de6cf49e536ce1851f987bd21a43b771b09473c3549a6c853db37c1c" +dependencies = [ + "futures-core", + "futures-util", + "pin-project", + "pin-project-lite", + "tokio", + "tower-layer", + "tower-service", + "tracing", +] + +[[package]] +name = "tower-layer" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c20c8dbed6283a09604c3e69b4b7eeb54e298b8a600d4d5ecb5ad39de609f1d0" + [[package]] name = "tower-service" version = "0.3.2" @@ -4762,6 +4819,7 @@ version = "0.1.40" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c3523ab5a71916ccf420eebdf5521fcef02141234bbc0b8a49f2fdc4544364ef" dependencies = [ + "log", "pin-project-lite", "tracing-attributes", "tracing-core", @@ -4826,52 +4884,6 @@ dependencies = [ "tracing-log", ] -[[package]] -name = "trust-dns-proto" -version = "0.23.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3119112651c157f4488931a01e586aa459736e9d6046d3bd9105ffb69352d374" -dependencies = [ - "async-trait", - "cfg-if", - "data-encoding", - "enum-as-inner", - "futures-channel", - "futures-io", - "futures-util", - "idna 0.4.0", - "ipnet", - "once_cell", - "rand", - "smallvec", - "thiserror", - "tinyvec", - "tokio", - "tracing", - "url", -] - -[[package]] -name = "trust-dns-resolver" -version = "0.23.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "10a3e6c3aff1718b3c73e395d1f35202ba2ffa847c6a62eea0db8fb4cfe30be6" -dependencies = [ - "cfg-if", - "futures-util", - "ipconfig", - "lru-cache", - "once_cell", - "parking_lot", - "rand", - "resolv-conf", - "smallvec", - "thiserror", - "tokio", - "tracing", - "trust-dns-proto", -] - [[package]] name = "try-lock" version = "0.2.5" @@ -4922,9 +4934,9 @@ dependencies = [ [[package]] name = "unicode-bidi" -version = "0.3.14" +version = "0.3.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6f2528f27a9eb2b21e69c95319b30bd0efd85d09c379741b0f78ea1d86be2416" +checksum = "08f95100a766bf4f8f28f90d77e0a5461bbdb219042e7679bebe79004fed8d75" [[package]] name = "unicode-bom" @@ -4973,12 +4985,12 @@ checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" [[package]] name = "url" -version = "2.5.0" +version = "2.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "31e6302e3bb753d46e83516cae55ae196fc0c309407cf11ab35cc51a4c2a4633" +checksum = "22784dbdf76fdde8af1aeda5622b546b422b6fc585325248a2bf9f5e41e94d6c" dependencies = [ "form_urlencoded", - "idna 0.5.0", + "idna", "percent-encoding", ] @@ -4996,9 +5008,9 @@ checksum = "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d" [[package]] name = "value-bag" -version = "1.4.2" +version = "1.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4a72e1902dde2bd6441347de2b70b7f5d59bf157c6c62f0c44572607a1d55bbe" +checksum = "5a84c137d37ab0142f0f2ddfe332651fdbf252e7b7dbb4e67b6c1f1b2e925101" [[package]] name = "vcpkg" @@ -5131,15 +5143,12 @@ dependencies = [ [[package]] name = "webpki-roots" -version = "0.25.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1778a42e8b3b90bff8d0f5032bf22250792889a5cdc752aa0020c84abe3aaf10" - -[[package]] -name = "widestring" -version = "1.0.2" +version = "0.26.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "653f141f39ec16bba3c5abe400a0c60da7468261cc2cbf36805022876bc721a8" +checksum = "b3de34ae270483955a94f4b21bdaaeb83d508bb84a01435f393818edb0012009" +dependencies = [ + "rustls-pki-types", +] [[package]] name = "winapi" @@ -5472,15 +5481,40 @@ dependencies = [ "syn 2.0.47", ] +[[package]] +name = "zeroize" +version = "1.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "525b4ec142c6b68a2d10f01f7bbf6755599ca3f81ea53b8431b7dd348f5fdb2d" + [[package]] name = "zip" -version = "0.6.6" +version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "760394e246e4c28189f19d488c058bf16f564016aefac5d32bb1f3b51d5e9261" +checksum = "e2568cd0f20e86cd9a7349fe05178f7bd22f22724678448ae5a9bac266df2689" dependencies = [ - "byteorder", + "arbitrary", "crc32fast", "crossbeam-utils", + "displaydoc", "flate2", + "indexmap", + "memchr", + "thiserror", "time", + "zopfli", +] + +[[package]] +name = "zopfli" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5019f391bac5cf252e93bbcc53d039ffd62c7bfb7c150414d61369afe57e946" +dependencies = [ + "bumpalo", + "crc32fast", + "lockfree-object-pool", + "log", + "once_cell", + "simd-adler32", ] diff --git a/Cargo.toml b/Cargo.toml index 4116d7bd39e..e36d81ab687 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -5,7 +5,7 @@ repository = "https://github.com/Byron/gitoxide" authors = ["Sebastian Thiel "] edition = "2021" license = "MIT OR Apache-2.0" -version = "0.34.0" +version = "0.36.1" default-run = "gix" include = ["src/**/*", "LICENSE-*", "README.md"] resolver = "2" @@ -42,29 +42,29 @@ max = ["max-control", "fast", "gitoxide-core-blocking-client", "http-client-curl ## transports as it uses Rust's HTTP implementation. ## ## As fast as possible, with TUI progress, progress line rendering with auto-configuration, all transports available but less mature pure Rust HTTP implementation, all `ein` tools, CLI colors and local-time support, JSON output, regex support for rev-specs. -max-pure = ["max-control", "gix-features/rustsha1", "gix-features/zlib-rust-backend", "http-client-reqwest", "gitoxide-core-blocking-client" ] +max-pure = ["max-control", "gix-features/rustsha1", "gix-features/zlib-rust-backend", "http-client-reqwest", "gitoxide-core-blocking-client"] ## Like `max`, but with more control for configuration. See the *Package Maintainers* headline for more information. -max-control = ["tracing", "fast-safe", "pretty-cli", "gitoxide-core-tools-query", "gitoxide-core-tools-corpus", "gitoxide-core-tools", "prodash-render-line", "prodash-render-tui", "prodash/render-line-autoconfigure", "gix/revparse-regex" ] +max-control = ["tracing", "fast-safe", "pretty-cli", "gitoxide-core-tools-query", "gitoxide-core-tools-corpus", "gitoxide-core-tools", "prodash-render-line", "prodash-render-tui", "prodash/render-line-autoconfigure", "gix/revparse-regex"] ## All of the good stuff, with less fanciness for smaller binaries. ## ## As fast as possible, progress line rendering, all transports based on their most mature implementation (HTTP), all `ein` tools, CLI colors and local-time support, JSON output. -lean = ["fast", "tracing", "pretty-cli", "http-client-curl", "gitoxide-core-tools-query", "gitoxide-core-tools-corpus", "gitoxide-core-tools", "gitoxide-core-blocking-client", "prodash-render-line" ] +lean = ["fast", "tracing", "pretty-cli", "http-client-curl", "gitoxide-core-tools-query", "gitoxide-core-tools-corpus", "gitoxide-core-tools", "gitoxide-core-blocking-client", "prodash-render-line"] ## The smallest possible build, best suitable for small single-core machines. ## ## This build is essentially limited to local operations without any fanciness. ## ## Optimized for size, no parallelism thus much slower, progress line rendering. -small = ["pretty-cli", "gix-features/rustsha1", "gix-features/zlib-rust-backend", "prodash-render-line", "is-terminal" ] +small = ["pretty-cli", "gix-features/rustsha1", "gix-features/zlib-rust-backend", "prodash-render-line", "is-terminal"] ## Like lean, but uses Rusts async implementations for networking. ## ## This build is more of a demonstration showing how async can work with `gitoxide`, which generally is blocking. This also means that the selection of async transports ## is very limited to only HTTP (without typical `git` configuration) and git over TCP like provided by the `git daemon`. ## -## As fast as possible, progress line rendering, less feature-ful HTTP (pure Rust) and only `git-damon` support, all `ein` tools, CLI colors and local-time support, JSON output. +## As fast as possible, progress line rendering, less featureful HTTP (pure Rust) and only `git-daemon` support, all `ein` tools, CLI colors and local-time support, JSON output. ## ## Due to async client-networking not being implemented for most transports, this one supports only the 'git+tcp' and HTTP transport. ## It uses, however, a fully asynchronous networking implementation which can serve a real-world example on how to implement custom async transports. @@ -107,12 +107,12 @@ fast = ["gix/max-performance", "gix/comfort"] fast-safe = ["gix/max-performance-safe", "gix/comfort"] ## Enable tracing in `gitoxide-core`. -tracing = ["dep:tracing-forest", "dep:tracing-subscriber", "dep:tracing", "gix-features/tracing", "gix-features/tracing-detail" ] +tracing = ["dep:tracing-forest", "dep:tracing-subscriber", "dep:tracing", "gix-features/tracing", "gix-features/tracing-detail"] ## Use `clap` 3.0 to build the prettiest, best documented and most user-friendly CLI at the expense of binary size. ## Provides a terminal user interface for detailed and exhaustive progress. ## Provides a line renderer for leaner progress display, without the need for a full-blown TUI. -pretty-cli = [ "gitoxide-core/serde", "prodash/progress-tree", "prodash/progress-tree-log", "prodash/local-time", "env_logger/humantime", "env_logger/color", "env_logger/auto-color" ] +pretty-cli = ["gitoxide-core/serde", "prodash/progress-tree", "prodash/progress-tree-log", "prodash/local-time", "env_logger/humantime", "env_logger/color", "env_logger/auto-color"] ## The `--verbose` flag will be powered by an interactive progress mechanism that doubles as log as well as interactive progress ## that appears after a short duration. @@ -168,9 +168,9 @@ gitoxide-core-async-client = ["gitoxide-core/async-client", "futures-lite"] [dependencies] anyhow = "1.0.42" -gitoxide-core = { version = "^0.36.0", path = "gitoxide-core" } -gix-features = { version = "^0.38.0", path = "gix-features" } -gix = { version = "^0.59.0", path = "gix", default-features = false } +gitoxide-core = { version = "^0.38.0", path = "gitoxide-core" } +gix-features = { version = "^0.38.2", path = "gix-features" } +gix = { version = "^0.63.0", path = "gix", default-features = false } time = "0.3.23" clap = { version = "4.1.1", features = ["derive", "cargo"] } @@ -285,9 +285,7 @@ members = [ "gix-worktree-stream", "gix-revwalk", "gix-fsck", - "tests/tools", - "gix-diff/tests", "gix-pack/tests", "gix-odb/tests", @@ -304,6 +302,8 @@ members = [ prodash = { version = "28.0.0", default-features = false } futures-lite = { version = "2.1.0", default-features = false, features = ["std"] } faster-hex = { version = "0.9.0", default-features = false } +reqwest = { version = "0.12.0", default-features = false, features = ["charset", "http2", "macos-system-configuration"] } # all but the 'default-tls' feature +curl = { version = "0.4" } [package.metadata.docs.rs] features = ["document-features", "max"] diff --git a/DEVELOPMENT.md b/DEVELOPMENT.md index b9d3ee59593..72b2a861ac0 100644 --- a/DEVELOPMENT.md +++ b/DEVELOPMENT.md @@ -1,6 +1,6 @@ # Development Guide -## Practices +## Practices * **test-first development** * protect against regression and make implementing features easy. @@ -9,12 +9,12 @@ for the mundane things, like unhappy code paths. * *use git itself* as reference implementation, and use their test-cases and fixtures where appropriate. At the very least, try to learn from them. - * Run the same test against git whenever feasible to assure git agrees with our implementation. + * Run the same test against git whenever feasible to assure git agrees with our implementation. See `gix-glob` for examples. * *use libgit2* test fixtures and cases where appropriate, or learn from them. * **safety first** * handle all errors, never `unwrap()`. If needed, `expect("why")`. - * provide an error chain and make it easy to understand what went wrong. + * provide an error chain and make it easy to understand what went wrong. * We `thiserror` generally. * Adhere to the [stability guide](https://github.com/Byron/gitoxide/blob/main/STABILITY.md) @@ -28,10 +28,10 @@ The _subject_ usually informs about the *what* and the body provides details and Commit messages _must_ show up in the changelog in case of breaking changes. Examples for that are: - change!: rename `Foo` to `Bar`. (#123) - + And this is why we do it in the body. - remove!: `Repository::obsolete()`. - + Nobody used this method. Features or other changes that are visible and people should know about look like this: @@ -48,7 +48,7 @@ Examples could be: - `make fmt` - thanks clippy -Please refrain from using `chore:` or `refactor:` prefixes as for the most part, users of the API don't care about those. When a `refactor` +Please refrain from using `chore:` or `refactor:` prefixes as for the most part, users of the API don't care about those. When a `refactor` changes the API in some way, prefer to use `feat`, `change`, `rename` or `remove` instead, and most of the time the ones that are not `feat` are breaking so would be seen with their _exclamation mark_ suffix, like `change!`. @@ -63,20 +63,20 @@ Knowing that `cargo smart-release` is driven by commit messages and affects thei to split edits into multiple commits to clearly indicate which crate is actually broken. Typical patterns include making a breaking change in one crate and then fix all others to work with it. For changelogs to look proper -and version bumps to be correct, the first commit would contain only the breaking changes themselves, +and version bumps to be correct, the first commit would contain only the breaking changes themselves, like "rename: `foo()` to `bar()`", and the second commit would contain all changes to adapt to that and look like "adapt to changes in ``". ## Commit History -We generally follow a 'track everything' approach and there is a lot of freedom leading to more commits rather than less. There +We generally follow a 'track everything' approach and there is a lot of freedom leading to more commits rather than less. There is no obligation to squash commits or to otherwise tune the history. We use feature branches and PRs most of the time to be able to take advantage of CI and GitHub review tools, and merge with merge commits -to make individual efforts stand out. There is no need for linearizing history or tuning it in any other way. However, each commit +to make individual efforts stand out. There is no need for linearizing history or tuning it in any other way. However, each commit _must_ follow the guidelines laid out in the `Commit Messages` paragraph. There is value in organizing commits by topic and [_Stacked Git_](https://stacked-git.github.io) is hereby endorsed to do that. - + ## Configuration and overrides As a general rule, respect and implement all applicable [git-config](https://git-scm.com/docs/git-config) by default, but allow the @@ -102,11 +102,11 @@ Parameters which are not available in git or specific to `gitoxide` or the needs * **User Interfaces** * User interfaces can greatly benefit from using async as it's much easier to maintain a responsive UI thread that way thanks to the wonderful future combinators. - * `blocking` can be used to make `Read` and `Iterator` async, or move any operation onto a thread which blends it into the - async world. + * `blocking` can be used to make `Read` and `Iterator` async, or move any operation onto a thread which blends it into the + async world. * Most operations are fast and 'interrupting' them is as easy as ignoring their result by cancelling their task. * Long-running operations can be roughly interacted with using `gix_features::interrupt::trigger()` function, and after a moment - of waiting the flag can be unset with the `…::uninterrupt()` function to allow new long-running operations to work. + of waiting the flag can be unset with the `…::uninterrupt()` function to allow new long-running operations to work. Every long running operation supports this. * **server-side** * ~~Building a pack is CPU and at some point, IO bound, and it makes no sense to use async to handle more connections - git @@ -119,7 +119,7 @@ Parameters which are not available in git or specific to `gitoxide` or the needs * **Why not use it to generate blocking versions of traits automatically?** * This would require `maybe_async` and its dependencies to always be present, increasing compile times. For now we chose a little more code to handle over increasing compile times for everyone. This stance may change later once compile times don't matter that much anymore to allow the removal of code. - + * **`Default` trait implementations** * These can change only if the effect is contained within the callers process. This means **changing the default of a file version** is a **breaking change**. @@ -127,7 +127,7 @@ Parameters which are not available in git or specific to `gitoxide` or the needs * When receiving a `Progress` implementation * without calling `add_child(…)` then use it directly to communicate progress, leaving control of the name to the caller. However, call `.init(…)` to configure the iteration. - * and when calling `add_child(…)` don't use the parent progress instance for anything else. + * and when calling `add_child(…)` don't use the parent progress instance for anything else. * **interruption of long-running operations** * Use `gix-features::interrupt::*` for building support for interruptions of long-running operations only. * It's up to the author to decide how to best integrate it, generally we use a poll-based mechanism to check whether @@ -136,9 +136,9 @@ Parameters which are not available in git or specific to `gitoxide` or the needs * …temporary resources like files might otherwise be leaked. * **this is optional but desirable if…** * …there is no leakage otherwise to support user interfaces. They background long-running operations and need them to be cancellable. - + * **prepare for SHA256 support by using `gix_hash::ObjectId` and `gix_hash::oid`** - * eventually there will be the need to support both Sha1 and Sha256. We anticipate it by using the `Id` type instead + * eventually there will be the need to support both Sha1 and Sha256. We anticipate it by using the `Id` type instead of slices or arrays of 20 bytes. This way, eventually we can support multiple hash digest sizes. * Right now it's unclear how Sha256 is going to work in git, so we only support Sha1 for now. It might be an avenue to proactively implement it ahead of time once there is a specification to follow. @@ -166,7 +166,7 @@ Parameters which are not available in git or specific to `gitoxide` or the needs - Assuming UTF8-ish bytes in paths produced by `git` even on windows due to `MSYS2`, we use `os_str_bytes` to convert these back into `OsStr` and derivatives like `Path` as needed even though it might not always be the case depending on the actual encoding used by `MSYS2` or other abstraction layers, or avoiding to use std types altogether using our own instead. - + ## Sha256 A bunch of notes collected to keep track of what's needed to eventually support it @@ -181,7 +181,7 @@ A bunch of notes collected to keep track of what's needed to eventually support * don't use unwrap, not even in tests. Instead use `quick_error!()` or `Box`. * Use `expect(…)` as assertion on Options, providing context on *why* the expectations should hold. Or in other words, answer "This should work _because_…" - + ## `Options` vs `Context` - Use `Options` whenever there is something to configure in terms of branching behaviour. It can be defaulted, and if it can't these fields should be parameters of the method @@ -195,7 +195,7 @@ In _plumbing_ crates, prefer to default to keeping references if this is feasibl In _porcelain_ crates, like `gix`, we have `Platforms` which are typically cheap enough to create on demand as they configure one or more method calls. These should keep a reference to the `Repository` instance that created them as the user is expected to clone the `Repository` if there is the need. However, if these structures are more expensive, call them `Cache` or `` and prefer to clone the `Repository` into them or otherwise keep them free of lifetimes -to allow the user to keep this structure around for repeated calls. References for this paragraph are [this PR](https://github.com/Canop/bacon/pull/98) and +to allow the user to keep this structure around for repeated calls. References for this paragraph are [this PR](https://github.com/Canop/bacon/pull/98) and [this discussion](https://github.com/Byron/gitoxide/discussions/675). ## Examples, Porcelain CLI and Plumbing CLI - which does what? @@ -240,12 +240,12 @@ by humans. * make it compile quickly, so no extras * **Examples** * An implementation of ideas for actual occasional use and the first step towards getting integrated into Porcelain or Plumbing CLIs. - * Proper command-line parsing with Clap. - * No tests or progress. - * High quality Rust code along with idiomatic `gitoxide` usage so people can learn from it. -* **Plumbing CLI** + * Proper command-line parsing with Clap. + * No tests or progress. + * High quality Rust code along with idiomatic `gitoxide` usage so people can learn from it. +* **Plumbing CLI** * Use Clap AND Argh for command-line parsing via feature toggles to allow for tiny builds as plumbing is mostly for scripts. - * Journey tests + * Journey tests * Progress can be turned on using the `--verbose` flag, quiet by default. * Examples can be turned into plumbing by adding journey tests and `argh` command-line parsing, as well as progress. * **Porcelain CLI** @@ -262,9 +262,8 @@ get an overview. ## Reviewing PRs -- be sure to clone locally and run tests with `GITOXIDE_TEST_IGNORE_ARCHIVES=1` to assure new fixture scripts (if there are any) are validated - on _MacOS_ and _Windows_. Note that linux doesn't need to be tested that way as CI on linux ignores them by merit of not checking them out - via `gix-lfs`. +- Be sure to clone locally and run tests with `GIX_TEST_IGNORE_ARCHIVES=1` to assure new fixture scripts (if there are any) are validated + on _MacOS_ and _Windows_. Linux doesn't need to be tested locally that way, as CI on Linux includes it. ## Creating a release @@ -309,5 +308,5 @@ GIT_SSH_COMMAND="ssh -VVV" \ git ``` -Consider adding `GIT_TRACE2_PERF=1` (possibly add `GIT_TRACE2_PERF_BRIEF=1` for brevity) as well for statistics and variables +Consider adding `GIT_TRACE2_PERF=1` (possibly add `GIT_TRACE2_PERF_BRIEF=1` for brevity) as well for statistics and variables (see [their source for more](https://github.com/git/git/blob/b50a608ba20348cb3dfc16a696816d51780e3f0f/trace2/tr2_sysenv.c#L50). diff --git a/README.md b/README.md index 7a2f98189d6..1b6e9b19d89 100644 --- a/README.md +++ b/README.md @@ -1,12 +1,12 @@ -[![Rust](https://github.com/Byron/gitoxide/workflows/Rust/badge.svg)](https://github.com/Byron/gitoxide/actions) +[![CI](https://github.com/Byron/gitoxide/workflows/ci/badge.svg)](https://github.com/Byron/gitoxide/actions) [![Crates.io](https://img.shields.io/crates/v/gitoxide.svg)](https://crates.io/crates/gitoxide) `gitoxide` is an implementation of `git` written in Rust for developing future-proof applications which strive for correctness and -performance while providing a pleasant and unsurprising developer experience. +performance while providing a pleasant and unsurprising developer experience. `gitoxide` provides the `gix` and `ein` binaries for use on the command-line to allow experimentation with key features -like `fetch` and `clone`, and to validate the usability and control of the API offered by the [`gix`] crate. +like `fetch` and `clone`, and to validate the usability and control of the API offered by the [`gix`] crate. `gitoxide` aspires to be a production-grade server implementation and the `ein` binary aspires to become the default way to interact with git repositories. @@ -16,10 +16,10 @@ like `fetch` and `clone`, and to validate the usability and control of the API o ## Development Status -The command-line tools as well as the status of each crate is described in +The command-line tools as well as the status of each crate is described in [the crate status document](https://github.com/Byron/gitoxide/blob/main/crate-status.md). -For use in applications, look for the [`gix`](https://github.com/Byron/gitoxide/blob/main/crate-status.md#gix) crate, +For use in applications, look for the [`gix`](https://github.com/Byron/gitoxide/blob/main/crate-status.md#gix) crate, which serves as entrypoint to the functionality provided by various lower-level plumbing crates like [`gix-config`](https://github.com/Byron/gitoxide/blob/main/crate-status.md#gix-config). @@ -66,10 +66,10 @@ Follow linked crate name for detailed status. Please note that all crates follow * **Stability Tier 1** - [gix-lock](https://github.com/Byron/gitoxide/blob/main/gix-lock/README.md) - + * **Stability Tier 2** - [gix-tempfile](https://github.com/Byron/gitoxide/blob/main/gix-tempfile/README.md) - + ### Stabilization Candidates Crates that seem feature complete and need to see some more use before they can be released as 1.0. @@ -144,13 +144,13 @@ is usable to some extent. * [gix-tix](https://github.com/Byron/gitoxide/blob/main/crate-status.md#gix-tix) * [gix-bundle](https://github.com/Byron/gitoxide/blob/main/crate-status.md#gix-bundle) * [gix-fsck](https://github.com/Byron/gitoxide/blob/main/crate-status.md#gix-fsck) - + ### Stress Testing * [x] Verify huge packs - * [x] Explode a pack to disk + * [x] Explode a pack to disk * [x] Generate and verify large commit graphs * [ ] Generate huge pack from a lot of loose objects - + ### Stability and MSRV Our [stability guide] helps to judge how much churn can be expected when depending on crates in this workspace. @@ -161,7 +161,7 @@ Our [stability guide] helps to judge how much churn can be expected when dependi ### Download a Binary Release -Using `cargo binstall`, one is able to fetch [binary releases][releases]. You can install it via `cargo install cargo-binstall`, assuming +Using `cargo binstall`, one is able to fetch [binary releases][releases]. You can install it via `cargo install cargo-binstall`, assuming the [rust toolchain][rustup] is present. Then install gitoxide with `cargo binstall gitoxide`. @@ -178,6 +178,14 @@ For Arch Linux you can download `gitoxide` from `community` repository: pacman -S gitoxide ``` +### Download from Exherbo Linux Rust repository + +For Exherbo Linux you can download `gitoxide` from the [Rust](https://gitlab.exherbo.org/exherbo/rust/-/tree/master/packages/dev-scm/gitoxide) repository: +``` +cave resolve -x repository/rust +cave resolve -x gitoxide +``` + ### From Source via Cargo `cargo` is the Rust package manager which can easily be obtained through [rustup]. With it, you can build your own binary @@ -192,7 +200,7 @@ for packagers who need to tune external dependencies. ``` # A certain way to install `gitoxide` with just Rust and a C compiler installed. # If there are problems with SSL certificates during clones, try to omit `--locked`. -cargo install gitoxide --locked --no-default-features --features max-pure +cargo install gitoxide --locked --no-default-features --features max-pure # The default installation, 'max', is the fastest, but also needs some libraries available to build successfully. # Installing these is platform-dependent and thus can't be explained here. @@ -218,8 +226,41 @@ What follows is a list of known failures. - On Fedora, `perl` needs to be installed for `OpenSSL` to build properly. This can be done with the following command: `dnf install perl` (see [this issue](https://github.com/Byron/gitoxide/issues/592)). +- +### Using Docker + +Some CI/CD pipelines leverage repository cloning. Below is a copy-paste-able example to build docker images for such workflows. +As no official image exists (at this time), an image must first be built. + +> [!NOTE] +> The dockerfile isn't continuously tested as it costs too much time and thus might already be broken. +> PRs are welcome. + +#### Building the most compatible base image + +```sh +docker build -f etc/docker/Dockerfile.alpine -t gitoxide:latest --compress . --target=pipeline +``` + +#### Basic usage in a Pipeline + +For example, if a `Dockerfile` currently uses something like `RUN git clone https://github.com/Byron/gitoxide`, first build the image: -[releases]: https://github.com/Byron/gitoxide/releases +```sh +docker build -f etc/docker/Dockerfile.alpine -t gitoxide:latest --compress . +``` + +Then copy the binaries into your image and replace the `git` directive with a `gix` equivalent. + +```dockerfile +COPY --from gitoxide:latest /bin/gix /usr/local/bin/ +COPY --from gitoxide:latest /bin/ein /usr/local/bin/ + +RUN /usr/local/bin/gix clone --depth 1 https://github.com/Byron/gitoxide gitoxide +``` + + +[releases]: https://github.com/Byron/gitoxide/releases [rustup]: https://rustup.rs ## Usage @@ -261,7 +302,6 @@ Project goals can change over time as we learn more, and they can be challenged. * With the tools and experience available here there is no reason not to support Windows. * [Windows is tested on CI](https://github.com/Byron/gitoxide/blob/df66d74aa2a8cb62d8a03383135f08c8e8c579a8/.github/workflows/rust.yml#L34) and failures do prevent releases. - ## Non-Goals @@ -275,7 +315,7 @@ Project non-goals can change over time as we learn more, and they can be challen * **use async IO everywhere** * for the most part, git operations are heavily reliant on memory mapped IO as well as CPU to decompress data, which doesn't lend itself well to async IO out of the box. - * Use `blocking` as well as `gix-features::interrupt` to bring operations into the async world and to control + * Use `blocking` as well as `gix-features::interrupt` to bring operations into the async world and to control long running operations. * When connecting or streaming over TCP connections, especially when receiving on the server, async seems like a must though, but behind a feature flag. @@ -284,12 +324,9 @@ Project non-goals can change over time as we learn more, and they can be challen If what you have seen so far sparked your interest to contribute, then let us say: We are happy to have you and help you to get started. -> ❗️Note❗️: For cloning, `gix-lfs` needs to be locally installed or the checkout will fail. `git lfs install` must have been called once, followed - by `git lfs pull` to replace the `lfs`-pointer files. - We recommend running `just test check-size` during the development process to assure CI is green before pushing. -A backlog for work ready to be picked up is [available in the Project's Kanban board][project-board], which contains instructions on how +A backlog for work ready to be picked up is [available in the Project's Kanban board][project-board], which contains instructions on how to pick a task. If it's empty or you have other questions, feel free to [start a discussion][discussions] or reach out to @Byron [privately][keybase]. For additional details, also take a look at the [collaboration guide]. @@ -307,7 +344,7 @@ For additional details, also take a look at the [collaboration guide]. - Get an introduction to `gitoxide` itself which should be a good foundation for any contribution, but isn't a requirement for contributions either. - [Gifting Gitoxide](https://www.youtube.com/playlist?list=PLMHbQxe1e9MlhyyZQXPi_dc-bKudE-WUw) - See how PRs are reviewed along with a lot of inner monologue. - + #### Other Media - [Rustacean Station Podcast](https://rustacean-station.org/episode/055-sebastian-thiel/) @@ -366,24 +403,23 @@ Provide a CLI to for the most basic user journey: [fossil-scm]: https://www.fossil-scm.org [magit]: https://magit.vc - ## Shortcomings & Limitations Please take a look at the [`SHORTCOMINGS.md` file](https://github.com/Byron/gitoxide/blob/main/SHORTCOMINGS.md) for details. - + ## Credits * **itertools** _(MIT Licensed)_ * We use the `izip!` macro in code * **deflate2** _(MIT Licensed)_ * We use various abstractions to implement decompression and compression directly on top of the rather low-level `miniz_oxide` crate - + ## 🙏 Special Thanks 🙏 At least for now this section is exclusive to highlight the incredible support that [Josh Triplett](https://github.com/joshtriplett) has provided to me in the form of advice, sponsorship and countless other benefits that were incredibly meaningful. Going full time with `gitoxide` would hardly have been feasible without his involvement, and I couldn't be more grateful 😌. - + ## License This project is licensed under either of @@ -399,6 +435,6 @@ at your option. * Originally @Byron was really fascinated by [this problem](https://github.com/gitpython-developers/GitPython/issues/765#issuecomment-396072153) and believes that with `gitoxide` it will be possible to provide the fastest solution for it. -* @Byron has been absolutely blown away by `git` from the first time he experienced git more than 13 years ago, and +* @Byron has been absolutely blown away by `git` from the first time he experienced git more than 13 years ago, and tried to implement it in [various shapes](https://github.com/gitpython-developers/GitPython/pull/1028) and [forms](https://github.com/byron/gogit) multiple [times](https://github.com/Byron/gitplusplus). Now with Rust @Byron finally feels to have found the right tool for the job! diff --git a/SECURITY.md b/SECURITY.md index f80d96826d1..0cfacebcfaf 100644 --- a/SECURITY.md +++ b/SECURITY.md @@ -2,4 +2,9 @@ ## Reporting a Vulnerability -Please inform me by writing an email to the @icloud.com email used in my commits. Thank you. +Please feel free to [draft a GitHub advisory](https://github.com/Byron/gitoxide/security/advisories/new), and I will work with you to disclose and or resolve the issue +responsibly. + +If this doesn't seem like the right approach or there are questions, please feel free to reach out to the @icloud.com email used in my commits. + +Thank you. diff --git a/_typos.toml b/_typos.toml index 8d8226d7aa3..ea8c5e4c03a 100644 --- a/_typos.toml +++ b/_typos.toml @@ -1,19 +1,32 @@ -[default] -extend-ignore-identifiers-re = [ - "[a-f0-9]{7,}", - "[Ff][Oo][Oo]", - "[a-zA-Z0-9]{20,}", -] - [files] extend-exclude = [ "**/CHANGELOG.md", - "**/tests/fixtures/**", + "*.response", + "etc/corpus/repo_metadata.sample.jsonl", "gix-config/benches/large_config_file.rs", - "gix-glob/tests/wildmatch/mod.rs" + "gix-config/tests/fixtures/clusterfuzz-testcase-minimized-gix-config-parse-6431708583690240", + "gix-glob/tests/wildmatch/mod.rs", + "gix-path/tests/fixtures/fuzzed/54k-path-components.path", ] +ignore-hidden = false +[default] +extend-ignore-re = [ + # PGP signature + "[0-9A-Za-z+=]{44,}", + '"egal"', + '"bar", "bart", "ba"', + '"foo/ba"', + '"FO", "FO", &\[\], "FO"', + '"(BarFoO|BarFoOo|FoO|FoOo)"', + "\\|pn: &BStr\\| pn !=", +] +extend-ignore-identifiers-re = [ + # Git commit hash + "[0-9a-f]{7,}", + "rela_[a-z_]+", +] -[default.extend-words] -rela = "rela" -unter = "unter" +[default.extend-identifiers] +_pn = "_pn" +ein = "ein" diff --git a/crate-status.md b/crate-status.md index 6a43168f4e7..ee017c8ef69 100644 --- a/crate-status.md +++ b/crate-status.md @@ -44,32 +44,31 @@ The top-level crate that acts as hub to all functionality provided by the `gix-* * [ ] handle other non-discovery modes and provide control over environment variable usage required in applications * [x] rev-parse - [ ] handle relative paths as relative to working directory - - [ ] handle `upstream` and `push` resolution. + - [x] handle `upstream` and `push` resolution. * [x] rev-walk * [x] include tips * [ ] exclude commits * [x] instantiation * [x] access to refs and objects + * [x] create a pathspec-search from a set of strings + - [ ] allow to construct Pathspecs using data structure instead of enforcing them to be passed as strings. * **credentials** * [x] run `git credential` directly * [x] use credential helper configuration and to obtain credentials with `gix_credentials::helper::Cascade` - * **config** - * [ ] facilities to apply the [url-match](https://git-scm.com/docs/git-config#Documentation/git-config.txt-httplturlgt) algorithm and to - [normalize urls](https://github.com/git/git/blob/be1a02a17ede4082a86dfbfee0f54f345e8b43ac/urlmatch.c#L109:L109) before comparison. * **traverse** * [x] commit graphs * [ ] make [git-notes](https://git-scm.com/docs/git-notes) accessible * [x] tree entries * **diffs/changes** * [x] tree with other tree - * [ ] respect case-sensitivity of host filesystem. + * [x] respect case-sensitivity of host filesystem. * [x] a way to access various diff related settings or use them - * [ ] respect `diff.*.textconv`, `diff.*.cachetextconv` and external diff viewers with `diff.*.command`, + * [x] respect `diff.*.textconv`, `diff.*.cachetextconv` and external diff viewers with `diff.*.command`, [along with support for reading `diff` gitattributes](https://github.com/git/git/blob/73876f4861cd3d187a4682290ab75c9dccadbc56/Documentation/gitattributes.txt#L699:L699). * **rewrite tracking** * **deviation** - git keeps up to four candidates whereas we use the first-found candidate that matches the similarity percentage. This can lead to different sources being found. As such, we also don't consider the filename at all. - * [ ] handle binary files correctly, and apply filters for that matter + * [x] handle binary files correctly, and apply filters for that matter * [x] computation limit with observable reduction of precision when it is hit, for copies and renames separately * **by identity** * [x] renames (sym-links are only ever compared by identity) @@ -78,9 +77,9 @@ The top-level crate that acts as hub to all functionality provided by the `gix-* * [x] renames * [x] copies * [x] 'find-copies-harder' - find copies with the source being the entire tree. - * [ ] tree or index with working tree - - [ ] rename tracking - - [ ] submodule status (recursive) + * [x] tree or index with working tree + - [x] rename tracking + - [x] submodule status (recursive) * [x] diffs between modified blobs with various algorithms * [ ] tree with index (via index-from-tree and index) - [ ] rename tracking @@ -152,7 +151,10 @@ The top-level crate that acts as hub to all functionality provided by the `gix-* * [x] read the primitive types `boolean`, `integer`, `string` * [x] read and interpolate trusted paths * [x] low-level API for more elaborate access to all details of `git-config` files - * [ ] a way to make changes to individual configuration files + * [ ] a way to make changes to individual configuration files in memory + * [ ] write configuration back + * [ ] auto-refresh configuration values after they changed on disk + * [ ] facilities to apply the [url-match](https://git-scm.com/docs/git-config#Documentation/git-config.txt-httplturlgt) algorithm and to [normalize urls](https://github.com/git/git/blob/be1a02a17ede4082a86dfbfee0f54f345e8b43ac/urlmatch.c#L109:L109) before comparison. * [x] mailmap * [x] object replacements (`git replace`) * [x] read git configuration @@ -459,7 +461,9 @@ Check out the [performance discussion][gix-traverse-performance] as well. ### gix-pathspec * [x] parse single * [ ] parse file line by line (with or without quoting, NUL and LF/CRLF line separation) (see `--pathspec-from-file` and `--pathspec-file-nul`) -* [ ] matching of paths with git-attributes support +* [x] matching of paths with git-attributes support +* [ ] programmatic creation of pathspecs +* [ ] `TryIntoPathspec` trait to parse strings or accept ready-made pathspecs as well, for use in APIs ### gix-refspec * [x] parse @@ -552,12 +556,12 @@ Make it the best-performing implementation and the most convenient one. ### gix-status * [x] differences between index and worktree to turn index into worktree - - [ ] rename tracking + - [x] rename tracking + - [x] untracked files + - [ ] support for fs-monitor for modification checks * [ ] differences between index and index to learn what changed - [ ] rename tracking -* [ ] untracked files -* [ ] fast answer to 'is it dirty'. -* + ### gix-worktree-state * handle the working **tree/checkout** - [x] checkout an index of files, executables and symlinks just as fast as git diff --git a/deny.toml b/deny.toml index ddddea8f5f6..bd16612e281 100644 --- a/deny.toml +++ b/deny.toml @@ -8,21 +8,10 @@ # More documentation for the advisories section can be found here: # https://embarkstudios.github.io/cargo-deny/checks/advisories/cfg.html [advisories] -# The path where the advisory database is cloned/fetched into -db-path = "~/.cargo/advisory-db" -# The url(s) of the advisory databases to use -db-urls = ["https://github.com/rustsec/advisory-db"] -# The lint level for security vulnerabilities -vulnerability = "deny" -# The lint level for unmaintained crates -unmaintained = "warn" -# The lint level for crates that have been yanked from their source registry -yanked = "warn" -# The lint level for crates with security notices. Note that as of -# 2019-12-17 there are no security notice advisories in -# https://github.com/rustsec/advisory-db -notice = "warn" -ignore = [] +ignore = [ + # this is `rustls@0.20.9` coming in with `curl`, which doesn't have an update yet. It's only active optionally, not by default. + "RUSTSEC-2024-0336", +] @@ -30,28 +19,21 @@ ignore = [] # More documentation for the licenses section can be found here: # https://embarkstudios.github.io/cargo-deny/checks/licenses/cfg.html [licenses] -# The lint level for crates which do not have a detectable license -unlicensed = "deny" # List of explicitly allowed licenses # See https://spdx.org/licenses/ for list of possible licenses # [possible values: any SPDX 3.11 short identifier (+ optional exception)]. allow = [ "Apache-2.0", "BSD-3-Clause", + "BSL-1.0", "MIT", "MIT-0", "ISC", "Unicode-DFS-2016", "LicenseRef-ring", - "Zlib" + "Zlib", + "Unicode-3.0" ] -# Lint level for licenses considered copyleft -copyleft = "allow" -# Lint level used when no other predicates are matched -# 1. License isn't in the allow or deny lists -# 2. License isn't copyleft -# 3. License isn't OSI/FSF, or allow-osi-fsf-free = "neither" -default = "deny" # The confidence threshold for detecting a license from license text. # The higher the value, the more closely the license text must be to the # canonical license text of a valid SPDX license file. diff --git a/etc/check-package-size.sh b/etc/check-package-size.sh index 7eee9d606ba..c23bad23336 100755 --- a/etc/check-package-size.sh +++ b/etc/check-package-size.sh @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash set -eu -o pipefail diff --git a/etc/copy-packetline.sh b/etc/copy-packetline.sh new file mode 100755 index 00000000000..9ed5a0eea89 --- /dev/null +++ b/etc/copy-packetline.sh @@ -0,0 +1,152 @@ +#!/usr/bin/env bash + +set -euC -o pipefail + +readonly input_dir='gix-packetline/src' +readonly output_parent_dir='gix-packetline-blocking' +readonly output_dir="$output_parent_dir/src" + +function fail () { + printf '%s: error: %s\n' "$0" "$1" >&2 + exit 1 +} + +function chdir_toplevel () { + local root_padded root + + # Find the working tree's root. (Padding covers the trailing-newline case.) + root_padded="$(git rev-parse --show-toplevel && echo -n .)" || + fail 'git-rev-parse failed to find top-level dir' + root="${root_padded%$'\n.'}" + + cd -- "$root" +} + +function merging () { + local git_dir_padded git_dir + + # Find the .git directory. (Padding covers the trailing-newline case.) + git_dir_padded="$(git rev-parse --git-dir && echo -n .)" || + fail 'git-rev-parse failed to find git dir' + git_dir="${git_dir_padded%$'\n.'}" + + test -e "$git_dir/MERGE_HEAD" +} + +function output_dir_status () { + git status --porcelain --ignored=traditional -- "$output_dir" || + fail 'git-status failed' +} + +function check_output_dir () { + if ! test -e "$output_dir"; then + # The destination does not exist on disk, so nothing will be lost. Proceed. + return + fi + + if merging; then + # In a merge, it would be confusing to replace anything at the destination. + if output_dir_status | grep -q '^'; then + fail 'output location exists, and a merge is in progress' + fi + else + # We can lose data if anything of value at the destination is not in the + # index. (This includes unstaged deletions, for two reasons. We could lose + # track of which files had been deleted. More importantly, replacing a + # staged symlink or regular file with an unstaged directory is shown by + # git-status as only a deletion, even if the directory is non-empty.) + if output_dir_status | grep -q '^.[^ ]'; then + fail 'output location exists, with unstaged changes or ignored files' + fi + fi +} + +function first_line_ends_crlf () { + # This is tricky to check portably. In Cygwin-like environments including + # MSYS2 and Git Bash, most text processing tools, including awk, sed, and + # grep, automatically ignore \r before \n. Some ignore \r everywhere. Some + # can be told to keep \r, but in non-portable ways that may affect other + # implementations. Bash ignores \r in some places even without "-o igncr", + # and ignores \r even more with it, including in all text from command + # substitution. Simple checks may be non-portable to other OSes. Fortunately, + # tools that treat input as binary data are exempt (even cat, but "-v" is + # non-portable, and unreliable in general because lines can end in "^M"). + # This may be doable without od, by using tr more heavily, but it could be + # hard to avoid false positives with unexpected characters or \r without \n. + + head -n 1 -- "$1" | # Get the longest prefix with no non-trailing \n byte. + od -An -ta | # Represent all bytes symbolically, without addresses. + tr -sd '\n' ' ' | # Scrunch into one line, so "cr nl" appears as such. + grep -q 'cr nl$' # Check if the result signifies a \r\n line ending. +} + +function make_header () { + local input_file endline + + input_file="$1" + endline="$2" + + # shellcheck disable=SC2016 # The backticks are intentionally literal. + printf '// DO NOT EDIT - this is a copy of %s. Run `just copy-packetline` to update it.%s%s' \ + "$input_file" "$endline" "$endline" +} + +function copy_with_header () { + local input_file output_file endline + + input_file="$1" + output_file="$2" + + if first_line_ends_crlf "$input_file"; then + endline=$'\r\n' + else + endline=$'\n' + fi + + make_header "$input_file" "$endline" | cat -- - "$input_file" >"$output_file" +} + +function generate_one () { + local input_file output_file + + input_file="$1" + output_file="$output_dir${input_file#"$input_dir"}" + + if test -d "$input_file"; then + mkdir -p -- "$output_file" + elif test -L "$input_file"; then + # Cover this case separately, for more useful error messages. + fail "input file is symbolic link: $input_file" + elif ! test -f "$input_file"; then + # This covers less common kinds of files we can't or shouldn't process. + fail "input file neither regular file nor directory: $input_file" + elif [[ "$input_file" =~ \.rs$ ]]; then + copy_with_header "$input_file" "$output_file" + else + fail "input file not named as Rust source code: $input_file" + fi +} + +function generate_all () { + local input_file + + if ! test -d "$input_dir"; then + fail "no input directory: $input_dir" + fi + if ! test -d "$output_parent_dir"; then + fail "no output parent directory: $output_parent_dir" + fi + check_output_dir + + rm -rf -- "$output_dir" # It may be a directory, symlink, or regular file. + if test -e "$output_dir"; then + fail 'unable to remove output location' + fi + + find "$input_dir" -print0 | while IFS= read -r -d '' input_file; do + generate_one "$input_file" + done +} + +chdir_toplevel +generate_all diff --git a/etc/corpus/clone-repos.sh b/etc/corpus/clone-repos.sh index 0581544b16f..b302fcbd4e8 100755 --- a/etc/corpus/clone-repos.sh +++ b/etc/corpus/clone-repos.sh @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash # Check if there is input on stdin if [ -t 0 ]; then diff --git a/etc/docker/Dockerfile.alpine b/etc/docker/Dockerfile.alpine new file mode 100644 index 00000000000..0c29bccb162 --- /dev/null +++ b/etc/docker/Dockerfile.alpine @@ -0,0 +1,70 @@ +# Please note that this file is not regularly tested, and you are welcome to create PRs with fixes as needed. +ARG GITOXIDE_VERSION=0.36.0 + +FROM rust:alpine AS bootstrap_os + # hadolint ignore=DL3018 + RUN apk upgrade --update-cache --available \ + && apk add --no-cache --virtual .runtime-gitoxide libressl zlib-ng \ + libressl3.8-libcrypto + + +FROM bootstrap_os AS bootstrap_build_deps + # hadolint ignore=DL3018 + RUN apk add --no-cache --virtual .rust-builder cmake gcc musl-dev make pkgconfig \ + && apk add --no-cache --virtual .bootstrap-gitoxide libressl-dev zlib-ng \ + libressl3.8-libcrypto + + +FROM bootstrap_build_deps AS bootstrap_builder + ARG GITOXIDE_VERSION + + ENV RUST_BACKTRACE=1 + + RUN cargo install gitoxide --version "${GITOXIDE_VERSION}" --message-format short \ + && apk del .bootstrap-gitoxide \ + && apk del .rust-builder + + +FROM alpine:3.13.3 AS pipeline + # hadolint ignore=SC2016 + RUN apk upgrade --update-cache --available \ + && { \ + echo '#!/bin/sh'; \ + echo 'set -eu'; \ + echo 'if [ "${#}" -gt 0 ] && [ "${1#-}" = "${1}" ] \'; \ + echo ' && command -v "${1}" > "/dev/null" 2>&1; then'; \ + echo ' exec "${@}"'; \ + echo 'else exec /bin/shfmt "${@}"; fi'; \ + echo 'exit 0'; \ + } > /init && chmod +x /init + + COPY --from=bootstrap_builder /usr/local/cargo/bin/gix /usr/local/cargo/bin/ + COPY --from=bootstrap_builder /usr/local/cargo/bin/ein /usr/local/cargo/bin/ + + WORKDIR /usr/local/cargo/bin + + SHELL [ "/bin/ash", "-o", "pipefail", "-c" ] + + RUN find . -type f -executable -not \( -name '*tkinter*' \) -exec ldd '{}' ';' \ + | awk '/=>/ { so = $(NF-1); if (index(so, "/usr/local/") == 1) { next }; gsub("^/(usr/)?", "", so); gsub(".*/", "", so); print so }' \ + | xargs -r apk search -f | awk '{ so = $(NF-1); gsub(/-\d+.*$/, "", so); print so }' \ + | xargs -r apk add --no-cache --virtual .runtime + + ENV PATH="/usr/local/cargo/bin:${PATH}" + + WORKDIR /root + + HEALTHCHECK --retries=1 --timeout=15s CMD gix help + + ENTRYPOINT [ "/init" ] + + +FROM scratch + COPY --from=bootstrap_builder /usr/local/cargo/bin/gix /bin/ + COPY --from=bootstrap_builder /usr/local/cargo/bin/ein /bin/ + + ENTRYPOINT [ "/bin/gix" ] + + CMD [ "/bin/gix" ] + +# vi: nospell diff --git a/examples/log.rs b/examples/log.rs index be892f37e15..c143f8544cc 100644 --- a/examples/log.rs +++ b/examples/log.rs @@ -8,7 +8,7 @@ use clap::Parser; use gix::{ bstr::{BString, ByteSlice}, date::time::format, - traverse::commit::Sorting, + traverse::commit::simple::Sorting, }; fn main() { diff --git a/gitoxide-core/CHANGELOG.md b/gitoxide-core/CHANGELOG.md index 4edba6a85c9..0878ad87357 100644 --- a/gitoxide-core/CHANGELOG.md +++ b/gitoxide-core/CHANGELOG.md @@ -5,6 +5,135 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## 0.38.0 (2024-05-22) + +### Bug Fixes + + - more robustness in the face of a trampling-herd of threads loading a single index. + The motivating example is here: https://github.com/praetorian-inc/noseyparker/issues/179 + + Previously, it was possible for a trampling herd of threads to consolidate the + disk state. Most of them would be 'needs-init' threads which could notice that + the initialization already happened, and just use that. + + But a thread might be late for the party and somehow manages to not get any + newly loaded index, and thus tries to consolidate with what's on disk again. + Then it would again determine no change, and return nothing, causing the caller + to abort and not find objects it should find because it wouldn't see the index + that it should have seen. + + The reason the thread got into this mess is that the 'is-load-ongoing' flagging + was racy itself, so it would not wait for ongoing loads and just conclude nothing + happened. An extra delay (by yielding) now assures it either seees the loading state + and waits for it, sees the newly loaded indices. + + Note that this issue can be reproduced with: + + ``` + './target/release/gix -r repo-with-one-pack -t10 --trace odb stats --extra-header-lookup' + ``` + +### Commit Statistics + + + + - 7 commits contributed to the release over the course of 10 calendar days. + - 38 days passed between releases. + - 1 commit was understood as [conventional](https://www.conventionalcommits.org). + - 0 issues like '(#ID)' were seen in commit messages + +### Commit Details + + + +
view details + + * **Uncategorized** + - Adjust changelogs prior to release ([`9511416`](https://github.com/Byron/gitoxide/commit/9511416a6cd0c571233f958c165329c8705c2498)) + - Merge branch 'various-fixes' ([`d6cd449`](https://github.com/Byron/gitoxide/commit/d6cd44930fb204b06e2b70fc6965e7705530c47a)) + - Update dependencies ([`cd4de83`](https://github.com/Byron/gitoxide/commit/cd4de8327fc195eb862ab6e138f2315a87374f85)) + - Merge pull request from GHSA-7w47-3wg8-547c ([`79dce79`](https://github.com/Byron/gitoxide/commit/79dce79c62f6072aa2653780d590dc3993dfa401)) + - Adapt to changes in `gix-worktree` ([`1ca6a3c`](https://github.com/Byron/gitoxide/commit/1ca6a3ce22887c7eb42ec3e0a19f6e1202715745)) + - Merge branch 'status' ([`04ef31e`](https://github.com/Byron/gitoxide/commit/04ef31e9d6f5332d49037a5a4c248ebbb5aaf92b)) + - More robustness in the face of a trampling-herd of threads loading a single index. ([`addf446`](https://github.com/Byron/gitoxide/commit/addf446f052ff74edcdb083f2b2968b313daa940)) +
+ +## 0.37.0 (2024-04-13) + +### New Features + + - add `gix status --ignored` support + - add `gix status --index-worktree-renames` + This enables rename-tracking between worktree and index, something + that Git also doesn't do or doesn't do by default. + It is, however, available in `git2`. + - `gix status` with submodule and rewrite support. + Submodule changes are now picked up as long as the submodule is + in the index. + Further, it's possible to enable rename-tracking between + index and worktree separately. + - add `gix is-clean|is-changed` + It's a good way to compare the time it takes to run a full status + compared to a quick is-dirty check. + - `gix submodules list --dirty-suffix` for dirty-information + This is a submodule-centric and greatly simplified way of obtaining + describe information with dirty-suffix. + + Note that `status` information is also possible, but it seems + hard to display nicely, which this command isn't great at + in the first place. + - add `gix commit describe --dirty-suffix` + That way a suffix will be added depending on the dirty-state of the repository. + +### Bug Fixes (BREAKING) + + - Make `topo` more similar to `Ancestors`, but also rename `Ancestors` to `Simple` + +### Commit Statistics + + + + - 26 commits contributed to the release over the course of 34 calendar days. + - 47 days passed between releases. + - 7 commits were understood as [conventional](https://www.conventionalcommits.org). + - 1 unique issue was worked on: [#1330](https://github.com/Byron/gitoxide/issues/1330) + +### Commit Details + + + +
view details + + * **[#1330](https://github.com/Byron/gitoxide/issues/1330)** + - Fix unread-fields warnings on latest nightly ([`2e06a00`](https://github.com/Byron/gitoxide/commit/2e06a00800418a6f571ba1731ffe05074565af03)) + * **Uncategorized** + - Release gix-trace v0.1.9, gix-utils v0.1.12, gix-packetline-blocking v0.17.4, gix-filter v0.11.1, gix-fs v0.10.2, gix-traverse v0.39.0, gix-worktree-stream v0.12.0, gix-archive v0.12.0, gix-config v0.36.1, gix-url v0.27.3, gix-index v0.32.0, gix-worktree v0.33.0, gix-diff v0.43.0, gix-pathspec v0.7.3, gix-dir v0.4.0, gix-pack v0.50.0, gix-odb v0.60.0, gix-transport v0.42.0, gix-protocol v0.45.0, gix-status v0.9.0, gix-worktree-state v0.10.0, gix v0.62.0, gix-fsck v0.4.0, gitoxide-core v0.37.0, gitoxide v0.35.0, safety bump 14 crates ([`095c673`](https://github.com/Byron/gitoxide/commit/095c6739b2722a8b9af90776b435ef2da454c0e6)) + - Prepare changelogs prior to release ([`5755271`](https://github.com/Byron/gitoxide/commit/57552717f46f96c35ba4ddc0a64434354ef845e9)) + - Merge pull request #1341 from szepeviktor/typos ([`55f379b`](https://github.com/Byron/gitoxide/commit/55f379bc47065822d078393d83d30c0835a89782)) + - Fix typos ([`f72ecce`](https://github.com/Byron/gitoxide/commit/f72ecce45babcad2a0c9b73c79d01ff502907a57)) + - Merge branch 'add-topo-walk' ([`b590a9d`](https://github.com/Byron/gitoxide/commit/b590a9d2b6a273f76f0320d2b9fe1f679c08f549)) + - Adapt to changes in `gix-traverse` ([`1cfeb11`](https://github.com/Byron/gitoxide/commit/1cfeb11f1fe9ad9c7b9084840ed7f5c5877f2f9a)) + - Make `topo` more similar to `Ancestors`, but also rename `Ancestors` to `Simple` ([`2a9c178`](https://github.com/Byron/gitoxide/commit/2a9c178326b7f13ba6bc1f89fc2b9d9facbecf48)) + - Adapt to changes in `gix-traverse` ([`6154bf3`](https://github.com/Byron/gitoxide/commit/6154bf3a346d69f9749271d50e4f3aacdcbad4d0)) + - Merge branch 'status' ([`45edd2e`](https://github.com/Byron/gitoxide/commit/45edd2ea66035adf526cb2f617873dcba60a2a9a)) + - Adapt to changes in `gix-index` ([`1e1fce1`](https://github.com/Byron/gitoxide/commit/1e1fce11a968ebbcede1135ccbd0b03e749a1267)) + - Release gix-packetline v0.17.5, gix-transport v0.41.3, gix v0.61.1 ([`57579f1`](https://github.com/Byron/gitoxide/commit/57579f1ee4ef12c214db36325a2a0b2e8b2b14fd)) + - Release gix-actor v0.31.1, gix-object v0.42.1, gix-index v0.31.1, gix-pathspec v0.7.2, gix-dir v0.3.0, gix-status v0.8.0, gix v0.61.0, safety bump 2 crates ([`155cc45`](https://github.com/Byron/gitoxide/commit/155cc45730b259e662d7c4be42a469a3af3750e1)) + - Merge branch 'improvements-for-cargo' ([`41cd53e`](https://github.com/Byron/gitoxide/commit/41cd53e2af76e35e047aac4eca6324774df4cb50)) + - Adapt to changes in `gix` ([`eff82eb`](https://github.com/Byron/gitoxide/commit/eff82eb0c67a7e7153c6119623a8aec052661b7d)) + - Release gix-date v0.8.5, gix-hash v0.14.2, gix-trace v0.1.8, gix-utils v0.1.11, gix-features v0.38.1, gix-actor v0.31.0, gix-validate v0.8.4, gix-object v0.42.0, gix-path v0.10.7, gix-glob v0.16.2, gix-quote v0.4.12, gix-attributes v0.22.2, gix-command v0.3.6, gix-filter v0.11.0, gix-fs v0.10.1, gix-chunk v0.4.8, gix-commitgraph v0.24.2, gix-hashtable v0.5.2, gix-revwalk v0.13.0, gix-traverse v0.38.0, gix-worktree-stream v0.11.0, gix-archive v0.11.0, gix-config-value v0.14.6, gix-tempfile v13.1.1, gix-lock v13.1.1, gix-ref v0.43.0, gix-sec v0.10.6, gix-config v0.36.0, gix-prompt v0.8.4, gix-url v0.27.2, gix-credentials v0.24.2, gix-ignore v0.11.2, gix-bitmap v0.2.11, gix-index v0.31.0, gix-worktree v0.32.0, gix-diff v0.42.0, gix-discover v0.31.0, gix-pathspec v0.7.1, gix-dir v0.2.0, gix-macros v0.1.4, gix-mailmap v0.23.0, gix-negotiate v0.13.0, gix-pack v0.49.0, gix-odb v0.59.0, gix-packetline v0.17.4, gix-transport v0.41.2, gix-protocol v0.44.2, gix-revision v0.27.0, gix-refspec v0.23.0, gix-status v0.7.0, gix-submodule v0.10.0, gix-worktree-state v0.9.0, gix v0.60.0, safety bump 26 crates ([`b050327`](https://github.com/Byron/gitoxide/commit/b050327e76f234b19be921b78b7b28e034319fdb)) + - Merge branch 'status' ([`3e5c974`](https://github.com/Byron/gitoxide/commit/3e5c974dd62ac134711c6c2f5a5490187a6ea55e)) + - Add `gix status --format` to communicate the current format is very simple. ([`23bea36`](https://github.com/Byron/gitoxide/commit/23bea36f046a6f652cd1e06885ae132c85bb4f05)) + - Add `gix status --ignored` support ([`84c74ff`](https://github.com/Byron/gitoxide/commit/84c74ffa698d35f8395c63db6acd3d0e6700d07f)) + - Add `gix status --index-worktree-renames` ([`66e87cd`](https://github.com/Byron/gitoxide/commit/66e87cd31c060c3f97ac685ee0541c408f600362)) + - Fix lints for nightly, and clippy ([`f8ce3d0`](https://github.com/Byron/gitoxide/commit/f8ce3d0721b6a53713a9392f2451874f520bc44c)) + - `gix status` with submodule and rewrite support. ([`61c002b`](https://github.com/Byron/gitoxide/commit/61c002bc4ca5b5345c411e561fdcb492e7ae1d97)) + - Add `gix is-clean|is-changed` ([`98b3680`](https://github.com/Byron/gitoxide/commit/98b368095ec99d1bc287da7f9294a9fce424deed)) + - `gix submodules list --dirty-suffix` for dirty-information ([`afd20ca`](https://github.com/Byron/gitoxide/commit/afd20caadb40b6b793f2099b7232669f9a8f9086)) + - Add `gix commit describe --dirty-suffix` ([`58231b4`](https://github.com/Byron/gitoxide/commit/58231b418fa39ea122ef41bb7691289f5b0be855)) + - Adapt to changes in `gix` ([`2d40bdf`](https://github.com/Byron/gitoxide/commit/2d40bdf325ed5b09b0ef6fd21e8b3da47d710451)) +
+ ## 0.36.0 (2024-02-25) ### New Features @@ -18,7 +147,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - - 29 commits contributed to the release over the course of 57 calendar days. + - 30 commits contributed to the release over the course of 57 calendar days. - 58 days passed between releases. - 4 commits were understood as [conventional](https://www.conventionalcommits.org). - 0 issues like '(#ID)' were seen in commit messages @@ -30,6 +159,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
view details * **Uncategorized** + - Release gix-date v0.8.4, gix-utils v0.1.10, gix-actor v0.30.1, gix-object v0.41.1, gix-path v0.10.6, gix-glob v0.16.1, gix-quote v0.4.11, gix-attributes v0.22.1, gix-command v0.3.5, gix-filter v0.10.0, gix-commitgraph v0.24.1, gix-worktree-stream v0.10.0, gix-archive v0.10.0, gix-config-value v0.14.5, gix-ref v0.42.0, gix-sec v0.10.5, gix-config v0.35.0, gix-prompt v0.8.3, gix-url v0.27.1, gix-credentials v0.24.1, gix-ignore v0.11.1, gix-index v0.30.0, gix-worktree v0.31.0, gix-diff v0.41.0, gix-discover v0.30.0, gix-pathspec v0.7.0, gix-dir v0.1.0, gix-pack v0.48.0, gix-odb v0.58.0, gix-transport v0.41.1, gix-protocol v0.44.1, gix-revision v0.26.1, gix-refspec v0.22.1, gix-status v0.6.0, gix-submodule v0.9.0, gix-worktree-state v0.8.0, gix v0.59.0, gix-fsck v0.3.0, gitoxide-core v0.36.0, gitoxide v0.34.0, safety bump 10 crates ([`45b4470`](https://github.com/Byron/gitoxide/commit/45b447045bc826f252129c300c531acde2652c64)) - Prepare changelogs prior to release ([`f2e111f`](https://github.com/Byron/gitoxide/commit/f2e111f768fc1bc6182355261c20b63610cffec7)) - Merge branch 'status' ([`d53504a`](https://github.com/Byron/gitoxide/commit/d53504a1fad41cec7b6ca2a4abb7f185d8941e3f)) - Adapt to changes in `gix-dir` ([`aa7c190`](https://github.com/Byron/gitoxide/commit/aa7c1908b82e3e23859a4c663faa40ec54611919)) diff --git a/gitoxide-core/Cargo.toml b/gitoxide-core/Cargo.toml index b69fb946a2a..1a5458e12ba 100644 --- a/gitoxide-core/Cargo.toml +++ b/gitoxide-core/Cargo.toml @@ -2,7 +2,7 @@ name = "gitoxide-core" description = "The library implementing all capabilities of the gitoxide CLI" repository = "https://github.com/Byron/gitoxide" -version = "0.36.0" +version = "0.38.0" authors = ["Sebastian Thiel "] license = "MIT OR Apache-2.0" edition = "2021" @@ -23,7 +23,7 @@ estimate-hours = ["dep:fs-err", "dep:crossbeam-channel", "dep:smallvec"] query = ["dep:rusqlite"] ## Run algorithms on a corpus of repositories and store their results for later comparison and intelligence gathering. ## *Note that* `organize` we need for finding git repositories fast. -corpus = [ "dep:rusqlite", "dep:sysinfo", "organize", "dep:crossbeam-channel", "dep:serde_json", "dep:tracing-forest", "dep:tracing-subscriber", "tracing", "dep:parking_lot" ] +corpus = ["dep:rusqlite", "dep:sysinfo", "organize", "dep:crossbeam-channel", "dep:serde_json", "dep:tracing-forest", "dep:tracing-subscriber", "tracing", "dep:parking_lot"] ## The ability to create archives from virtual worktrees, similar to `git archive`. archive = ["dep:gix-archive-for-configuration-only", "gix/worktree-archive"] @@ -47,12 +47,12 @@ serde = ["gix/serde", "dep:serde_json", "dep:serde", "bytesize/serde"] [dependencies] # deselect everything else (like "performance") as this should be controllable by the parent application. -gix = { version = "^0.59.0", path = "../gix", default-features = false, features = ["blob-diff", "revision", "mailmap", "excludes", "attributes", "worktree-mutation", "credentials", "interrupt", "status", "dirwalk"] } -gix-pack-for-configuration-only = { package = "gix-pack", version = "^0.48.0", path = "../gix-pack", default-features = false, features = ["pack-cache-lru-dynamic", "pack-cache-lru-static", "generate", "streaming-input"] } -gix-transport-configuration-only = { package = "gix-transport", version = "^0.41.1", path = "../gix-transport", default-features = false } -gix-archive-for-configuration-only = { package = "gix-archive", version = "^0.10.0", path = "../gix-archive", optional = true, features = ["tar", "tar_gz"] } -gix-status = { version = "^0.6.0", path = "../gix-status" } -gix-fsck = { version = "^0.3.0", path = "../gix-fsck" } +gix = { version = "^0.63.0", path = "../gix", default-features = false, features = ["blob-diff", "revision", "mailmap", "excludes", "attributes", "worktree-mutation", "credentials", "interrupt", "status", "dirwalk"] } +gix-pack-for-configuration-only = { package = "gix-pack", version = "^0.51.0", path = "../gix-pack", default-features = false, features = ["pack-cache-lru-dynamic", "pack-cache-lru-static", "generate", "streaming-input"] } +gix-transport-configuration-only = { package = "gix-transport", version = "^0.42.1", path = "../gix-transport", default-features = false } +gix-archive-for-configuration-only = { package = "gix-archive", version = "^0.13.1", path = "../gix-archive", optional = true, features = ["tar", "tar_gz"] } +gix-status = { version = "^0.10.0", path = "../gix-status" } +gix-fsck = { version = "^0.4.0", path = "../gix-fsck" } serde = { version = "1.0.114", optional = true, default-features = false, features = ["derive"] } anyhow = "1.0.42" thiserror = "1.0.34" @@ -68,7 +68,7 @@ futures-io = { version = "0.3.16", optional = true } blocking = { version = "1.0.2", optional = true } # for 'organize' functionality -gix-url = { version = "^0.27.1", path = "../gix-url", optional = true } +gix-url = { version = "^0.27.3", path = "../gix-url", optional = true } jwalk = { version = "0.8.0", optional = true } # for 'hours' @@ -77,7 +77,7 @@ crossbeam-channel = { version = "0.5.6", optional = true } smallvec = { version = "1.10.0", optional = true } # for 'query' and 'corpus' -rusqlite = { version = "0.30.0", optional = true, features = ["bundled"] } +rusqlite = { version = "0.31.0", optional = true, features = ["bundled"] } # for 'corpus' parking_lot = { version = "0.12.1", optional = true } diff --git a/gitoxide-core/src/hours/mod.rs b/gitoxide-core/src/hours/mod.rs index 0418ef1f01e..4080c30c4a0 100644 --- a/gitoxide-core/src/hours/mod.rs +++ b/gitoxide-core/src/hours/mod.rs @@ -175,7 +175,7 @@ where } commit_idx += 1; } - Err(gix::traverse::commit::ancestors::Error::Find { .. }) => { + Err(gix::traverse::commit::simple::Error::Find { .. }) => { is_shallow = true; break; } diff --git a/gitoxide-core/src/index/information.rs b/gitoxide-core/src/index/information.rs index aa618ab10c3..4540e5bc832 100644 --- a/gitoxide-core/src/index/information.rs +++ b/gitoxide-core/src/index/information.rs @@ -6,6 +6,7 @@ pub struct Options { #[cfg(feature = "serde")] mod serde_only { + use gix::index::entry::Stage; mod ext { #[derive(serde::Serialize)] @@ -115,11 +116,10 @@ mod serde_only { let (mut intent_to_add, mut skip_worktree) = (0, 0); for entry in f.entries() { match entry.flags.stage() { - 0 => stage_0_merged += 1, - 1 => stage_1_base += 1, - 2 => stage_2_ours += 1, - 3 => stage_3_theirs += 1, - invalid => anyhow::bail!("Invalid stage {} encountered", invalid), + Stage::Unconflicted => stage_0_merged += 1, + Stage::Base => stage_1_base += 1, + Stage::Ours => stage_2_ours += 1, + Stage::Theirs => stage_3_theirs += 1, } match entry.mode { gix::index::entry::Mode::DIR => dir += 1, diff --git a/gitoxide-core/src/lib.rs b/gitoxide-core/src/lib.rs index 2cf788a994a..9ff7ab73760 100644 --- a/gitoxide-core/src/lib.rs +++ b/gitoxide-core/src/lib.rs @@ -84,3 +84,11 @@ pub use discover::discover; #[cfg(all(feature = "async-client", feature = "blocking-client"))] compile_error!("Cannot set both 'blocking-client' and 'async-client' features as they are mutually exclusive"); + +fn is_dir_to_mode(is_dir: bool) -> gix::index::entry::Mode { + if is_dir { + gix::index::entry::Mode::DIR + } else { + gix::index::entry::Mode::FILE + } +} diff --git a/gitoxide-core/src/pack/create.rs b/gitoxide-core/src/pack/create.rs index f8141e357d6..76bacd079c3 100644 --- a/gitoxide-core/src/pack/create.rs +++ b/gitoxide-core/src/pack/create.rs @@ -130,7 +130,7 @@ where .collect::, _>>()?; let handle = repo.objects.into_shared_arc().to_cache_arc(); let iter = Box::new( - traverse::commit::Ancestors::new(tips, traverse::commit::ancestors::State::default(), handle.clone()) + traverse::commit::Simple::new(tips, handle.clone()) .map(|res| res.map_err(|err| Box::new(err) as Box<_>).map(|c| c.id)) .inspect(move |_| progress.inc()), ); @@ -361,7 +361,7 @@ pub mod input_iteration { #[derive(Debug, thiserror::Error)] pub enum Error { #[error("input objects couldn't be iterated completely")] - Iteration(#[from] traverse::commit::ancestors::Error), + Iteration(#[from] traverse::commit::simple::Error), #[error("An error occurred while reading hashes from standard input")] InputLinesIo(#[from] std::io::Error), #[error("Could not decode hex hash provided on standard input")] diff --git a/gitoxide-core/src/query/engine/update.rs b/gitoxide-core/src/query/engine/update.rs index b5c6467e0d6..2e809f0e2b3 100644 --- a/gitoxide-core/src/query/engine/update.rs +++ b/gitoxide-core/src/query/engine/update.rs @@ -429,7 +429,7 @@ pub fn update( break; } } - Err(gix::traverse::commit::ancestors::Error::Find { .. }) => { + Err(gix::traverse::commit::simple::Error::Find { .. }) => { writeln!(err, "shallow repository - commit history is truncated").ok(); break; } diff --git a/gitoxide-core/src/repository/attributes/query.rs b/gitoxide-core/src/repository/attributes/query.rs index 09d1b0a0b48..9081eb9ed78 100644 --- a/gitoxide-core/src/repository/attributes/query.rs +++ b/gitoxide-core/src/repository/attributes/query.rs @@ -1,4 +1,4 @@ -use gix::repository::IndexPersistedOrInMemory; +use gix::worktree::IndexPersistedOrInMemory; use crate::OutputFormat; @@ -14,6 +14,7 @@ pub(crate) mod function { use gix::bstr::BStr; use crate::{ + is_dir_to_mode, repository::{ attributes::query::{attributes_cache, Options}, PathsOrPatterns, @@ -38,12 +39,12 @@ pub(crate) mod function { match input { PathsOrPatterns::Paths(paths) => { for path in paths { - let is_dir = gix::path::from_bstr(Cow::Borrowed(path.as_ref())) + let mode = gix::path::from_bstr(Cow::Borrowed(path.as_ref())) .metadata() .ok() - .map(|m| m.is_dir()); + .map(|m| is_dir_to_mode(m.is_dir())); - let entry = cache.at_entry(path.as_slice(), is_dir)?; + let entry = cache.at_entry(path.as_slice(), mode)?; if !entry.matching_attributes(&mut matches) { continue; } @@ -61,9 +62,9 @@ pub(crate) mod function { )?; let mut pathspec_matched_entry = false; if let Some(it) = pathspec.index_entries_with_paths(&index) { - for (path, _entry) in it { + for (path, entry) in it { pathspec_matched_entry = true; - let entry = cache.at_entry(path, Some(false))?; + let entry = cache.at_entry(path, entry.mode.into())?; if !entry.matching_attributes(&mut matches) { continue; } @@ -87,10 +88,10 @@ pub(crate) mod function { let path = pattern.path(); let entry = cache.at_entry( path, - Some( + Some(is_dir_to_mode( workdir.map_or(false, |wd| wd.join(gix::path::from_bstr(path)).is_dir()) || pattern.signature.contains(gix::pathspec::MagicSignature::MUST_BE_DIR), - ), + )), )?; if !entry.matching_attributes(&mut matches) { continue; diff --git a/gitoxide-core/src/repository/attributes/validate_baseline.rs b/gitoxide-core/src/repository/attributes/validate_baseline.rs index f2c92f71a30..686c668add6 100644 --- a/gitoxide-core/src/repository/attributes/validate_baseline.rs +++ b/gitoxide-core/src/repository/attributes/validate_baseline.rs @@ -192,7 +192,7 @@ pub(crate) mod function { ); for (rela_path, baseline) in rx_base { - let entry = cache.at_entry(rela_path.as_str(), Some(false))?; + let entry = cache.at_entry(rela_path.as_str(), None)?; match baseline { Baseline::Attribute { assignments: expected } => { entry.matching_attributes(&mut matches); @@ -262,6 +262,8 @@ pub(crate) mod function { } #[derive(Debug)] + // See note on `Mismatch` + #[allow(dead_code)] pub struct ExcludeLocation { pub line: usize, pub rela_source_file: String, @@ -269,6 +271,9 @@ pub(crate) mod function { } #[derive(Debug)] + // We debug-print this structure, which makes all fields 'used', but it doesn't count. + // TODO: find a way to not have to do more work, but make the warning go away. + #[allow(dead_code)] pub enum Mismatch { Attributes { actual: Vec, @@ -281,6 +286,8 @@ pub(crate) mod function { } #[derive(Debug)] + // See note on `Mismatch` + #[allow(dead_code)] pub struct ExcludeMatch { pub pattern: gix::glob::Pattern, pub source: Option, @@ -298,13 +305,13 @@ pub(crate) mod function { } fn parse_exclude(line: &str) -> Option<(String, Baseline)> { - let (left, value) = line.split_at(line.find(|c| c == '\t')?); + let (left, value) = line.split_at(line.find('\t')?); let value = &value[1..]; let location = if left == "::" { None } else { - let mut tokens = left.split(|b| b == ':'); + let mut tokens = left.split(':'); let source = tokens.next()?; let line_number: usize = tokens.next()?.parse().ok()?; let pattern = tokens.next()?; @@ -356,8 +363,8 @@ pub(crate) mod function { "unspecified" => StateRef::Unspecified, _ => StateRef::from_bytes(info.as_bytes()), }; - path = path.trim_end_matches(|b| b == ':'); - let attr = attr.trim_end_matches(|b| b == ':'); + path = path.trim_end_matches(':'); + let attr = attr.trim_end_matches(':'); let assignment = gix::attrs::AssignmentRef { name: gix::attrs::NameRef::try_from(attr.as_bytes().as_bstr()).ok()?, state, diff --git a/gitoxide-core/src/repository/clean.rs b/gitoxide-core/src/repository/clean.rs index fe91e3d2b38..b9bac90d9f3 100644 --- a/gitoxide-core/src/repository/clean.rs +++ b/gitoxide-core/src/repository/clean.rs @@ -60,7 +60,7 @@ pub(crate) mod function { let index = repo.index_or_empty()?; let pathspec_for_dirwalk = !pathspec_matches_result; let has_patterns = !patterns.is_empty(); - let mut collect = InterruptableCollect::default(); + let mut collect = InterruptibleCollect::default(); let collapse_directories = CollapseDirectory; let options = repo .dirwalk_options()? @@ -86,6 +86,7 @@ pub(crate) mod function { } else { Vec::new() }, + &gix::interrupt::IS_INTERRUPTED, options, &mut collect, )?; @@ -364,11 +365,11 @@ pub(crate) mod function { } #[derive(Default)] - struct InterruptableCollect { + struct InterruptibleCollect { inner: gix::dir::walk::delegate::Collect, } - impl gix::dir::walk::Delegate for InterruptableCollect { + impl gix::dir::walk::Delegate for InterruptibleCollect { fn emit(&mut self, entry: EntryRef<'_>, collapsed_directory_status: Option) -> walk::Action { let res = self.inner.emit(entry, collapsed_directory_status); if gix::interrupt::is_triggered() { diff --git a/gitoxide-core/src/repository/clone.rs b/gitoxide-core/src/repository/clone.rs index 9f09b71d55e..cf810deb389 100644 --- a/gitoxide-core/src/repository/clone.rs +++ b/gitoxide-core/src/repository/clone.rs @@ -6,6 +6,7 @@ pub struct Options { pub handshake_info: bool, pub no_tags: bool, pub shallow: gix::remote::fetch::Shallow, + pub ref_name: Option, } pub const PROGRESS_RANGE: std::ops::RangeInclusive = 1..=3; @@ -31,6 +32,7 @@ pub(crate) mod function { handshake_info, bare, no_tags, + ref_name, shallow, }: Options, ) -> anyhow::Result<()> @@ -75,6 +77,7 @@ pub(crate) mod function { } let (mut checkout, fetch_outcome) = prepare .with_shallow(shallow) + .with_ref_name(ref_name.as_ref())? .fetch_then_checkout(&mut progress, &gix::interrupt::IS_INTERRUPTED)?; let (repo, outcome) = if bare { diff --git a/gitoxide-core/src/repository/commit.rs b/gitoxide-core/src/repository/commit.rs index 17ce99e5f3c..e0070dc2ae3 100644 --- a/gitoxide-core/src/repository/commit.rs +++ b/gitoxide-core/src/repository/commit.rs @@ -52,6 +52,7 @@ pub fn describe( statistics, max_candidates, long_format, + dirty_suffix, }: describe::Options, ) -> Result<()> { repo.object_cache_size_if_unset(4 * 1024 * 1024); @@ -80,7 +81,7 @@ pub fn describe( writeln!(err, "traversed {} commits", resolution.outcome.commits_seen)?; } - let mut describe_id = resolution.format()?; + let mut describe_id = resolution.format_with_dirty_suffix(dirty_suffix)?; describe_id.long(long_format); writeln!(out, "{describe_id}")?; @@ -97,5 +98,6 @@ pub mod describe { pub long_format: bool, pub statistics: bool, pub max_candidates: usize, + pub dirty_suffix: Option, } } diff --git a/gitoxide-core/src/repository/commitgraph/list.rs b/gitoxide-core/src/repository/commitgraph/list.rs index 59e74a699cd..d9c024ed3f1 100644 --- a/gitoxide-core/src/repository/commitgraph/list.rs +++ b/gitoxide-core/src/repository/commitgraph/list.rs @@ -2,7 +2,7 @@ pub(crate) mod function { use std::{borrow::Cow, ffi::OsString}; use anyhow::{bail, Context}; - use gix::{prelude::ObjectIdExt, traverse::commit::Sorting}; + use gix::{prelude::ObjectIdExt, traverse::commit::simple::Sorting}; use crate::OutputFormat; diff --git a/gitoxide-core/src/repository/credential.rs b/gitoxide-core/src/repository/credential.rs index b9b93b9d057..c901f06f2ba 100644 --- a/gitoxide-core/src/repository/credential.rs +++ b/gitoxide-core/src/repository/credential.rs @@ -1,5 +1,3 @@ -use std::convert::TryInto; - #[derive(Debug, thiserror::Error)] enum Error { #[error(transparent)] diff --git a/gitoxide-core/src/repository/dirty.rs b/gitoxide-core/src/repository/dirty.rs new file mode 100644 index 00000000000..8981199d532 --- /dev/null +++ b/gitoxide-core/src/repository/dirty.rs @@ -0,0 +1,32 @@ +use crate::OutputFormat; +use anyhow::bail; + +pub enum Mode { + IsClean, + IsDirty, +} + +pub fn check( + repo: gix::Repository, + mode: Mode, + out: &mut dyn std::io::Write, + format: OutputFormat, +) -> anyhow::Result<()> { + if format != OutputFormat::Human { + bail!("JSON output isn't implemented yet"); + } + let is_dirty = repo.is_dirty()?; + let res = match (is_dirty, mode) { + (false, Mode::IsClean) => Ok("The repository is clean"), + (true, Mode::IsClean) => Err("The repository has changes"), + (false, Mode::IsDirty) => Err("The repository is clean"), + (true, Mode::IsDirty) => Ok("The repository has changes"), + }; + + let suffix = "(not counting untracked files)"; + match res { + Ok(msg) => writeln!(out, "{msg} {suffix}")?, + Err(msg) => bail!("{msg} {suffix}"), + } + Ok(()) +} diff --git a/gitoxide-core/src/repository/exclude.rs b/gitoxide-core/src/repository/exclude.rs index ac837fe0303..a0cd212d08e 100644 --- a/gitoxide-core/src/repository/exclude.rs +++ b/gitoxide-core/src/repository/exclude.rs @@ -3,7 +3,7 @@ use std::{borrow::Cow, io}; use anyhow::bail; use gix::bstr::BStr; -use crate::{repository::PathsOrPatterns, OutputFormat}; +use crate::{is_dir_to_mode, repository::PathsOrPatterns, OutputFormat}; pub mod query { use std::ffi::OsString; @@ -44,11 +44,11 @@ pub fn query( match input { PathsOrPatterns::Paths(paths) => { for path in paths { - let is_dir = gix::path::from_bstr(Cow::Borrowed(path.as_ref())) + let mode = gix::path::from_bstr(Cow::Borrowed(path.as_ref())) .metadata() .ok() - .map(|m| m.is_dir()); - let entry = cache.at_entry(path.as_slice(), is_dir)?; + .map(|m| is_dir_to_mode(m.is_dir())); + let entry = cache.at_entry(path.as_slice(), mode)?; let match_ = entry .matching_exclude_pattern() .and_then(|m| (show_ignore_patterns || !m.pattern.is_negative()).then_some(m)); @@ -66,9 +66,9 @@ pub fn query( )?; if let Some(it) = pathspec.index_entries_with_paths(&index) { - for (path, _entry) in it { + for (path, entry) in it { pathspec_matched_something = true; - let entry = cache.at_entry(path, Some(false))?; + let entry = cache.at_entry(path, entry.mode.into())?; let match_ = entry .matching_exclude_pattern() .and_then(|m| (show_ignore_patterns || !m.pattern.is_negative()).then_some(m)); @@ -92,10 +92,10 @@ pub fn query( let path = pattern.path(); let entry = cache.at_entry( path, - Some( + Some(is_dir_to_mode( workdir.map_or(false, |wd| wd.join(gix::path::from_bstr(path)).is_dir()) || pattern.signature.contains(gix::pathspec::MagicSignature::MUST_BE_DIR), - ), + )), )?; let match_ = entry .matching_exclude_pattern() diff --git a/gitoxide-core/src/repository/index/entries.rs b/gitoxide-core/src/repository/index/entries.rs index b4f335e6ae3..20a21e3a38b 100644 --- a/gitoxide-core/src/repository/index/entries.rs +++ b/gitoxide-core/src/repository/index/entries.rs @@ -23,13 +23,15 @@ pub(crate) mod function { io::{BufWriter, Write}, }; + use gix::index::entry::Stage; use gix::{ bstr::{BStr, BString}, - repository::IndexPersistedOrInMemory, + worktree::IndexPersistedOrInMemory, Repository, }; use crate::{ + is_dir_to_mode, repository::index::entries::{Attributes, Options}, OutputFormat, }; @@ -173,7 +175,7 @@ pub(crate) mod function { } // The user doesn't want attributes, so we set the cache position on demand only None => cache - .at_entry(rela_path, Some(is_dir)) + .at_entry(rela_path, Some(is_dir_to_mode(is_dir))) .ok() .map(|platform| platform.matching_attributes(out)) .unwrap_or_default(), @@ -317,7 +319,7 @@ pub(crate) mod function { #[cfg(feature = "serde")] fn to_json( - mut out: &mut impl std::io::Write, + out: &mut impl std::io::Write, index: &gix::index::File, entry: &gix::index::Entry, attrs: Option, @@ -336,7 +338,7 @@ pub(crate) mod function { } serde_json::to_writer( - &mut out, + &mut *out, &Entry { stat: &entry.stat, hex_id: entry.id.to_hex().to_string(), @@ -392,11 +394,10 @@ pub(crate) mod function { out, "{} {}{:?} {} {}{}{}", match entry.flags.stage() { - 0 => " ", - 1 => "BASE ", - 2 => "OURS ", - 3 => "THEIRS ", - _ => "UNKNOWN", + Stage::Unconflicted => " ", + Stage::Base => "BASE ", + Stage::Ours => "OURS ", + Stage::Theirs => "THEIRS ", }, if entry.flags.is_empty() { "".to_string() diff --git a/gitoxide-core/src/repository/mod.rs b/gitoxide-core/src/repository/mod.rs index 35d0c156a95..55816dcb774 100644 --- a/gitoxide-core/src/repository/mod.rs +++ b/gitoxide-core/src/repository/mod.rs @@ -26,6 +26,7 @@ pub use credential::function as credential; pub mod attributes; #[cfg(feature = "clean")] pub mod clean; +pub mod dirty; #[cfg(feature = "clean")] pub use clean::function::clean; #[cfg(feature = "blocking-client")] diff --git a/gitoxide-core/src/repository/odb.rs b/gitoxide-core/src/repository/odb.rs index 85c35618db2..e8cb1f7a0b1 100644 --- a/gitoxide-core/src/repository/odb.rs +++ b/gitoxide-core/src/repository/odb.rs @@ -1,4 +1,5 @@ use std::io; +use std::sync::atomic::Ordering; use anyhow::bail; @@ -50,6 +51,8 @@ pub mod statistics { pub struct Options { pub format: OutputFormat, pub thread_limit: Option, + /// A debug-flag that triggers looking up the headers of all objects again, but without indices preloaded + pub extra_header_lookup: bool, } } @@ -59,7 +62,11 @@ pub fn statistics( mut progress: impl gix::Progress, out: impl io::Write, mut err: impl io::Write, - statistics::Options { format, thread_limit }: statistics::Options, + statistics::Options { + format, + thread_limit, + extra_header_lookup, + }: statistics::Options, ) -> anyhow::Result<()> { use bytesize::ByteSize; use gix::odb::{find, HeaderExt}; @@ -76,6 +83,10 @@ pub fn statistics( #[cfg_attr(feature = "serde", derive(serde::Serialize))] #[derive(Default)] struct Statistics { + /// All objects that were used to produce these statistics. + /// Only `Some` if we are doing an extra round of header queries on a repository without loaded indices. + #[cfg_attr(feature = "serde", serde(skip_serializing))] + ids: Option>, total_objects: usize, loose_objects: usize, packed_objects: usize, @@ -135,14 +146,17 @@ pub fn statistics( } impl gix::parallel::Reduce for Reduce { - type Input = Result, anyhow::Error>; + type Input = Result, anyhow::Error>; type FeedProduce = (); type Output = Statistics; type Error = anyhow::Error; fn feed(&mut self, items: Self::Input) -> Result { - for item in items? { + for (id, item) in items? { self.stats.consume(item); + if let Some(ids) = self.stats.ids.as_mut() { + ids.push(id); + } } Ok(()) } @@ -154,9 +168,9 @@ pub fn statistics( } let cancelled = || anyhow::anyhow!("Cancelled by user"); - let object_ids = repo.objects.store_ref().iter()?.filter_map(Result::ok); + let object_ids = repo.objects.iter()?.filter_map(Result::ok); let chunk_size = 1_000; - let stats = if gix::parallel::num_threads(thread_limit) > 1 { + let mut stats = if gix::parallel::num_threads(thread_limit) > 1 { gix::parallel::in_parallel( gix::interrupt::Iter::new( gix::features::iter::Chunks { @@ -166,19 +180,30 @@ pub fn statistics( cancelled, ), thread_limit, - move |_| (repo.objects.clone().into_inner(), counter), + { + let objects = repo.objects.clone(); + move |_| (objects.clone().into_inner(), counter) + }, |ids, (handle, counter)| { let ids = ids?; - counter.fetch_add(ids.len(), std::sync::atomic::Ordering::Relaxed); + counter.fetch_add(ids.len(), Ordering::Relaxed); let out = ids .into_iter() - .map(|id| handle.header(id)) + .map(|id| handle.header(id).map(|hdr| (id, hdr))) .collect::, _>>()?; Ok(out) }, - Reduce::default(), + Reduce { + stats: Statistics { + ids: extra_header_lookup.then(Vec::new), + ..Default::default() + }, + }, )? } else { + if extra_header_lookup { + bail!("extra-header-lookup is only meaningful in threaded mode"); + } let mut stats = Statistics::default(); for (count, id) in object_ids.enumerate() { @@ -193,6 +218,39 @@ pub fn statistics( progress.show_throughput(start); + if let Some(mut ids) = stats.ids.take() { + // Critical to re-open the repo to assure we don't have any ODB state and start fresh. + let start = std::time::Instant::now(); + let repo = gix::open_opts(repo.git_dir(), repo.open_options().to_owned())?; + progress.set_name("re-counting".into()); + progress.init(Some(ids.len()), gix::progress::count("objects")); + let counter = progress.counter(); + counter.store(0, Ordering::Relaxed); + let errors = gix::parallel::in_parallel_with_slice( + &mut ids, + thread_limit, + { + let objects = repo.objects.clone(); + move |_| (objects.clone().into_inner(), counter, false) + }, + |id, (odb, counter, has_error), _threads_left, _stop_everything| -> anyhow::Result<()> { + counter.fetch_add(1, Ordering::Relaxed); + if let Err(_err) = odb.header(id) { + *has_error = true; + gix::trace::error!(err = ?_err, "Object that is known to be present wasn't found"); + } + Ok(()) + }, + || Some(std::time::Duration::from_millis(100)), + |(_, _, has_error)| has_error, + )?; + + progress.show_throughput(start); + if errors.contains(&true) { + bail!("At least one object couldn't be looked up even though it must exist"); + } + } + #[cfg(feature = "serde")] { serde_json::to_writer_pretty(out, &stats)?; diff --git a/gitoxide-core/src/repository/revision/list.rs b/gitoxide-core/src/repository/revision/list.rs index ab2bb2a60ed..7050bb6a667 100644 --- a/gitoxide-core/src/repository/revision/list.rs +++ b/gitoxide-core/src/repository/revision/list.rs @@ -17,7 +17,7 @@ pub const PROGRESS_RANGE: std::ops::RangeInclusive = 0..=2; pub(crate) mod function { use anyhow::{bail, Context}; - use gix::{hashtable::HashMap, traverse::commit::Sorting, Progress}; + use gix::{hashtable::HashMap, traverse::commit::simple::Sorting, Progress}; use layout::{ backends::svg::SVGWriter, core::{base::Orientation, geometry::Point, style::StyleAttr}, diff --git a/gitoxide-core/src/repository/revision/resolve.rs b/gitoxide-core/src/repository/revision/resolve.rs index b4494e9e046..6e8a2b1ad65 100644 --- a/gitoxide-core/src/repository/revision/resolve.rs +++ b/gitoxide-core/src/repository/revision/resolve.rs @@ -127,11 +127,10 @@ pub(crate) mod function { } gix::object::Kind::Blob if cache.is_some() && spec.path_and_mode().is_some() => { let (path, mode) = spec.path_and_mode().expect("is present"); - let is_dir = Some(mode.is_tree()); match cache.expect("is some") { (BlobFormat::Git, _) => unreachable!("no need for a cache when querying object db"), (BlobFormat::Worktree, cache) => { - let platform = cache.attr_stack.at_entry(path, is_dir, &repo.objects)?; + let platform = cache.attr_stack.at_entry(path, Some(mode.into()), &repo.objects)?; let object = id.object()?; let mut converted = cache.filter.worktree_filter.convert_to_worktree( &object.data, diff --git a/gitoxide-core/src/repository/status.rs b/gitoxide-core/src/repository/status.rs index ee36b1b1f3f..d3180f9c251 100644 --- a/gitoxide-core/src/repository/status.rs +++ b/gitoxide-core/src/repository/status.rs @@ -1,12 +1,8 @@ -use anyhow::{bail, Context}; -use gix::bstr::ByteSlice; -use gix::{ - bstr::{BStr, BString}, - index::Entry, - Progress, -}; -use gix_status::index_as_worktree::{traits::FastEq, Change, Conflict, EntryStatus}; -use std::path::{Path, PathBuf}; +use anyhow::bail; +use gix::bstr::{BStr, BString}; +use gix::status::index_worktree::iter::Item; +use gix_status::index_as_worktree::{Change, Conflict, EntryStatus}; +use std::path::Path; use crate::OutputFormat; @@ -17,226 +13,196 @@ pub enum Submodules { RefChange, /// See if there are worktree modifications compared to the index, but do not check for untracked files. Modifications, + /// Ignore all submodule changes. + None, +} + +#[derive(Copy, Clone)] +pub enum Ignored { + Collapsed, + Matching, +} + +#[derive(Copy, Clone)] +pub enum Format { + Simplified, + PorcelainV2, } pub struct Options { - pub format: OutputFormat, - pub submodules: Submodules, + pub ignored: Option, + pub format: Format, + pub output_format: OutputFormat, + pub submodules: Option, pub thread_limit: Option, pub statistics: bool, pub allow_write: bool, + pub index_worktree_renames: Option, } pub fn show( repo: gix::Repository, pathspecs: Vec, - out: impl std::io::Write, + mut out: impl std::io::Write, mut err: impl std::io::Write, - mut progress: impl gix::NestedProgress, + mut progress: impl gix::NestedProgress + 'static, Options { + ignored, format, - // TODO: implement this - submodules: _, + output_format, + submodules, thread_limit, allow_write, statistics, + index_worktree_renames, }: Options, ) -> anyhow::Result<()> { - if format != OutputFormat::Human { + if output_format != OutputFormat::Human { bail!("Only human format is supported right now"); } - let mut index = repo.index_or_empty()?; - let index = gix::threading::make_mut(&mut index); - let mut progress = progress.add_child("traverse index"); - let start = std::time::Instant::now(); - let stack = repo - .attributes_only( - index, - gix::worktree::stack::state::attributes::Source::WorktreeThenIdMapping, - )? - .detach(); - let pathspec = gix::Pathspec::new(&repo, false, pathspecs.iter().map(|p| p.as_bstr()), true, || { - Ok(stack.clone()) - })?; - let options = gix_status::index_as_worktree::Options { - fs: repo.filesystem_options()?, - thread_limit, - stat: repo.stat_options()?, - }; - let prefix = repo.prefix()?.unwrap_or(Path::new("")); - let mut printer = Printer { - out, - changes: Vec::new(), - prefix: prefix.to_owned(), - }; - let filter_pipeline = repo - .filter_pipeline(Some(gix::hash::ObjectId::empty_tree(repo.object_hash())))? - .0 - .into_parts() - .0; - let ctx = gix_status::index_as_worktree::Context { - pathspec: pathspec.into_parts().0, - stack, - filter: filter_pipeline, - should_interrupt: &gix::interrupt::IS_INTERRUPTED, - }; - let mut collect = gix::dir::walk::delegate::Collect::default(); - let (outcome, walk_outcome) = gix::features::parallel::threads(|scope| -> anyhow::Result<_> { - // TODO: it's either this, or not running both in parallel and setting UPTODATE flags whereever - // there is no modification. This can save disk queries as dirwalk can then trust what's in - // the index regarding the type. - // NOTE: collect here as rename-tracking needs that anyway. - let walk_outcome = gix::features::parallel::build_thread() - .name("gix status::dirwalk".into()) - .spawn_scoped(scope, { - let repo = repo.clone().into_sync(); - let index = &index; - let collect = &mut collect; - move || -> anyhow::Result<_> { - let repo = repo.to_thread_local(); - let outcome = repo.dirwalk( - index, - pathspecs, - repo.dirwalk_options()? - .emit_untracked(gix::dir::walk::EmissionMode::CollapseDirectory), - collect, - )?; - Ok(outcome.dirwalk) - } - })?; - - let outcome = gix_status::index_as_worktree( - index, - repo.work_dir() - .context("This operation cannot be run on a bare repository")?, - &mut printer, - FastEq, - Submodule, - repo.objects.clone().into_arc()?, - &mut progress, - ctx, - options, - )?; - - let walk_outcome = walk_outcome.join().expect("no panic")?; - Ok((outcome, walk_outcome)) - })?; - - for entry in collect - .into_entries_by_path() - .into_iter() - .filter_map(|(entry, dir_status)| dir_status.is_none().then_some(entry)) - { - writeln!( - printer.out, - "{status: >3} {rela_path}", - status = "?", - rela_path = gix::path::relativize_with_prefix(&gix::path::from_bstr(entry.rela_path), prefix).display() - )?; + if !matches!(format, Format::Simplified) { + bail!("Only the simplified format is currently implemented"); } - if outcome.entries_to_update != 0 && allow_write { - { - let entries = index.entries_mut(); - for (entry_index, change) in printer.changes { - let entry = &mut entries[entry_index]; - match change { - ApplyChange::SetSizeToZero => { - entry.stat.size = 0; - } - ApplyChange::NewStat(new_stat) => { - entry.stat = new_stat; + let start = std::time::Instant::now(); + let prefix = repo.prefix()?.unwrap_or(Path::new("")); + let index_progress = progress.add_child("traverse index"); + let mut iter = repo + .status(index_progress)? + .should_interrupt_shared(&gix::interrupt::IS_INTERRUPTED) + .index_worktree_options_mut(|opts| { + if let Some((opts, ignored)) = opts.dirwalk_options.as_mut().zip(ignored) { + opts.set_emit_ignored(Some(match ignored { + Ignored::Collapsed => gix::dir::walk::EmissionMode::CollapseDirectory, + Ignored::Matching => gix::dir::walk::EmissionMode::Matching, + })); + } + opts.rewrites = index_worktree_renames.map(|percentage| gix::diff::Rewrites { + copies: None, + percentage: Some(percentage), + limit: 0, + }); + if opts.rewrites.is_some() { + if let Some(opts) = opts.dirwalk_options.as_mut() { + opts.set_emit_untracked(gix::dir::walk::EmissionMode::Matching); + if ignored.is_some() { + opts.set_emit_ignored(Some(gix::dir::walk::EmissionMode::Matching)); } } } + opts.thread_limit = thread_limit; + opts.sorting = Some(gix::status::plumbing::index_as_worktree_with_renames::Sorting::ByPathCaseSensitive); + }) + .index_worktree_submodules(match submodules { + Some(mode) => { + let ignore = match mode { + Submodules::All => gix::submodule::config::Ignore::None, + Submodules::RefChange => gix::submodule::config::Ignore::Dirty, + Submodules::Modifications => gix::submodule::config::Ignore::Untracked, + Submodules::None => gix::submodule::config::Ignore::All, + }; + gix::status::Submodule::Given { + ignore, + check_dirty: false, + } + } + None => gix::status::Submodule::AsConfigured { check_dirty: false }, + }) + .into_index_worktree_iter(pathspecs)?; + + for item in iter.by_ref() { + let item = item?; + match item { + Item::Modification { + entry: _, + entry_index: _, + rela_path, + status, + } => print_index_entry_status(&mut out, prefix, rela_path.as_ref(), status)?, + Item::DirectoryContents { + entry, + collapsed_directory_status, + } => { + if collapsed_directory_status.is_none() { + writeln!( + out, + "{status: >3} {rela_path}{slash}", + status = "?", + rela_path = + gix::path::relativize_with_prefix(&gix::path::from_bstr(entry.rela_path), prefix).display(), + slash = if entry.disk_kind.unwrap_or(gix::dir::entry::Kind::File).is_dir() { + "/" + } else { + "" + } + )?; + } + } + Item::Rewrite { + source, + dirwalk_entry, + copy: _, // TODO: how to visualize copies? + .. + } => { + // TODO: handle multi-status characters, there can also be modifications at the same time as determined by their ID and potentially diffstats. + writeln!( + out, + "{status: >3} {source_rela_path} → {dest_rela_path}", + status = "R", + source_rela_path = + gix::path::relativize_with_prefix(&gix::path::from_bstr(source.rela_path()), prefix).display(), + dest_rela_path = gix::path::relativize_with_prefix( + &gix::path::from_bstr(dirwalk_entry.rela_path.as_ref()), + prefix + ) + .display(), + )?; + } } - index.write(gix::index::write::Options { - extensions: Default::default(), - skip_hash: false, // TODO: make this based on configuration - })?; } - - if statistics { - writeln!(err, "{outcome:#?}").ok(); - writeln!(err, "{walk_outcome:#?}").ok(); + if gix::interrupt::is_triggered() { + bail!("interrupted by user"); } - writeln!(err, "\nhead -> index and untracked files aren't implemented yet")?; - progress.show_throughput(start); - Ok(()) -} - -#[derive(Clone)] -struct Submodule; - -impl gix_status::index_as_worktree::traits::SubmoduleStatus for Submodule { - type Output = (); - type Error = std::convert::Infallible; + let out = iter.outcome_mut().expect("successful iteration has outcome"); - fn status(&mut self, _entry: &Entry, _rela_path: &BStr) -> Result, Self::Error> { - Ok(None) + if out.has_changes() && allow_write { + out.write_changes().transpose()?; } -} - -struct Printer { - out: W, - changes: Vec<(usize, ApplyChange)>, - prefix: PathBuf, -} - -enum ApplyChange { - SetSizeToZero, - NewStat(gix::index::entry::Stat), -} - -impl<'index, W> gix_status::index_as_worktree::VisitEntry<'index> for Printer -where - W: std::io::Write, -{ - type ContentChange = (); - type SubmoduleStatus = (); - fn visit_entry( - &mut self, - _entries: &'index [Entry], - _entry: &'index Entry, - entry_index: usize, - rela_path: &'index BStr, - status: EntryStatus, - ) { - self.visit_inner(entry_index, rela_path, status).ok(); + if statistics { + writeln!(err, "{outcome:#?}", outcome = out.index_worktree).ok(); } + + writeln!(err, "\nhead -> index isn't implemented yet")?; + progress.init(Some(out.index.entries().len()), gix::progress::count("files")); + progress.set(out.index.entries().len()); + progress.show_throughput(start); + Ok(()) } -impl Printer { - fn visit_inner(&mut self, entry_index: usize, rela_path: &BStr, status: EntryStatus<()>) -> std::io::Result<()> { - let char_storage; - let status = match status { - EntryStatus::Conflict(conflict) => as_str(conflict), - EntryStatus::Change(change) => { - if matches!( - change, - Change::Modification { - set_entry_stat_size_zero: true, - .. - } - ) { - self.changes.push((entry_index, ApplyChange::SetSizeToZero)) - } - char_storage = change_to_char(&change); - std::str::from_utf8(std::slice::from_ref(&char_storage)).expect("valid ASCII") - } - EntryStatus::NeedsUpdate(stat) => { - self.changes.push((entry_index, ApplyChange::NewStat(stat))); - return Ok(()); - } - EntryStatus::IntentToAdd => "A", - }; +fn print_index_entry_status( + out: &mut dyn std::io::Write, + prefix: &Path, + rela_path: &BStr, + status: EntryStatus<(), gix::submodule::Status>, +) -> std::io::Result<()> { + let char_storage; + let status = match status { + EntryStatus::Conflict(conflict) => as_str(conflict), + EntryStatus::Change(change) => { + char_storage = change_to_char(&change); + std::str::from_utf8(std::slice::from_ref(&char_storage)).expect("valid ASCII") + } + EntryStatus::NeedsUpdate(_stat) => { + return Ok(()); + } + EntryStatus::IntentToAdd => "A", + }; - let rela_path = gix::path::from_bstr(rela_path); - let display_path = gix::path::relativize_with_prefix(&rela_path, &self.prefix); - writeln!(&mut self.out, "{status: >3} {}", display_path.display()) - } + let rela_path = gix::path::from_bstr(rela_path); + let display_path = gix::path::relativize_with_prefix(&rela_path, prefix); + writeln!(out, "{status: >3} {}", display_path.display()) } fn as_str(c: Conflict) -> &'static str { @@ -251,7 +217,7 @@ fn as_str(c: Conflict) -> &'static str { } } -fn change_to_char(change: &Change<()>) -> u8 { +fn change_to_char(change: &Change<(), gix::submodule::Status>) -> u8 { // Known status letters: https://github.com/git/git/blob/6807fcfedab84bc8cd0fbf721bc13c4e68cda9ae/diff.h#L613 match change { Change::Removed => b'D', diff --git a/gitoxide-core/src/repository/submodule.rs b/gitoxide-core/src/repository/submodule.rs index c8e8c45eaa2..12ef64870e8 100644 --- a/gitoxide-core/src/repository/submodule.rs +++ b/gitoxide-core/src/repository/submodule.rs @@ -3,7 +3,12 @@ use gix::{commit::describe::SelectRef, prelude::ObjectIdExt, Repository, Submodu use crate::OutputFormat; -pub fn list(repo: Repository, mut out: impl std::io::Write, format: OutputFormat) -> anyhow::Result<()> { +pub fn list( + repo: Repository, + mut out: impl std::io::Write, + format: OutputFormat, + dirty_suffix: Option, +) -> anyhow::Result<()> { if format != OutputFormat::Human { bail!("Only human output is supported for now") } @@ -12,12 +17,12 @@ pub fn list(repo: Repository, mut out: impl std::io::Write, format: OutputFormat return Ok(()); }; for sm in submodules { - print_sm(sm, &mut out)?; + print_sm(sm, dirty_suffix.as_deref(), &mut out)?; } Ok(()) } -fn print_sm(sm: Submodule<'_>, out: &mut impl std::io::Write) -> anyhow::Result<()> { +fn print_sm(sm: Submodule<'_>, dirty_suffix: Option<&str>, out: &mut impl std::io::Write) -> anyhow::Result<()> { let _span = gix::trace::coarse!("print_sm", path = ?sm.path()); let state = sm.state()?; let mut sm_repo = sm.open()?; @@ -48,7 +53,10 @@ fn print_sm(sm: Submodule<'_>, out: &mut impl std::io::Write) -> anyhow::Result< repo.head_commit()? .describe() .names(SelectRef::AllRefs) - .format()? + .id_as_fallback(true) + .try_resolve()? + .expect("resolution present if ID can be used as fallback") + .format_with_dirty_suffix(dirty_suffix.map(ToOwned::to_owned))? .to_string() } None => { diff --git a/gix-actor/CHANGELOG.md b/gix-actor/CHANGELOG.md index 7290079d9b9..103319ef7e1 100644 --- a/gix-actor/CHANGELOG.md +++ b/gix-actor/CHANGELOG.md @@ -5,6 +5,100 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## 0.31.2 (2024-05-22) + +A maintenance release without user-facing changes. + +### Commit Statistics + + + + - 2 commits contributed to the release over the course of 8 calendar days. + - 65 days passed between releases. + - 0 commits were understood as [conventional](https://www.conventionalcommits.org). + - 0 issues like '(#ID)' were seen in commit messages + +### Commit Details + + + +
view details + + * **Uncategorized** + - Adjust changelogs prior to release ([`9511416`](https://github.com/Byron/gitoxide/commit/9511416a6cd0c571233f958c165329c8705c2498)) + - Release gix-date v0.8.6 ([`d3588ca`](https://github.com/Byron/gitoxide/commit/d3588ca4fe0364c88e42cdac24ceae548355d99d)) +
+ +## 0.31.1 (2024-03-18) + +### Bug Fixes + + - allow parsing signatures with trailing numbers in offset + That way this won't fail, but *just* silently degenerates information. + + The idea is that during FSCK, objects should be decoded and then + re-encoded to see if they are still the same. If not, this means + some leniency kicked in. + + Maybe for FSCK, there would also have to be a refactor so there + is a lenient and strict parsing mode. One problem at a time. + +### Commit Statistics + + + + - 4 commits contributed to the release. + - 3 days passed between releases. + - 1 commit was understood as [conventional](https://www.conventionalcommits.org). + - 1 unique issue was worked on: [#1322](https://github.com/Byron/gitoxide/issues/1322) + +### Commit Details + + + +
view details + + * **[#1322](https://github.com/Byron/gitoxide/issues/1322)** + - Allow parsing signatures with trailing numbers in offset ([`741e373`](https://github.com/Byron/gitoxide/commit/741e3739ebd4bf48c3ef94b87ccce7602cb7cc2f)) + * **Uncategorized** + - Release gix-actor v0.31.1, gix-object v0.42.1, gix-index v0.31.1, gix-pathspec v0.7.2, gix-dir v0.3.0, gix-status v0.8.0, gix v0.61.0, safety bump 2 crates ([`155cc45`](https://github.com/Byron/gitoxide/commit/155cc45730b259e662d7c4be42a469a3af3750e1)) + - Prepare changelog prior to release ([`129ba3d`](https://github.com/Byron/gitoxide/commit/129ba3deccc9ada0dc571466458845939502763d)) + - Merge branch 'improvements-for-cargo' ([`41cd53e`](https://github.com/Byron/gitoxide/commit/41cd53e2af76e35e047aac4eca6324774df4cb50)) +
+ +## 0.31.0 (2024-03-14) + + + +### Chore (BREAKING) + + - update to `winnow` 0.6 + This upgrade is breaking enough and slipped through the radar the last time, + leading to a now-yanked release that breaks `gix` v0.58. + +### Commit Statistics + + + + - 5 commits contributed to the release over the course of 17 calendar days. + - 18 days passed between releases. + - 1 commit was understood as [conventional](https://www.conventionalcommits.org). + - 0 issues like '(#ID)' were seen in commit messages + +### Commit Details + + + +
view details + + * **Uncategorized** + - Release gix-date v0.8.5, gix-hash v0.14.2, gix-trace v0.1.8, gix-utils v0.1.11, gix-features v0.38.1, gix-actor v0.31.0, gix-validate v0.8.4, gix-object v0.42.0, gix-path v0.10.7, gix-glob v0.16.2, gix-quote v0.4.12, gix-attributes v0.22.2, gix-command v0.3.6, gix-filter v0.11.0, gix-fs v0.10.1, gix-chunk v0.4.8, gix-commitgraph v0.24.2, gix-hashtable v0.5.2, gix-revwalk v0.13.0, gix-traverse v0.38.0, gix-worktree-stream v0.11.0, gix-archive v0.11.0, gix-config-value v0.14.6, gix-tempfile v13.1.1, gix-lock v13.1.1, gix-ref v0.43.0, gix-sec v0.10.6, gix-config v0.36.0, gix-prompt v0.8.4, gix-url v0.27.2, gix-credentials v0.24.2, gix-ignore v0.11.2, gix-bitmap v0.2.11, gix-index v0.31.0, gix-worktree v0.32.0, gix-diff v0.42.0, gix-discover v0.31.0, gix-pathspec v0.7.1, gix-dir v0.2.0, gix-macros v0.1.4, gix-mailmap v0.23.0, gix-negotiate v0.13.0, gix-pack v0.49.0, gix-odb v0.59.0, gix-packetline v0.17.4, gix-transport v0.41.2, gix-protocol v0.44.2, gix-revision v0.27.0, gix-refspec v0.23.0, gix-status v0.7.0, gix-submodule v0.10.0, gix-worktree-state v0.9.0, gix v0.60.0, safety bump 26 crates ([`b050327`](https://github.com/Byron/gitoxide/commit/b050327e76f234b19be921b78b7b28e034319fdb)) + - Prepare changelogs prior to release ([`52c3bbd`](https://github.com/Byron/gitoxide/commit/52c3bbd36b9e94a0f3a78b4ada84d0c08eba27f6)) + - Merge branch 'status' ([`3e5c974`](https://github.com/Byron/gitoxide/commit/3e5c974dd62ac134711c6c2f5a5490187a6ea55e)) + - Fix lints for nightly, and clippy ([`f8ce3d0`](https://github.com/Byron/gitoxide/commit/f8ce3d0721b6a53713a9392f2451874f520bc44c)) + - Update to `winnow` 0.6 ([`35592c9`](https://github.com/Byron/gitoxide/commit/35592c9aa9665e1d9f554b589614fe59308a5f25)) +
+ ## 0.30.1 (2024-02-25) A maintenance release without user-facing changes. @@ -13,7 +107,7 @@ A maintenance release without user-facing changes. - - 10 commits contributed to the release over the course of 24 calendar days. + - 11 commits contributed to the release over the course of 24 calendar days. - 36 days passed between releases. - 0 commits were understood as [conventional](https://www.conventionalcommits.org). - 0 issues like '(#ID)' were seen in commit messages @@ -25,6 +119,7 @@ A maintenance release without user-facing changes.
view details * **Uncategorized** + - Release gix-date v0.8.4, gix-utils v0.1.10, gix-actor v0.30.1, gix-object v0.41.1, gix-path v0.10.6, gix-glob v0.16.1, gix-quote v0.4.11, gix-attributes v0.22.1, gix-command v0.3.5, gix-filter v0.10.0, gix-commitgraph v0.24.1, gix-worktree-stream v0.10.0, gix-archive v0.10.0, gix-config-value v0.14.5, gix-ref v0.42.0, gix-sec v0.10.5, gix-config v0.35.0, gix-prompt v0.8.3, gix-url v0.27.1, gix-credentials v0.24.1, gix-ignore v0.11.1, gix-index v0.30.0, gix-worktree v0.31.0, gix-diff v0.41.0, gix-discover v0.30.0, gix-pathspec v0.7.0, gix-dir v0.1.0, gix-pack v0.48.0, gix-odb v0.58.0, gix-transport v0.41.1, gix-protocol v0.44.1, gix-revision v0.26.1, gix-refspec v0.22.1, gix-status v0.6.0, gix-submodule v0.9.0, gix-worktree-state v0.8.0, gix v0.59.0, gix-fsck v0.3.0, gitoxide-core v0.36.0, gitoxide v0.34.0, safety bump 10 crates ([`45b4470`](https://github.com/Byron/gitoxide/commit/45b447045bc826f252129c300c531acde2652c64)) - Prepare changelogs prior to release ([`f2e111f`](https://github.com/Byron/gitoxide/commit/f2e111f768fc1bc6182355261c20b63610cffec7)) - Merge branch 'btoi' ([`5fc379d`](https://github.com/Byron/gitoxide/commit/5fc379d1dc867d15a50cb086e30beefde2b42d86)) - Refactor ([`c5c69bd`](https://github.com/Byron/gitoxide/commit/c5c69bd355771a6fb3e4f6db0c5f49aa2bf7f42f)) diff --git a/gix-actor/Cargo.toml b/gix-actor/Cargo.toml index d82e2b35f39..1271a01dce5 100644 --- a/gix-actor/Cargo.toml +++ b/gix-actor/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "gix-actor" -version = "0.30.1" +version = "0.31.2" description = "A way to identify git actors" authors = ["Sebastian Thiel "] repository = "https://github.com/Byron/gitoxide" @@ -18,9 +18,9 @@ doctest = false serde = ["dep:serde", "bstr/serde", "gix-date/serde"] [dependencies] -gix-features = { version = "^0.38.0", path = "../gix-features", optional = true } -gix-date = { version = "^0.8.4", path = "../gix-date" } -gix-utils = { version = "^0.1.10", path = "../gix-utils" } +gix-features = { version = "^0.38.2", path = "../gix-features", optional = true } +gix-date = { version = "^0.8.6", path = "../gix-date" } +gix-utils = { version = "^0.1.11", path = "../gix-utils" } thiserror = "1.0.38" bstr = { version = "1.3.0", default-features = false, features = [ diff --git a/gix-actor/src/lib.rs b/gix-actor/src/lib.rs index e582541cb1a..e7e94770098 100644 --- a/gix-actor/src/lib.rs +++ b/gix-actor/src/lib.rs @@ -22,6 +22,7 @@ use gix_date::Time; mod identity; /// +#[allow(clippy::empty_docs)] pub mod signature; /// A person with name and email. diff --git a/gix-actor/src/signature/decode.rs b/gix-actor/src/signature/decode.rs index b19deb510bd..8b31da6b001 100644 --- a/gix-actor/src/signature/decode.rs +++ b/gix-actor/src/signature/decode.rs @@ -36,9 +36,14 @@ pub(crate) mod function { take_while(1..=2, AsChar::is_dec_digit) .verify_map(|v| to_signed::(v).ok()) .context(StrContext::Expected("MM".into())), + take_while(0.., AsChar::is_dec_digit).map(|v: &[u8]| v), ) - .map(|(time, sign, hours, minutes)| { - let offset = (hours * 3600 + minutes * 60) * if sign == Sign::Minus { -1 } else { 1 }; + .map(|(time, sign, hours, minutes, trailing_digits)| { + let offset = if trailing_digits.is_empty() { + (hours * 3600 + minutes * 60) * if sign == Sign::Minus { -1 } else { 1 } + } else { + 0 + }; Time { seconds: time, offset, diff --git a/gix-actor/src/signature/mod.rs b/gix-actor/src/signature/mod.rs index 057d4293e7d..34a0f7e28f3 100644 --- a/gix-actor/src/signature/mod.rs +++ b/gix-actor/src/signature/mod.rs @@ -129,5 +129,6 @@ pub(crate) mod write { } /// +#[allow(clippy::empty_docs)] pub mod decode; pub use decode::function::decode; diff --git a/gix-actor/tests/signature/mod.rs b/gix-actor/tests/signature/mod.rs index 496fb54ae54..55effea170d 100644 --- a/gix-actor/tests/signature/mod.rs +++ b/gix-actor/tests/signature/mod.rs @@ -53,7 +53,7 @@ mod write_to { } use bstr::ByteSlice; -use gix_actor::Signature; +use gix_actor::{Signature, SignatureRef}; #[test] fn trim() { @@ -80,3 +80,28 @@ fn round_trip() -> Result<(), Box> { } Ok(()) } + +#[test] +fn parse_timestamp_with_trailing_digits() { + let signature = gix_actor::SignatureRef::from_bytes::<()>(b"first last 1312735823 +051800") + .expect("deal with trailing zeroes in timestamp by discarding it"); + assert_eq!( + signature, + SignatureRef { + name: "first last".into(), + email: "name@example.com".into(), + time: gix_actor::date::Time::new(1312735823, 0), + } + ); + + let signature = gix_actor::SignatureRef::from_bytes::<()>(b"first last 1312735823 +0518") + .expect("this naturally works as the timestamp does not have trailing zeroes"); + assert_eq!( + signature, + SignatureRef { + name: "first last".into(), + email: "name@example.com".into(), + time: gix_actor::date::Time::new(1312735823, 19080), + } + ); +} diff --git a/gix-archive/CHANGELOG.md b/gix-archive/CHANGELOG.md index 2b542729079..0845d84d64d 100644 --- a/gix-archive/CHANGELOG.md +++ b/gix-archive/CHANGELOG.md @@ -5,6 +5,114 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## 0.13.1 (2024-05-25) + +### Chore + + - version bump dependency zip-2.0.0 + upstream revoked version zip-1.3.1 + +### Commit Statistics + + + + - 3 commits contributed to the release. + - 3 days passed between releases. + - 1 commit was understood as [conventional](https://www.conventionalcommits.org). + - 0 issues like '(#ID)' were seen in commit messages + +### Commit Details + + + +
view details + + * **Uncategorized** + - Merge pull request #1379 from belt/main ([`b4ab6f7`](https://github.com/Byron/gitoxide/commit/b4ab6f77596ae54f68743bbd93228dca08cc0440)) + - Adjust expectations in `gix-archive` ([`67bc66c`](https://github.com/Byron/gitoxide/commit/67bc66c1bb670971211968186ede27d4860f444b)) + - Version bump dependency zip-2.0.0 ([`7f0a08f`](https://github.com/Byron/gitoxide/commit/7f0a08f31661b917859dd761bf6610a303c1d432)) +
+ +## 0.13.0 (2024-05-22) + +### Bug Fixes + + - symlink support for `zip` archives + This started working with the upgradde of the `zip` crate. + +### Commit Statistics + + + + - 7 commits contributed to the release over the course of 8 calendar days. + - 38 days passed between releases. + - 1 commit was understood as [conventional](https://www.conventionalcommits.org). + - 0 issues like '(#ID)' were seen in commit messages + +### Commit Details + + + +
view details + + * **Uncategorized** + - Release gix-features v0.38.2, gix-actor v0.31.2, gix-validate v0.8.5, gix-object v0.42.2, gix-command v0.3.7, gix-filter v0.11.2, gix-fs v0.11.0, gix-revwalk v0.13.1, gix-traverse v0.39.1, gix-worktree-stream v0.13.0, gix-archive v0.13.0, gix-tempfile v14.0.0, gix-lock v14.0.0, gix-ref v0.44.0, gix-config v0.37.0, gix-prompt v0.8.5, gix-index v0.33.0, gix-worktree v0.34.0, gix-diff v0.44.0, gix-discover v0.32.0, gix-pathspec v0.7.5, gix-dir v0.5.0, gix-macros v0.1.5, gix-mailmap v0.23.1, gix-negotiate v0.13.1, gix-pack v0.51.0, gix-odb v0.61.0, gix-transport v0.42.1, gix-protocol v0.45.1, gix-revision v0.27.1, gix-status v0.10.0, gix-submodule v0.11.0, gix-worktree-state v0.11.0, gix v0.63.0, gitoxide-core v0.38.0, gitoxide v0.36.0, safety bump 19 crates ([`4f98e94`](https://github.com/Byron/gitoxide/commit/4f98e94e0e8b79ed2899b35bef40f3c30b3025b0)) + - Adjust changelogs prior to release ([`9511416`](https://github.com/Byron/gitoxide/commit/9511416a6cd0c571233f958c165329c8705c2498)) + - Merge branch 'various-fixes' ([`d6cd449`](https://github.com/Byron/gitoxide/commit/d6cd44930fb204b06e2b70fc6965e7705530c47a)) + - Symlink support for `zip` archives ([`e955770`](https://github.com/Byron/gitoxide/commit/e955770c0b5c06a6c8518a06df4aa0cc3b506f16)) + - Merge pull request from GHSA-7w47-3wg8-547c ([`79dce79`](https://github.com/Byron/gitoxide/commit/79dce79c62f6072aa2653780d590dc3993dfa401)) + - Adapt to changes in `gix-worktree` ([`1ca6a3c`](https://github.com/Byron/gitoxide/commit/1ca6a3ce22887c7eb42ec3e0a19f6e1202715745)) + - Release gix-date v0.8.6 ([`d3588ca`](https://github.com/Byron/gitoxide/commit/d3588ca4fe0364c88e42cdac24ceae548355d99d)) +
+ +## 0.12.0 (2024-04-13) + +A maintenance release without user-facing changes. + +### Commit Statistics + + + + - 2 commits contributed to the release. + - 29 days passed between releases. + - 0 commits were understood as [conventional](https://www.conventionalcommits.org). + - 0 issues like '(#ID)' were seen in commit messages + +### Commit Details + + + +
view details + + * **Uncategorized** + - Release gix-trace v0.1.9, gix-utils v0.1.12, gix-packetline-blocking v0.17.4, gix-filter v0.11.1, gix-fs v0.10.2, gix-traverse v0.39.0, gix-worktree-stream v0.12.0, gix-archive v0.12.0, gix-config v0.36.1, gix-url v0.27.3, gix-index v0.32.0, gix-worktree v0.33.0, gix-diff v0.43.0, gix-pathspec v0.7.3, gix-dir v0.4.0, gix-pack v0.50.0, gix-odb v0.60.0, gix-transport v0.42.0, gix-protocol v0.45.0, gix-status v0.9.0, gix-worktree-state v0.10.0, gix v0.62.0, gix-fsck v0.4.0, gitoxide-core v0.37.0, gitoxide v0.35.0, safety bump 14 crates ([`095c673`](https://github.com/Byron/gitoxide/commit/095c6739b2722a8b9af90776b435ef2da454c0e6)) + - Prepare changelogs prior to release ([`5755271`](https://github.com/Byron/gitoxide/commit/57552717f46f96c35ba4ddc0a64434354ef845e9)) +
+ +## 0.11.0 (2024-03-14) + +A maintenance release without user-facing changes. + +### Commit Statistics + + + + - 2 commits contributed to the release. + - 18 days passed between releases. + - 0 commits were understood as [conventional](https://www.conventionalcommits.org). + - 0 issues like '(#ID)' were seen in commit messages + +### Commit Details + + + +
view details + + * **Uncategorized** + - Release gix-date v0.8.5, gix-hash v0.14.2, gix-trace v0.1.8, gix-utils v0.1.11, gix-features v0.38.1, gix-actor v0.31.0, gix-validate v0.8.4, gix-object v0.42.0, gix-path v0.10.7, gix-glob v0.16.2, gix-quote v0.4.12, gix-attributes v0.22.2, gix-command v0.3.6, gix-filter v0.11.0, gix-fs v0.10.1, gix-chunk v0.4.8, gix-commitgraph v0.24.2, gix-hashtable v0.5.2, gix-revwalk v0.13.0, gix-traverse v0.38.0, gix-worktree-stream v0.11.0, gix-archive v0.11.0, gix-config-value v0.14.6, gix-tempfile v13.1.1, gix-lock v13.1.1, gix-ref v0.43.0, gix-sec v0.10.6, gix-config v0.36.0, gix-prompt v0.8.4, gix-url v0.27.2, gix-credentials v0.24.2, gix-ignore v0.11.2, gix-bitmap v0.2.11, gix-index v0.31.0, gix-worktree v0.32.0, gix-diff v0.42.0, gix-discover v0.31.0, gix-pathspec v0.7.1, gix-dir v0.2.0, gix-macros v0.1.4, gix-mailmap v0.23.0, gix-negotiate v0.13.0, gix-pack v0.49.0, gix-odb v0.59.0, gix-packetline v0.17.4, gix-transport v0.41.2, gix-protocol v0.44.2, gix-revision v0.27.0, gix-refspec v0.23.0, gix-status v0.7.0, gix-submodule v0.10.0, gix-worktree-state v0.9.0, gix v0.60.0, safety bump 26 crates ([`b050327`](https://github.com/Byron/gitoxide/commit/b050327e76f234b19be921b78b7b28e034319fdb)) + - Prepare changelogs prior to release ([`52c3bbd`](https://github.com/Byron/gitoxide/commit/52c3bbd36b9e94a0f3a78b4ada84d0c08eba27f6)) +
+ ## 0.10.0 (2024-02-25) A maintenance release without user-facing changes. @@ -13,7 +121,7 @@ A maintenance release without user-facing changes. - - 2 commits contributed to the release over the course of 30 calendar days. + - 3 commits contributed to the release over the course of 30 calendar days. - 36 days passed between releases. - 0 commits were understood as [conventional](https://www.conventionalcommits.org). - 0 issues like '(#ID)' were seen in commit messages @@ -25,6 +133,7 @@ A maintenance release without user-facing changes.
view details * **Uncategorized** + - Release gix-date v0.8.4, gix-utils v0.1.10, gix-actor v0.30.1, gix-object v0.41.1, gix-path v0.10.6, gix-glob v0.16.1, gix-quote v0.4.11, gix-attributes v0.22.1, gix-command v0.3.5, gix-filter v0.10.0, gix-commitgraph v0.24.1, gix-worktree-stream v0.10.0, gix-archive v0.10.0, gix-config-value v0.14.5, gix-ref v0.42.0, gix-sec v0.10.5, gix-config v0.35.0, gix-prompt v0.8.3, gix-url v0.27.1, gix-credentials v0.24.1, gix-ignore v0.11.1, gix-index v0.30.0, gix-worktree v0.31.0, gix-diff v0.41.0, gix-discover v0.30.0, gix-pathspec v0.7.0, gix-dir v0.1.0, gix-pack v0.48.0, gix-odb v0.58.0, gix-transport v0.41.1, gix-protocol v0.44.1, gix-revision v0.26.1, gix-refspec v0.22.1, gix-status v0.6.0, gix-submodule v0.9.0, gix-worktree-state v0.8.0, gix v0.59.0, gix-fsck v0.3.0, gitoxide-core v0.36.0, gitoxide v0.34.0, safety bump 10 crates ([`45b4470`](https://github.com/Byron/gitoxide/commit/45b447045bc826f252129c300c531acde2652c64)) - Prepare changelogs prior to release ([`f2e111f`](https://github.com/Byron/gitoxide/commit/f2e111f768fc1bc6182355261c20b63610cffec7)) - Release gix-path v0.10.5 ([`b8cba96`](https://github.com/Byron/gitoxide/commit/b8cba96ce57f8b6b0067d6a8cf3e37eaf280a238))
diff --git a/gix-archive/Cargo.toml b/gix-archive/Cargo.toml index 0552e8613db..8c83535029f 100644 --- a/gix-archive/Cargo.toml +++ b/gix-archive/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "gix-archive" -version = "0.10.0" +version = "0.13.1" repository = "https://github.com/Byron/gitoxide" license = "MIT OR Apache-2.0" description = "archive generation from of a worktree stream" @@ -25,13 +25,13 @@ zip = ["dep:zip", "dep:time"] [dependencies] -gix-worktree-stream = { version = "^0.10.0", path = "../gix-worktree-stream" } -gix-object = { version = "^0.41.1", path = "../gix-object" } -gix-path = { version = "^0.10.6", path = "../gix-path", optional = true } -gix-date = { version = "^0.8.4", path = "../gix-date" } +gix-worktree-stream = { version = "^0.13.0", path = "../gix-worktree-stream" } +gix-object = { version = "^0.42.2", path = "../gix-object" } +gix-path = { version = "^0.10.7", path = "../gix-path", optional = true } +gix-date = { version = "^0.8.6", path = "../gix-date" } flate2 = { version = "1.0.26", optional = true } -zip = { version = "0.6.6", optional = true, default-features = false, features = ["deflate", "time"] } +zip = { version = "2.1.0", optional = true, default-features = false, features = ["deflate", "time"] } time = { version = "0.3.23", optional = true, default-features = false, features = ["std"] } thiserror = "1.0.26" @@ -42,13 +42,13 @@ tar = { version = "0.4.38", optional = true } document-features = { version = "0.2.0", optional = true } [dev-dependencies] -gix-testtools = { path = "../tests/tools"} -gix-odb = { path = "../gix-odb"} -gix-worktree = { path = "../gix-worktree", default-features = false, features = ["attributes"]} -gix-hash = { path = "../gix-hash"} -gix-attributes = { path = "../gix-attributes"} -gix-object = { path = "../gix-object"} -gix-filter = { path = "../gix-filter"} +gix-testtools = { path = "../tests/tools" } +gix-odb = { path = "../gix-odb" } +gix-worktree = { path = "../gix-worktree", default-features = false, features = ["attributes"] } +gix-hash = { path = "../gix-hash" } +gix-attributes = { path = "../gix-attributes" } +gix-object = { path = "../gix-object" } +gix-filter = { path = "../gix-filter" } [package.metadata.docs.rs] all-features = true diff --git a/gix-archive/src/write.rs b/gix-archive/src/write.rs index 5c5dc885eca..244b40554e0 100644 --- a/gix-archive/src/write.rs +++ b/gix-archive/src/write.rs @@ -126,7 +126,7 @@ where NextFn: FnMut(&mut Stream) -> Result>, gix_worktree_stream::entry::Error>, { let compression_level = match opts.format { - Format::Zip { compression_level } => compression_level.map(|lvl| lvl as i32), + Format::Zip { compression_level } => compression_level.map(|lvl| lvl as i64), _other => return write_stream(stream, next_entry, out, opts), }; @@ -161,10 +161,10 @@ fn append_zip_entry( mut entry: gix_worktree_stream::Entry<'_>, buf: &mut Vec, mtime: zip::DateTime, - compression_level: Option, + compression_level: Option, tree_prefix: Option<&bstr::BString>, ) -> Result<(), Error> { - let file_opts = zip::write::FileOptions::default() + let file_opts = zip::write::FileOptions::<'_, ()>::default() .compression_method(zip::CompressionMethod::Deflated) .compression_level(compression_level) .large_file(entry.bytes_remaining().map_or(true, |len| len > u32::MAX as usize)) diff --git a/gix-archive/tests/archive.rs b/gix-archive/tests/archive.rs index c9a4e8bb281..2fb6f364dcc 100644 --- a/gix-archive/tests/archive.rs +++ b/gix-archive/tests/archive.rs @@ -183,8 +183,8 @@ mod from_tree { }, |buf| { assert!( - buf.len() < 1220, - "bigger than uncompressed for some reason: {} < 1220", + buf.len() < 1270, + "much bigger than uncompressed for some reason (565): {} < 1270", buf.len() ); let mut ar = zip::ZipArchive::new(std::io::Cursor::new(buf.as_slice()))?; @@ -208,7 +208,11 @@ mod from_tree { ); let mut link = ar.by_name("prefix/symlink-to-a")?; assert!(!link.is_dir()); - assert!(link.is_file(), "no symlink differentiation"); + assert_eq!( + link.is_symlink(), + cfg!(not(windows)), + "symlinks are supported as well, but only on Unix" + ); assert_eq!( link.unix_mode(), Some(if cfg!(windows) { 0o100644 } else { 0o120644 }), @@ -233,7 +237,7 @@ mod from_tree { noop_pipeline(), move |rela_path, mode, attrs| { cache - .at_entry(rela_path, mode.is_tree().into(), &odb) + .at_entry(rela_path, Some(mode.into()), &odb) .map(|entry| entry.matching_attributes(attrs)) .map(|_| ()) }, diff --git a/gix-archive/tests/fixtures/basic.sh b/gix-archive/tests/fixtures/basic.sh index ea026738d0f..5cd4c2e859c 100755 --- a/gix-archive/tests/fixtures/basic.sh +++ b/gix-archive/tests/fixtures/basic.sh @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash set -eu -o pipefail git init diff --git a/gix-attributes/CHANGELOG.md b/gix-attributes/CHANGELOG.md index 47281d59252..8ac51e8a4cf 100644 --- a/gix-attributes/CHANGELOG.md +++ b/gix-attributes/CHANGELOG.md @@ -5,6 +5,31 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## 0.22.2 (2024-03-14) + +A maintenance release without user-facing changes. + +### Commit Statistics + + + + - 3 commits contributed to the release over the course of 4 calendar days. + - 18 days passed between releases. + - 0 commits were understood as [conventional](https://www.conventionalcommits.org). + - 0 issues like '(#ID)' were seen in commit messages + +### Commit Details + + + +
view details + + * **Uncategorized** + - Prepare changelogs prior to release ([`52c3bbd`](https://github.com/Byron/gitoxide/commit/52c3bbd36b9e94a0f3a78b4ada84d0c08eba27f6)) + - Merge branch 'status' ([`3e5c974`](https://github.com/Byron/gitoxide/commit/3e5c974dd62ac134711c6c2f5a5490187a6ea55e)) + - Fix lints for nightly, and clippy ([`f8ce3d0`](https://github.com/Byron/gitoxide/commit/f8ce3d0721b6a53713a9392f2451874f520bc44c)) +
+ ## 0.22.1 (2024-02-25) A maintenance release without user-facing changes. @@ -13,7 +38,7 @@ A maintenance release without user-facing changes. - - 4 commits contributed to the release over the course of 30 calendar days. + - 5 commits contributed to the release over the course of 30 calendar days. - 36 days passed between releases. - 0 commits were understood as [conventional](https://www.conventionalcommits.org). - 0 issues like '(#ID)' were seen in commit messages @@ -25,6 +50,7 @@ A maintenance release without user-facing changes.
view details * **Uncategorized** + - Release gix-date v0.8.4, gix-utils v0.1.10, gix-actor v0.30.1, gix-object v0.41.1, gix-path v0.10.6, gix-glob v0.16.1, gix-quote v0.4.11, gix-attributes v0.22.1, gix-command v0.3.5, gix-filter v0.10.0, gix-commitgraph v0.24.1, gix-worktree-stream v0.10.0, gix-archive v0.10.0, gix-config-value v0.14.5, gix-ref v0.42.0, gix-sec v0.10.5, gix-config v0.35.0, gix-prompt v0.8.3, gix-url v0.27.1, gix-credentials v0.24.1, gix-ignore v0.11.1, gix-index v0.30.0, gix-worktree v0.31.0, gix-diff v0.41.0, gix-discover v0.30.0, gix-pathspec v0.7.0, gix-dir v0.1.0, gix-pack v0.48.0, gix-odb v0.58.0, gix-transport v0.41.1, gix-protocol v0.44.1, gix-revision v0.26.1, gix-refspec v0.22.1, gix-status v0.6.0, gix-submodule v0.9.0, gix-worktree-state v0.8.0, gix v0.59.0, gix-fsck v0.3.0, gitoxide-core v0.36.0, gitoxide v0.34.0, safety bump 10 crates ([`45b4470`](https://github.com/Byron/gitoxide/commit/45b447045bc826f252129c300c531acde2652c64)) - Prepare changelogs prior to release ([`f2e111f`](https://github.com/Byron/gitoxide/commit/f2e111f768fc1bc6182355261c20b63610cffec7)) - Merge branch 'dirwalk' ([`face359`](https://github.com/Byron/gitoxide/commit/face359443ba33e8985ec1525d5ec38b743ea7a9)) - Adjust gitignore files with precious declarations ([`ae86a6a`](https://github.com/Byron/gitoxide/commit/ae86a6a206074b85ff1eba32aea9c8b40c087b17)) diff --git a/gix-attributes/Cargo.toml b/gix-attributes/Cargo.toml index a8fd3bb6b5d..c259e8961c9 100644 --- a/gix-attributes/Cargo.toml +++ b/gix-attributes/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "gix-attributes" -version = "0.22.1" +version = "0.22.2" repository = "https://github.com/Byron/gitoxide" license = "MIT OR Apache-2.0" description = "A crate of the gitoxide project dealing .gitattributes files" @@ -17,10 +17,10 @@ doctest = false serde = ["dep:serde", "bstr/serde", "gix-glob/serde", "kstring/serde"] [dependencies] -gix-path = { version = "^0.10.6", path = "../gix-path" } -gix-quote = { version = "^0.4.11", path = "../gix-quote" } -gix-glob = { version = "^0.16.1", path = "../gix-glob" } -gix-trace = { version = "^0.1.7", path = "../gix-trace" } +gix-path = { version = "^0.10.7", path = "../gix-path" } +gix-quote = { version = "^0.4.12", path = "../gix-quote" } +gix-glob = { version = "^0.16.3", path = "../gix-glob" } +gix-trace = { version = "^0.1.8", path = "../gix-trace" } bstr = { version = "1.3.0", default-features = false, features = ["std", "unicode"]} smallvec = "1.10.0" diff --git a/gix-attributes/src/lib.rs b/gix-attributes/src/lib.rs index 812ffe49a75..4b1090fa9bb 100644 --- a/gix-attributes/src/lib.rs +++ b/gix-attributes/src/lib.rs @@ -13,14 +13,18 @@ use kstring::{KString, KStringRef}; mod assignment; /// +#[allow(clippy::empty_docs)] pub mod name; /// +#[allow(clippy::empty_docs)] pub mod state; /// +#[allow(clippy::empty_docs)] pub mod search; /// +#[allow(clippy::empty_docs)] pub mod parse; /// Parse attribute assignments line by line from `bytes`, and fail the operation on error. diff --git a/gix-attributes/src/search/outcome.rs b/gix-attributes/src/search/outcome.rs index 41d70e5806c..2a0df7e478a 100644 --- a/gix-attributes/src/search/outcome.rs +++ b/gix-attributes/src/search/outcome.rs @@ -26,7 +26,7 @@ impl Outcome { for (order, macro_attributes) in collection.iter().filter_map(|(_, meta)| { (!meta.macro_attributes.is_empty()).then_some((meta.id.0, &meta.macro_attributes)) }) { - self.matches_by_id[order].macro_attributes = macro_attributes.clone() + self.matches_by_id[order].macro_attributes.clone_from(macro_attributes) } for (name, id) in self.selected.iter_mut().filter(|(_, id)| id.is_none()) { @@ -88,7 +88,7 @@ impl Outcome { /// Note that it's safe to call it multiple times, so that it can be called after this instance was used to store a search result. pub fn copy_into(&self, collection: &MetadataCollection, dest: &mut Self) { dest.initialize(collection); - dest.matches_by_id = self.matches_by_id.clone(); + dest.matches_by_id.clone_from(&self.matches_by_id); if dest.patterns.len() != self.patterns.len() { dest.patterns = self.patterns.clone(); } @@ -325,7 +325,11 @@ impl MetadataCollection { }; self.assign_order_to_attributes(attrs); - self.name_to_meta.get_mut(name).expect("just added").macro_attributes = attrs.clone(); + self.name_to_meta + .get_mut(name) + .expect("just added") + .macro_attributes + .clone_from(attrs); order } diff --git a/gix-attributes/tests/fixtures/make_attributes_baseline.sh b/gix-attributes/tests/fixtures/make_attributes_baseline.sh index ce48ebe1abe..47f109e971d 100755 --- a/gix-attributes/tests/fixtures/make_attributes_baseline.sh +++ b/gix-attributes/tests/fixtures/make_attributes_baseline.sh @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash set -eu -o pipefail mkdir basics; diff --git a/gix-attributes/tests/search/mod.rs b/gix-attributes/tests/search/mod.rs index 36e6286c8ed..6ce6f7b3d3d 100644 --- a/gix-attributes/tests/search/mod.rs +++ b/gix-attributes/tests/search/mod.rs @@ -307,11 +307,8 @@ mod baseline { let mut buf = Vec::new(); let mut collection = MetadataCollection::default(); - let group = gix_attributes::Search::new_globals( - &mut [base.join("user.attributes")].into_iter(), - &mut buf, - &mut collection, - )?; + let group = + gix_attributes::Search::new_globals([base.join("user.attributes")].into_iter(), &mut buf, &mut collection)?; Ok((group, collection, base, input)) } diff --git a/gix-bitmap/CHANGELOG.md b/gix-bitmap/CHANGELOG.md index a8236c4d264..291a2fe7d5b 100644 --- a/gix-bitmap/CHANGELOG.md +++ b/gix-bitmap/CHANGELOG.md @@ -5,22 +5,49 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## 0.2.11 (2024-03-14) + +A maintenance release without user-facing changes. + +### Commit Statistics + + + + - 3 commits contributed to the release over the course of 4 calendar days. + - 75 days passed between releases. + - 0 commits were understood as [conventional](https://www.conventionalcommits.org). + - 0 issues like '(#ID)' were seen in commit messages + +### Commit Details + + + +
view details + + * **Uncategorized** + - Prepare changelogs prior to release ([`52c3bbd`](https://github.com/Byron/gitoxide/commit/52c3bbd36b9e94a0f3a78b4ada84d0c08eba27f6)) + - Merge branch 'status' ([`3e5c974`](https://github.com/Byron/gitoxide/commit/3e5c974dd62ac134711c6c2f5a5490187a6ea55e)) + - Fix lints for nightly, and clippy ([`f8ce3d0`](https://github.com/Byron/gitoxide/commit/f8ce3d0721b6a53713a9392f2451874f520bc44c)) +
+ ## 0.2.10 (2023-12-30) + + ### Chore - - change `rust-version` manifest field back to 1.65. - They didn't actually need to be higher to work, and changing them - unecessarily can break downstream CI. - - Let's keep this value as low as possible, and only increase it when - more recent features are actually used. +- change `rust-version` manifest field back to 1.65. + They didn't actually need to be higher to work, and changing them + unecessarily can break downstream CI. + + Let's keep this value as low as possible, and only increase it when + more recent features are actually used. ### Commit Statistics - - 2 commits contributed to the release. + - 3 commits contributed to the release. - 1 commit was understood as [conventional](https://www.conventionalcommits.org). - 0 issues like '(#ID)' were seen in commit messages @@ -31,6 +58,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
view details * **Uncategorized** + - Release gix-date v0.8.3, gix-hash v0.14.1, gix-trace v0.1.6, gix-features v0.37.1, gix-actor v0.29.1, gix-validate v0.8.3, gix-object v0.40.1, gix-path v0.10.3, gix-glob v0.15.1, gix-quote v0.4.10, gix-attributes v0.21.1, gix-command v0.3.2, gix-packetline-blocking v0.17.2, gix-utils v0.1.8, gix-filter v0.8.1, gix-fs v0.9.1, gix-chunk v0.4.7, gix-commitgraph v0.23.1, gix-hashtable v0.5.1, gix-revwalk v0.11.1, gix-traverse v0.36.1, gix-worktree-stream v0.8.1, gix-archive v0.8.1, gix-config-value v0.14.3, gix-tempfile v12.0.1, gix-lock v12.0.1, gix-ref v0.40.1, gix-sec v0.10.3, gix-config v0.33.1, gix-prompt v0.8.2, gix-url v0.26.1, gix-credentials v0.23.1, gix-ignore v0.10.1, gix-bitmap v0.2.10, gix-index v0.28.1, gix-worktree v0.29.1, gix-diff v0.39.1, gix-discover v0.28.1, gix-macros v0.1.3, gix-mailmap v0.21.1, gix-negotiate v0.11.1, gix-pack v0.46.1, gix-odb v0.56.1, gix-pathspec v0.5.1, gix-packetline v0.17.2, gix-transport v0.40.1, gix-protocol v0.43.1, gix-revision v0.25.1, gix-refspec v0.21.1, gix-status v0.4.1, gix-submodule v0.7.1, gix-worktree-state v0.6.1, gix v0.57.1 ([`972241f`](https://github.com/Byron/gitoxide/commit/972241f1904944e8b6e84c6aa1649a49be7a85c3)) - Merge branch 'msrv' ([`8c492d7`](https://github.com/Byron/gitoxide/commit/8c492d7b7e6e5d520b1e3ffeb489eeb88266aa75)) - Change `rust-version` manifest field back to 1.65. ([`3bd09ef`](https://github.com/Byron/gitoxide/commit/3bd09ef120945a9669321ea856db4079a5dab930))
@@ -41,8 +69,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Chore - - upgrade MSRV to v1.70 - Our MSRV follows the one of `helix`, which in turn follows Firefox. +- upgrade MSRV to v1.70 + Our MSRV follows the one of `helix`, which in turn follows Firefox. ### Commit Statistics @@ -99,7 +127,7 @@ A maintenance release without user-facing changes. ### Chore - - don't call crate 'WIP' in manifest anymore. +- don't call crate 'WIP' in manifest anymore. ### Commit Statistics @@ -244,10 +272,10 @@ A maintenance release without user-facing changes. ### Chore - - replace `quick-error` with `thiserror` - This increases the compile time of the crate alone if there is no proc-macro - in the dependency tree, but will ever so slightly improve compile times for `gix` - as a whole. +- replace `quick-error` with `thiserror` + This increases the compile time of the crate alone if there is no proc-macro + in the dependency tree, but will ever so slightly improve compile times for `gix` + as a whole. ### Bug Fixes @@ -310,8 +338,8 @@ A maintenance release without user-facing changes. ### Chore - - uniformize deny attributes - - remove default link to cargo doc everywhere +- uniformize deny attributes +- remove default link to cargo doc everywhere ### Documentation @@ -452,8 +480,8 @@ A maintenance release without user-facing changes. ### Chore - - uniformize deny attributes - - remove default link to cargo doc everywhere +- uniformize deny attributes +- remove default link to cargo doc everywhere ## 0.1.1 (2022-08-17) diff --git a/gix-bitmap/Cargo.toml b/gix-bitmap/Cargo.toml index 2a77f9ab593..1a14e8b1a50 100644 --- a/gix-bitmap/Cargo.toml +++ b/gix-bitmap/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "gix-bitmap" -version = "0.2.10" +version = "0.2.11" repository = "https://github.com/Byron/gitoxide" license = "MIT OR Apache-2.0" description = "A crate of the gitoxide project dedicated implementing the standard git bitmap format" diff --git a/gix-bitmap/src/ewah.rs b/gix-bitmap/src/ewah.rs index e2575f90500..64d363beb28 100644 --- a/gix-bitmap/src/ewah.rs +++ b/gix-bitmap/src/ewah.rs @@ -1,6 +1,5 @@ -use std::convert::TryInto; - /// +#[allow(clippy::empty_docs)] pub mod decode { /// The error returned by [`decode()`][super::decode()]. #[derive(Debug, thiserror::Error)] @@ -52,8 +51,6 @@ pub fn decode(data: &[u8]) -> Result<(Vec, &[u8]), decode::Error> { } mod access { - use std::convert::{TryFrom, TryInto}; - use super::Vec; impl Vec { diff --git a/gix-bitmap/src/lib.rs b/gix-bitmap/src/lib.rs index 83756d98897..0ccc5824843 100644 --- a/gix-bitmap/src/lib.rs +++ b/gix-bitmap/src/lib.rs @@ -7,8 +7,6 @@ pub mod ewah; pub(crate) mod decode { - use std::convert::TryInto; - #[inline] pub(crate) fn split_at_pos(data: &[u8], pos: usize) -> Option<(&[u8], &[u8])> { if data.len() < pos { diff --git a/gix-chunk/CHANGELOG.md b/gix-chunk/CHANGELOG.md index faf3ac6c99f..5fcbf8a671c 100644 --- a/gix-chunk/CHANGELOG.md +++ b/gix-chunk/CHANGELOG.md @@ -5,22 +5,49 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## 0.4.8 (2024-03-14) + +A maintenance release without user-facing changes. + +### Commit Statistics + + + + - 3 commits contributed to the release over the course of 4 calendar days. + - 75 days passed between releases. + - 0 commits were understood as [conventional](https://www.conventionalcommits.org). + - 0 issues like '(#ID)' were seen in commit messages + +### Commit Details + + + +
view details + + * **Uncategorized** + - Prepare changelogs prior to release ([`52c3bbd`](https://github.com/Byron/gitoxide/commit/52c3bbd36b9e94a0f3a78b4ada84d0c08eba27f6)) + - Merge branch 'status' ([`3e5c974`](https://github.com/Byron/gitoxide/commit/3e5c974dd62ac134711c6c2f5a5490187a6ea55e)) + - Fix lints for nightly, and clippy ([`f8ce3d0`](https://github.com/Byron/gitoxide/commit/f8ce3d0721b6a53713a9392f2451874f520bc44c)) +
+ ## 0.4.7 (2023-12-30) + + ### Chore - - change `rust-version` manifest field back to 1.65. - They didn't actually need to be higher to work, and changing them - unecessarily can break downstream CI. - - Let's keep this value as low as possible, and only increase it when - more recent features are actually used. +- change `rust-version` manifest field back to 1.65. + They didn't actually need to be higher to work, and changing them + unecessarily can break downstream CI. + + Let's keep this value as low as possible, and only increase it when + more recent features are actually used. ### Commit Statistics - - 2 commits contributed to the release. + - 3 commits contributed to the release. - 1 commit was understood as [conventional](https://www.conventionalcommits.org). - 0 issues like '(#ID)' were seen in commit messages @@ -31,6 +58,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
view details * **Uncategorized** + - Release gix-date v0.8.3, gix-hash v0.14.1, gix-trace v0.1.6, gix-features v0.37.1, gix-actor v0.29.1, gix-validate v0.8.3, gix-object v0.40.1, gix-path v0.10.3, gix-glob v0.15.1, gix-quote v0.4.10, gix-attributes v0.21.1, gix-command v0.3.2, gix-packetline-blocking v0.17.2, gix-utils v0.1.8, gix-filter v0.8.1, gix-fs v0.9.1, gix-chunk v0.4.7, gix-commitgraph v0.23.1, gix-hashtable v0.5.1, gix-revwalk v0.11.1, gix-traverse v0.36.1, gix-worktree-stream v0.8.1, gix-archive v0.8.1, gix-config-value v0.14.3, gix-tempfile v12.0.1, gix-lock v12.0.1, gix-ref v0.40.1, gix-sec v0.10.3, gix-config v0.33.1, gix-prompt v0.8.2, gix-url v0.26.1, gix-credentials v0.23.1, gix-ignore v0.10.1, gix-bitmap v0.2.10, gix-index v0.28.1, gix-worktree v0.29.1, gix-diff v0.39.1, gix-discover v0.28.1, gix-macros v0.1.3, gix-mailmap v0.21.1, gix-negotiate v0.11.1, gix-pack v0.46.1, gix-odb v0.56.1, gix-pathspec v0.5.1, gix-packetline v0.17.2, gix-transport v0.40.1, gix-protocol v0.43.1, gix-revision v0.25.1, gix-refspec v0.21.1, gix-status v0.4.1, gix-submodule v0.7.1, gix-worktree-state v0.6.1, gix v0.57.1 ([`972241f`](https://github.com/Byron/gitoxide/commit/972241f1904944e8b6e84c6aa1649a49be7a85c3)) - Merge branch 'msrv' ([`8c492d7`](https://github.com/Byron/gitoxide/commit/8c492d7b7e6e5d520b1e3ffeb489eeb88266aa75)) - Change `rust-version` manifest field back to 1.65. ([`3bd09ef`](https://github.com/Byron/gitoxide/commit/3bd09ef120945a9669321ea856db4079a5dab930))
@@ -41,8 +69,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Chore - - upgrade MSRV to v1.70 - Our MSRV follows the one of `helix`, which in turn follows Firefox. +- upgrade MSRV to v1.70 + Our MSRV follows the one of `helix`, which in turn follows Firefox. ### Commit Statistics @@ -227,7 +255,7 @@ A maintenance release without user-facing changes. ### Chore - - uniformize deny attributes +- uniformize deny attributes ### Documentation @@ -379,7 +407,7 @@ Maintenance release without observable changes. ### Chore - - uniformize deny attributes +- uniformize deny attributes ## 0.3.0 (2022-01-23) diff --git a/gix-chunk/Cargo.toml b/gix-chunk/Cargo.toml index 8679538e347..94b1b9fc4e9 100644 --- a/gix-chunk/Cargo.toml +++ b/gix-chunk/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "gix-chunk" -version = "0.4.7" +version = "0.4.8" description = "Interact with the git chunk file format used in multi-pack index and commit-graph files" authors = ["Sebastian Thiel "] repository = "https://github.com/Byron/gitoxide" diff --git a/gix-chunk/src/file/decode.rs b/gix-chunk/src/file/decode.rs index 22bdc169bd9..5f397d415df 100644 --- a/gix-chunk/src/file/decode.rs +++ b/gix-chunk/src/file/decode.rs @@ -1,4 +1,4 @@ -use std::{convert::TryInto, ops::Range}; +use std::ops::Range; mod error { /// The value returned by [`crate::file::Index::from_bytes()`] diff --git a/gix-chunk/src/file/index.rs b/gix-chunk/src/file/index.rs index c9211ab6fec..e511781a0e4 100644 --- a/gix-chunk/src/file/index.rs +++ b/gix-chunk/src/file/index.rs @@ -3,6 +3,7 @@ use std::ops::Range; use crate::file::Index; /// +#[allow(clippy::empty_docs)] pub mod offset_by_kind { use std::fmt::{Display, Formatter}; @@ -27,6 +28,7 @@ pub mod offset_by_kind { } /// +#[allow(clippy::empty_docs)] pub mod data_by_kind { /// The error returned by [`Index::data_by_id()`][super::Index::data_by_id()]. #[derive(Debug, thiserror::Error)] diff --git a/gix-chunk/src/file/mod.rs b/gix-chunk/src/file/mod.rs index 4ddd94999ab..46b82a7d256 100644 --- a/gix-chunk/src/file/mod.rs +++ b/gix-chunk/src/file/mod.rs @@ -1,9 +1,12 @@ /// +#[allow(clippy::empty_docs)] pub mod decode; /// +#[allow(clippy::empty_docs)] pub mod index; /// +#[allow(clippy::empty_docs)] pub mod write; /// The offset to a chunk as seen relative to the beginning of the file containing it. diff --git a/gix-chunk/src/lib.rs b/gix-chunk/src/lib.rs index 0647e320b1c..e7dc725c7d0 100644 --- a/gix-chunk/src/lib.rs +++ b/gix-chunk/src/lib.rs @@ -10,8 +10,9 @@ pub type Id = [u8; 4]; pub const SENTINEL: Id = [0u8; 4]; /// +#[allow(clippy::empty_docs)] pub mod range { - use std::{convert::TryInto, ops::Range}; + use std::ops::Range; use crate::file; @@ -33,4 +34,5 @@ pub mod range { } /// +#[allow(clippy::empty_docs)] pub mod file; diff --git a/gix-command/CHANGELOG.md b/gix-command/CHANGELOG.md index ed98707bc7f..df77014b0dd 100644 --- a/gix-command/CHANGELOG.md +++ b/gix-command/CHANGELOG.md @@ -5,6 +5,63 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## 0.3.7 (2024-05-22) + +A maintenance release without user-facing changes. + +### Commit Statistics + + + + - 3 commits contributed to the release over the course of 38 calendar days. + - 68 days passed between releases. + - 0 commits were understood as [conventional](https://www.conventionalcommits.org). + - 0 issues like '(#ID)' were seen in commit messages + +### Commit Details + + + +
view details + + * **Uncategorized** + - Adjust changelogs prior to release ([`9511416`](https://github.com/Byron/gitoxide/commit/9511416a6cd0c571233f958c165329c8705c2498)) + - Merge pull request #1345 from EliahKagan/shell-scripts ([`fe24c89`](https://github.com/Byron/gitoxide/commit/fe24c89e326670deaa3aaa643276d612d866072e)) + - Add missing +x bit on scripts that are run and not sourced ([`41bf65a`](https://github.com/Byron/gitoxide/commit/41bf65adef6f7d2cdd28fede262173ec7ba10822)) +
+ +## 0.3.6 (2024-03-14) + + + +### Chore + + - remove repetitive words + +### Commit Statistics + + + + - 6 commits contributed to the release over the course of 4 calendar days. + - 18 days passed between releases. + - 1 commit was understood as [conventional](https://www.conventionalcommits.org). + - 0 issues like '(#ID)' were seen in commit messages + +### Commit Details + + + +
view details + + * **Uncategorized** + - Release gix-date v0.8.5, gix-hash v0.14.2, gix-trace v0.1.8, gix-utils v0.1.11, gix-features v0.38.1, gix-actor v0.31.0, gix-validate v0.8.4, gix-object v0.42.0, gix-path v0.10.7, gix-glob v0.16.2, gix-quote v0.4.12, gix-attributes v0.22.2, gix-command v0.3.6, gix-filter v0.11.0, gix-fs v0.10.1, gix-chunk v0.4.8, gix-commitgraph v0.24.2, gix-hashtable v0.5.2, gix-revwalk v0.13.0, gix-traverse v0.38.0, gix-worktree-stream v0.11.0, gix-archive v0.11.0, gix-config-value v0.14.6, gix-tempfile v13.1.1, gix-lock v13.1.1, gix-ref v0.43.0, gix-sec v0.10.6, gix-config v0.36.0, gix-prompt v0.8.4, gix-url v0.27.2, gix-credentials v0.24.2, gix-ignore v0.11.2, gix-bitmap v0.2.11, gix-index v0.31.0, gix-worktree v0.32.0, gix-diff v0.42.0, gix-discover v0.31.0, gix-pathspec v0.7.1, gix-dir v0.2.0, gix-macros v0.1.4, gix-mailmap v0.23.0, gix-negotiate v0.13.0, gix-pack v0.49.0, gix-odb v0.59.0, gix-packetline v0.17.4, gix-transport v0.41.2, gix-protocol v0.44.2, gix-revision v0.27.0, gix-refspec v0.23.0, gix-status v0.7.0, gix-submodule v0.10.0, gix-worktree-state v0.9.0, gix v0.60.0, safety bump 26 crates ([`b050327`](https://github.com/Byron/gitoxide/commit/b050327e76f234b19be921b78b7b28e034319fdb)) + - Prepare changelogs prior to release ([`52c3bbd`](https://github.com/Byron/gitoxide/commit/52c3bbd36b9e94a0f3a78b4ada84d0c08eba27f6)) + - Merge branch 'status' ([`3e5c974`](https://github.com/Byron/gitoxide/commit/3e5c974dd62ac134711c6c2f5a5490187a6ea55e)) + - Merge pull request #1314 from avoidalone/main ([`5722e3a`](https://github.com/Byron/gitoxide/commit/5722e3aeeba5dd44e38a6cdbb70717a45345307e)) + - Remove repetitive words ([`39879af`](https://github.com/Byron/gitoxide/commit/39879af6eaf2bf4fe159a5c6371c98d516c4febe)) + - Fix lints for nightly, and clippy ([`f8ce3d0`](https://github.com/Byron/gitoxide/commit/f8ce3d0721b6a53713a9392f2451874f520bc44c)) +
+ ## 0.3.5 (2024-02-25) ### Bug Fixes @@ -33,7 +90,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - - 2 commits contributed to the release over the course of 12 calendar days. + - 3 commits contributed to the release over the course of 12 calendar days. - 21 days passed between releases. - 1 commit was understood as [conventional](https://www.conventionalcommits.org). - 1 unique issue was worked on: [#1284](https://github.com/Byron/gitoxide/issues/1284) @@ -47,6 +104,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 * **[#1284](https://github.com/Byron/gitoxide/issues/1284)** - Add test to assure `echo "$@" >&2` works as expected ([`74631d4`](https://github.com/Byron/gitoxide/commit/74631d4171cdfd4f97d61203c2b65ef02da7b2c4)) * **Uncategorized** + - Release gix-date v0.8.4, gix-utils v0.1.10, gix-actor v0.30.1, gix-object v0.41.1, gix-path v0.10.6, gix-glob v0.16.1, gix-quote v0.4.11, gix-attributes v0.22.1, gix-command v0.3.5, gix-filter v0.10.0, gix-commitgraph v0.24.1, gix-worktree-stream v0.10.0, gix-archive v0.10.0, gix-config-value v0.14.5, gix-ref v0.42.0, gix-sec v0.10.5, gix-config v0.35.0, gix-prompt v0.8.3, gix-url v0.27.1, gix-credentials v0.24.1, gix-ignore v0.11.1, gix-index v0.30.0, gix-worktree v0.31.0, gix-diff v0.41.0, gix-discover v0.30.0, gix-pathspec v0.7.0, gix-dir v0.1.0, gix-pack v0.48.0, gix-odb v0.58.0, gix-transport v0.41.1, gix-protocol v0.44.1, gix-revision v0.26.1, gix-refspec v0.22.1, gix-status v0.6.0, gix-submodule v0.9.0, gix-worktree-state v0.8.0, gix v0.59.0, gix-fsck v0.3.0, gitoxide-core v0.36.0, gitoxide v0.34.0, safety bump 10 crates ([`45b4470`](https://github.com/Byron/gitoxide/commit/45b447045bc826f252129c300c531acde2652c64)) - Prepare changelogs prior to release ([`f2e111f`](https://github.com/Byron/gitoxide/commit/f2e111f768fc1bc6182355261c20b63610cffec7))
diff --git a/gix-command/Cargo.toml b/gix-command/Cargo.toml index 36f919d0167..c1425daf6d2 100644 --- a/gix-command/Cargo.toml +++ b/gix-command/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "gix-command" -version = "0.3.5" +version = "0.3.7" repository = "https://github.com/Byron/gitoxide" license = "MIT OR Apache-2.0" description = "A crate of the gitoxide project handling internal git command execution" @@ -13,8 +13,8 @@ include = ["src/lib.rs", "LICENSE-*"] doctest = false [dependencies] -gix-trace = { version = "^0.1.7", path = "../gix-trace" } -gix-path = { version = "^0.10.6", path = "../gix-path" } +gix-trace = { version = "^0.1.8", path = "../gix-trace" } +gix-path = { version = "^0.10.7", path = "../gix-path" } bstr = { version = "1.5.0", default-features = false, features = ["std", "unicode"] } shell-words = "1.0" diff --git a/gix-command/src/lib.rs b/gix-command/src/lib.rs index b1bcd87e881..e5437c552ed 100644 --- a/gix-command/src/lib.rs +++ b/gix-command/src/lib.rs @@ -52,7 +52,7 @@ pub struct Prepare { pub struct Context { /// The `.git` directory that contains the repository. /// - /// If set, it will be used to set the the `GIT_DIR` environment variable. + /// If set, it will be used to set the `GIT_DIR` environment variable. pub git_dir: Option, /// Set the `GIT_WORK_TREE` environment variable with the given path. pub worktree_dir: Option, @@ -330,6 +330,7 @@ pub fn extract_interpreter(executable: &Path) -> Option { } /// +#[allow(clippy::empty_docs)] pub mod shebang { use bstr::{BStr, ByteSlice}; use std::ffi::OsString; diff --git a/gix-command/tests/fixtures/win_path_lookup.sh b/gix-command/tests/fixtures/win_path_lookup.sh old mode 100644 new mode 100755 index 7d351f0b13d..0a588efb8a5 --- a/gix-command/tests/fixtures/win_path_lookup.sh +++ b/gix-command/tests/fixtures/win_path_lookup.sh @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash set -eu -o pipefail mkdir a b c @@ -7,4 +7,3 @@ echo "#!/a/x" > a/x echo "#!/b/exe" > b/exe echo "#!/b/exe.com" > b/exe.com echo "#!/c/x.exe" > c/x.exe - diff --git a/gix-commitgraph/CHANGELOG.md b/gix-commitgraph/CHANGELOG.md index 1d004310140..40ba8dd9b33 100644 --- a/gix-commitgraph/CHANGELOG.md +++ b/gix-commitgraph/CHANGELOG.md @@ -5,6 +5,45 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## 0.24.2 (2024-03-14) + + + +### Chore + + - remove repetitive words + +### Bug Fixes + + - assure memory maps are created with `MAP_PRIVATE` + That way, the mmap process should work under more circumstances. + +### Commit Statistics + + + + - 7 commits contributed to the release over the course of 5 calendar days. + - 18 days passed between releases. + - 2 commits were understood as [conventional](https://www.conventionalcommits.org). + - 1 unique issue was worked on: [#1312](https://github.com/Byron/gitoxide/issues/1312) + +### Commit Details + + + +
view details + + * **[#1312](https://github.com/Byron/gitoxide/issues/1312)** + - Assure memory maps are created with `MAP_PRIVATE` ([`88061a1`](https://github.com/Byron/gitoxide/commit/88061a176b2f4b5a377a4cff513979ddb1e306a1)) + * **Uncategorized** + - Prepare changelogs prior to release ([`52c3bbd`](https://github.com/Byron/gitoxide/commit/52c3bbd36b9e94a0f3a78b4ada84d0c08eba27f6)) + - Merge branch 'status' ([`3e5c974`](https://github.com/Byron/gitoxide/commit/3e5c974dd62ac134711c6c2f5a5490187a6ea55e)) + - Merge pull request #1314 from avoidalone/main ([`5722e3a`](https://github.com/Byron/gitoxide/commit/5722e3aeeba5dd44e38a6cdbb70717a45345307e)) + - Remove repetitive words ([`39879af`](https://github.com/Byron/gitoxide/commit/39879af6eaf2bf4fe159a5c6371c98d516c4febe)) + - Fix lints for nightly, and clippy ([`f8ce3d0`](https://github.com/Byron/gitoxide/commit/f8ce3d0721b6a53713a9392f2451874f520bc44c)) + - Merge branch 'mmap-mode' ([`9e9b9fe`](https://github.com/Byron/gitoxide/commit/9e9b9fe6f63759d3bf479a8ec2f9dd1fbef87a08)) +
+ ## 0.24.1 (2024-02-25) A maintenance release without user-facing changes. @@ -13,7 +52,7 @@ A maintenance release without user-facing changes. - - 5 commits contributed to the release over the course of 19 calendar days. + - 6 commits contributed to the release over the course of 19 calendar days. - 36 days passed between releases. - 0 commits were understood as [conventional](https://www.conventionalcommits.org). - 0 issues like '(#ID)' were seen in commit messages @@ -25,6 +64,7 @@ A maintenance release without user-facing changes.
view details * **Uncategorized** + - Release gix-date v0.8.4, gix-utils v0.1.10, gix-actor v0.30.1, gix-object v0.41.1, gix-path v0.10.6, gix-glob v0.16.1, gix-quote v0.4.11, gix-attributes v0.22.1, gix-command v0.3.5, gix-filter v0.10.0, gix-commitgraph v0.24.1, gix-worktree-stream v0.10.0, gix-archive v0.10.0, gix-config-value v0.14.5, gix-ref v0.42.0, gix-sec v0.10.5, gix-config v0.35.0, gix-prompt v0.8.3, gix-url v0.27.1, gix-credentials v0.24.1, gix-ignore v0.11.1, gix-index v0.30.0, gix-worktree v0.31.0, gix-diff v0.41.0, gix-discover v0.30.0, gix-pathspec v0.7.0, gix-dir v0.1.0, gix-pack v0.48.0, gix-odb v0.58.0, gix-transport v0.41.1, gix-protocol v0.44.1, gix-revision v0.26.1, gix-refspec v0.22.1, gix-status v0.6.0, gix-submodule v0.9.0, gix-worktree-state v0.8.0, gix v0.59.0, gix-fsck v0.3.0, gitoxide-core v0.36.0, gitoxide v0.34.0, safety bump 10 crates ([`45b4470`](https://github.com/Byron/gitoxide/commit/45b447045bc826f252129c300c531acde2652c64)) - Prepare changelogs prior to release ([`f2e111f`](https://github.com/Byron/gitoxide/commit/f2e111f768fc1bc6182355261c20b63610cffec7)) - Merge branch 'dirwalk' ([`face359`](https://github.com/Byron/gitoxide/commit/face359443ba33e8985ec1525d5ec38b743ea7a9)) - Adjust gitignore files with precious declarations ([`ae86a6a`](https://github.com/Byron/gitoxide/commit/ae86a6a206074b85ff1eba32aea9c8b40c087b17)) diff --git a/gix-commitgraph/Cargo.toml b/gix-commitgraph/Cargo.toml index c1ae1d57edc..f7db2fe6917 100644 --- a/gix-commitgraph/Cargo.toml +++ b/gix-commitgraph/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "gix-commitgraph" -version = "0.24.1" +version = "0.24.2" repository = "https://github.com/Byron/gitoxide" documentation = "https://git-scm.com/docs/commit-graph#:~:text=The%20commit-graph%20file%20is%20a%20supplemental%20data%20structure,or%20in%20the%20info%20directory%20of%20an%20alternate." license = "MIT OR Apache-2.0" @@ -18,9 +18,9 @@ doctest = false serde = ["dep:serde", "gix-hash/serde", "bstr/serde"] [dependencies] -gix-features = { version = "^0.38.0", path = "../gix-features", features = ["rustsha1"] } -gix-hash = { version = "^0.14.1", path = "../gix-hash" } -gix-chunk = { version = "^0.4.7", path = "../gix-chunk" } +gix-features = { version = "^0.38.1", path = "../gix-features", features = ["rustsha1"] } +gix-hash = { version = "^0.14.2", path = "../gix-hash" } +gix-chunk = { version = "^0.4.8", path = "../gix-chunk" } bstr = { version = "1.3.0", default-features = false, features = ["std"] } memmap2 = "0.9.0" diff --git a/gix-commitgraph/src/file/access.rs b/gix-commitgraph/src/file/access.rs index 57c75ae569c..6ccb98cc322 100644 --- a/gix-commitgraph/src/file/access.rs +++ b/gix-commitgraph/src/file/access.rs @@ -1,5 +1,4 @@ use std::{ - convert::TryInto, fmt::{Debug, Formatter}, path::Path, }; diff --git a/gix-commitgraph/src/file/commit.rs b/gix-commitgraph/src/file/commit.rs index 14fe15e8413..db47cfcead9 100644 --- a/gix-commitgraph/src/file/commit.rs +++ b/gix-commitgraph/src/file/commit.rs @@ -1,6 +1,5 @@ //! Low-level operations on individual commits. use std::{ - convert::TryInto, fmt::{Debug, Formatter}, slice::Chunks, }; diff --git a/gix-commitgraph/src/file/init.rs b/gix-commitgraph/src/file/init.rs index 51c2739501f..47dc3616f80 100644 --- a/gix-commitgraph/src/file/init.rs +++ b/gix-commitgraph/src/file/init.rs @@ -1,11 +1,5 @@ +use std::path::Path; use std::path::PathBuf; -use std::{ - convert::{TryFrom, TryInto}, - path::Path, -}; - -use bstr::ByteSlice; -use memmap2::Mmap; use crate::{ file::{ @@ -14,6 +8,7 @@ use crate::{ }, File, }; +use bstr::ByteSlice; /// The error used in [`File::at()`]. #[derive(thiserror::Error, Debug)] @@ -246,7 +241,7 @@ impl TryFrom<&Path> for File { // SAFETY: we have to take the risk of somebody changing the file underneath. Git never writes into the same file. #[allow(unsafe_code)] unsafe { - Mmap::map(&file) + memmap2::MmapOptions::new().map_copy_read_only(&file) } }) .map_err(|e| Error::Io { diff --git a/gix-commitgraph/src/init.rs b/gix-commitgraph/src/init.rs index 0b03ba9462d..a1f98f3d24a 100644 --- a/gix-commitgraph/src/init.rs +++ b/gix-commitgraph/src/init.rs @@ -1,5 +1,4 @@ use std::{ - convert::TryFrom, io::{BufRead, BufReader}, path::{Path, PathBuf}, }; diff --git a/gix-commitgraph/src/lib.rs b/gix-commitgraph/src/lib.rs index d9f4d36bf2f..5d60b9c2ed4 100644 --- a/gix-commitgraph/src/lib.rs +++ b/gix-commitgraph/src/lib.rs @@ -51,6 +51,7 @@ pub fn at(path: impl AsRef) -> Result { mod access; pub mod file; /// +#[allow(clippy::empty_docs)] pub mod init; pub mod verify; diff --git a/gix-commitgraph/src/verify.rs b/gix-commitgraph/src/verify.rs index 56eb721e6e3..9a37cff83ad 100644 --- a/gix-commitgraph/src/verify.rs +++ b/gix-commitgraph/src/verify.rs @@ -2,7 +2,6 @@ use std::{ cmp::{max, min}, collections::BTreeMap, - convert::TryInto, path::PathBuf, }; diff --git a/gix-commitgraph/tests/access/mod.rs b/gix-commitgraph/tests/access/mod.rs index 12d7c2e6af5..914ef3af4ec 100644 --- a/gix-commitgraph/tests/access/mod.rs +++ b/gix-commitgraph/tests/access/mod.rs @@ -47,7 +47,7 @@ fn single_commit_future_64bit_dates_work() { assert_eq!( actual.committer_timestamp(), info.time.seconds.try_into().expect("timestamps in bound"), - "this is close the the highest representable value in the graph, like year 2500, so we are good for longer than I should care about" + "this is close the highest representable value in the graph, like year 2500, so we are good for longer than I should care about" ); assert_eq!(actual.generation(), 1); } diff --git a/gix-commitgraph/tests/commitgraph.rs b/gix-commitgraph/tests/commitgraph.rs index 94568fe5cfa..3444536ef8a 100644 --- a/gix-commitgraph/tests/commitgraph.rs +++ b/gix-commitgraph/tests/commitgraph.rs @@ -1,6 +1,5 @@ use std::{ collections::{HashMap, HashSet}, - convert::{TryFrom, TryInto}, hash::BuildHasher, io::{BufRead, Cursor}, path::Path, diff --git a/gix-commitgraph/tests/fixtures/generated-archives/generation_number_overflow.tar.xz b/gix-commitgraph/tests/fixtures/generated-archives/generation_number_overflow.tar.xz index 3ee3aa982c8..cdf5916afd8 100644 Binary files a/gix-commitgraph/tests/fixtures/generated-archives/generation_number_overflow.tar.xz and b/gix-commitgraph/tests/fixtures/generated-archives/generation_number_overflow.tar.xz differ diff --git a/gix-commitgraph/tests/fixtures/generated-archives/octopus_merges.tar.xz b/gix-commitgraph/tests/fixtures/generated-archives/octopus_merges.tar.xz index a0642ff6c00..159d76d1dd2 100644 Binary files a/gix-commitgraph/tests/fixtures/generated-archives/octopus_merges.tar.xz and b/gix-commitgraph/tests/fixtures/generated-archives/octopus_merges.tar.xz differ diff --git a/gix-commitgraph/tests/fixtures/generated-archives/single_commit.tar.xz b/gix-commitgraph/tests/fixtures/generated-archives/single_commit.tar.xz index 3245729f1f4..43986a0a812 100644 Binary files a/gix-commitgraph/tests/fixtures/generated-archives/single_commit.tar.xz and b/gix-commitgraph/tests/fixtures/generated-archives/single_commit.tar.xz differ diff --git a/gix-commitgraph/tests/fixtures/generated-archives/single_commit_huge_dates.tar.xz b/gix-commitgraph/tests/fixtures/generated-archives/single_commit_huge_dates.tar.xz index 26a0f802867..95657e6f1a8 100644 Binary files a/gix-commitgraph/tests/fixtures/generated-archives/single_commit_huge_dates.tar.xz and b/gix-commitgraph/tests/fixtures/generated-archives/single_commit_huge_dates.tar.xz differ diff --git a/gix-commitgraph/tests/fixtures/generated-archives/single_parent.tar.xz b/gix-commitgraph/tests/fixtures/generated-archives/single_parent.tar.xz index e9e0ee1a197..dbec6da6322 100644 Binary files a/gix-commitgraph/tests/fixtures/generated-archives/single_parent.tar.xz and b/gix-commitgraph/tests/fixtures/generated-archives/single_parent.tar.xz differ diff --git a/gix-commitgraph/tests/fixtures/generated-archives/two_parents.tar.xz b/gix-commitgraph/tests/fixtures/generated-archives/two_parents.tar.xz index 20fc6202c16..98f7748f120 100644 Binary files a/gix-commitgraph/tests/fixtures/generated-archives/two_parents.tar.xz and b/gix-commitgraph/tests/fixtures/generated-archives/two_parents.tar.xz differ diff --git a/gix-commitgraph/tests/fixtures/generation_number_overflow.sh b/gix-commitgraph/tests/fixtures/generation_number_overflow.sh index 2592c92d4c9..1bbe2dfdf31 100755 --- a/gix-commitgraph/tests/fixtures/generation_number_overflow.sh +++ b/gix-commitgraph/tests/fixtures/generation_number_overflow.sh @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash set -eu -o pipefail function tick () { diff --git a/gix-commitgraph/tests/fixtures/octopus_merges.sh b/gix-commitgraph/tests/fixtures/octopus_merges.sh index 812dfbc7e92..89106361158 100755 --- a/gix-commitgraph/tests/fixtures/octopus_merges.sh +++ b/gix-commitgraph/tests/fixtures/octopus_merges.sh @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash set -eu -o pipefail git init -q diff --git a/gix-commitgraph/tests/fixtures/single_commit.sh b/gix-commitgraph/tests/fixtures/single_commit.sh index 0f450431098..227453b2cd0 100755 --- a/gix-commitgraph/tests/fixtures/single_commit.sh +++ b/gix-commitgraph/tests/fixtures/single_commit.sh @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash set -eu -o pipefail # The goal with this repo is to have the smallest commit-graph file possible, in the hopes that an diff --git a/gix-commitgraph/tests/fixtures/single_commit_huge_dates.sh b/gix-commitgraph/tests/fixtures/single_commit_huge_dates.sh index 19ffba26770..28fe6f47f4a 100755 --- a/gix-commitgraph/tests/fixtures/single_commit_huge_dates.sh +++ b/gix-commitgraph/tests/fixtures/single_commit_huge_dates.sh @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash set -eu -o pipefail function setup_repo() { diff --git a/gix-commitgraph/tests/fixtures/single_parent.sh b/gix-commitgraph/tests/fixtures/single_parent.sh index 67cf24012f7..5395bb6376a 100755 --- a/gix-commitgraph/tests/fixtures/single_parent.sh +++ b/gix-commitgraph/tests/fixtures/single_parent.sh @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash set -eu -o pipefail git init -q diff --git a/gix-commitgraph/tests/fixtures/split_chain.sh b/gix-commitgraph/tests/fixtures/split_chain.sh index 284f4fc7eb9..4316d5045f0 100755 --- a/gix-commitgraph/tests/fixtures/split_chain.sh +++ b/gix-commitgraph/tests/fixtures/split_chain.sh @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash set -eu -o pipefail git init -q diff --git a/gix-commitgraph/tests/fixtures/two_parents.sh b/gix-commitgraph/tests/fixtures/two_parents.sh index ba4a8174365..aa2fd7dbe92 100755 --- a/gix-commitgraph/tests/fixtures/two_parents.sh +++ b/gix-commitgraph/tests/fixtures/two_parents.sh @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash set -eu -o pipefail git init -q diff --git a/gix-config-value/CHANGELOG.md b/gix-config-value/CHANGELOG.md index 5ee5eb3be87..9499020c402 100644 --- a/gix-config-value/CHANGELOG.md +++ b/gix-config-value/CHANGELOG.md @@ -5,6 +5,31 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## 0.14.6 (2024-03-14) + +A maintenance release without user-facing changes. + +### Commit Statistics + + + + - 3 commits contributed to the release over the course of 4 calendar days. + - 18 days passed between releases. + - 0 commits were understood as [conventional](https://www.conventionalcommits.org). + - 0 issues like '(#ID)' were seen in commit messages + +### Commit Details + + + +
view details + + * **Uncategorized** + - Prepare changelogs prior to release ([`52c3bbd`](https://github.com/Byron/gitoxide/commit/52c3bbd36b9e94a0f3a78b4ada84d0c08eba27f6)) + - Merge branch 'status' ([`3e5c974`](https://github.com/Byron/gitoxide/commit/3e5c974dd62ac134711c6c2f5a5490187a6ea55e)) + - Fix lints for nightly, and clippy ([`f8ce3d0`](https://github.com/Byron/gitoxide/commit/f8ce3d0721b6a53713a9392f2451874f520bc44c)) +
+ ## 0.14.5 (2024-02-25) A maintenance release without user-facing changes. @@ -13,7 +38,7 @@ A maintenance release without user-facing changes. - - 4 commits contributed to the release over the course of 30 calendar days. + - 5 commits contributed to the release over the course of 30 calendar days. - 36 days passed between releases. - 0 commits were understood as [conventional](https://www.conventionalcommits.org). - 0 issues like '(#ID)' were seen in commit messages @@ -25,6 +50,7 @@ A maintenance release without user-facing changes.
view details * **Uncategorized** + - Release gix-date v0.8.4, gix-utils v0.1.10, gix-actor v0.30.1, gix-object v0.41.1, gix-path v0.10.6, gix-glob v0.16.1, gix-quote v0.4.11, gix-attributes v0.22.1, gix-command v0.3.5, gix-filter v0.10.0, gix-commitgraph v0.24.1, gix-worktree-stream v0.10.0, gix-archive v0.10.0, gix-config-value v0.14.5, gix-ref v0.42.0, gix-sec v0.10.5, gix-config v0.35.0, gix-prompt v0.8.3, gix-url v0.27.1, gix-credentials v0.24.1, gix-ignore v0.11.1, gix-index v0.30.0, gix-worktree v0.31.0, gix-diff v0.41.0, gix-discover v0.30.0, gix-pathspec v0.7.0, gix-dir v0.1.0, gix-pack v0.48.0, gix-odb v0.58.0, gix-transport v0.41.1, gix-protocol v0.44.1, gix-revision v0.26.1, gix-refspec v0.22.1, gix-status v0.6.0, gix-submodule v0.9.0, gix-worktree-state v0.8.0, gix v0.59.0, gix-fsck v0.3.0, gitoxide-core v0.36.0, gitoxide v0.34.0, safety bump 10 crates ([`45b4470`](https://github.com/Byron/gitoxide/commit/45b447045bc826f252129c300c531acde2652c64)) - Prepare changelogs prior to release ([`f2e111f`](https://github.com/Byron/gitoxide/commit/f2e111f768fc1bc6182355261c20b63610cffec7)) - Merge branch 'dirwalk' ([`face359`](https://github.com/Byron/gitoxide/commit/face359443ba33e8985ec1525d5ec38b743ea7a9)) - Adjust gitignore files with precious declarations ([`ae86a6a`](https://github.com/Byron/gitoxide/commit/ae86a6a206074b85ff1eba32aea9c8b40c087b17)) diff --git a/gix-config-value/Cargo.toml b/gix-config-value/Cargo.toml index d92c87a8c39..78aaa2d7b88 100644 --- a/gix-config-value/Cargo.toml +++ b/gix-config-value/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "gix-config-value" -version = "0.14.5" +version = "0.14.6" repository = "https://github.com/Byron/gitoxide" license = "MIT OR Apache-2.0" description = "A crate of the gitoxide project providing git-config value parsing" @@ -17,7 +17,7 @@ doctest = false serde = ["dep:serde", "bstr/serde"] [dependencies] -gix-path = { version = "^0.10.6", path = "../gix-path" } +gix-path = { version = "^0.10.7", path = "../gix-path" } thiserror = "1.0.32" bstr = { version = "1.0.1", default-features = false, features = ["std"] } diff --git a/gix-config-value/src/boolean.rs b/gix-config-value/src/boolean.rs index 908e11a300a..ff146e7f17c 100644 --- a/gix-config-value/src/boolean.rs +++ b/gix-config-value/src/boolean.rs @@ -1,4 +1,4 @@ -use std::{borrow::Cow, convert::TryFrom, ffi::OsString, fmt::Display}; +use std::{borrow::Cow, ffi::OsString, fmt::Display}; use bstr::{BStr, BString, ByteSlice}; diff --git a/gix-config-value/src/color.rs b/gix-config-value/src/color.rs index 5cc0b997f40..884a346d37a 100644 --- a/gix-config-value/src/color.rs +++ b/gix-config-value/src/color.rs @@ -1,5 +1,5 @@ #![allow(missing_docs)] -use std::{borrow::Cow, convert::TryFrom, fmt::Display, str::FromStr}; +use std::{borrow::Cow, fmt::Display, str::FromStr}; use bstr::{BStr, BString}; diff --git a/gix-config-value/src/integer.rs b/gix-config-value/src/integer.rs index 4286a83b5be..4864b4b3e41 100644 --- a/gix-config-value/src/integer.rs +++ b/gix-config-value/src/integer.rs @@ -1,4 +1,4 @@ -use std::{borrow::Cow, convert::TryFrom, fmt::Display, str::FromStr}; +use std::{borrow::Cow, fmt::Display, str::FromStr}; use bstr::{BStr, BString}; diff --git a/gix-config-value/src/lib.rs b/gix-config-value/src/lib.rs index b3be069e328..7dd7e89c9e1 100644 --- a/gix-config-value/src/lib.rs +++ b/gix-config-value/src/lib.rs @@ -37,10 +37,13 @@ impl Error { mod boolean; /// +#[allow(clippy::empty_docs)] pub mod color; /// +#[allow(clippy::empty_docs)] pub mod integer; /// +#[allow(clippy::empty_docs)] pub mod path; mod types; diff --git a/gix-config-value/src/path.rs b/gix-config-value/src/path.rs index 58b5970eecd..23550f7cb16 100644 --- a/gix-config-value/src/path.rs +++ b/gix-config-value/src/path.rs @@ -5,6 +5,7 @@ use bstr::BStr; use crate::Path; /// +#[allow(clippy::empty_docs)] pub mod interpolate { use std::path::PathBuf; diff --git a/gix-config-value/tests/value/boolean.rs b/gix-config-value/tests/value/boolean.rs index 8fe8b43f0d4..7af370c8c97 100644 --- a/gix-config-value/tests/value/boolean.rs +++ b/gix-config-value/tests/value/boolean.rs @@ -1,5 +1,3 @@ -use std::convert::TryFrom; - use gix_config_value::Boolean; use crate::b; diff --git a/gix-config-value/tests/value/color.rs b/gix-config-value/tests/value/color.rs index 18baaba4c92..7ff5b2bd649 100644 --- a/gix-config-value/tests/value/color.rs +++ b/gix-config-value/tests/value/color.rs @@ -110,8 +110,6 @@ mod attribute { } mod from_git { - use std::convert::TryFrom; - use bstr::BStr; use gix_config_value::Color; diff --git a/gix-config-value/tests/value/integer.rs b/gix-config-value/tests/value/integer.rs index 8a5d8016021..6caa7f3492e 100644 --- a/gix-config-value/tests/value/integer.rs +++ b/gix-config-value/tests/value/integer.rs @@ -1,5 +1,3 @@ -use std::convert::TryFrom; - use gix_config_value::{integer::Suffix, Integer}; use crate::b; diff --git a/gix-config/CHANGELOG.md b/gix-config/CHANGELOG.md index e77fe7a87bc..8ab8dc04880 100644 --- a/gix-config/CHANGELOG.md +++ b/gix-config/CHANGELOG.md @@ -5,6 +5,100 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## 0.37.0 (2024-05-22) + +A maintenance release without user-facing changes. + +### Commit Statistics + + + + - 5 commits contributed to the release over the course of 8 calendar days. + - 38 days passed between releases. + - 0 commits were understood as [conventional](https://www.conventionalcommits.org). + - 0 issues like '(#ID)' were seen in commit messages + +### Commit Details + + + +
view details + + * **Uncategorized** + - Adjust changelogs prior to release ([`9511416`](https://github.com/Byron/gitoxide/commit/9511416a6cd0c571233f958c165329c8705c2498)) + - Merge branch 'various-fixes' ([`d6cd449`](https://github.com/Byron/gitoxide/commit/d6cd44930fb204b06e2b70fc6965e7705530c47a)) + - Update dependencies ([`cd4de83`](https://github.com/Byron/gitoxide/commit/cd4de8327fc195eb862ab6e138f2315a87374f85)) + - Merge branch 'status' ([`04ef31e`](https://github.com/Byron/gitoxide/commit/04ef31e9d6f5332d49037a5a4c248ebbb5aaf92b)) + - Adapt to changes in `gix-pack` ([`bad5b48`](https://github.com/Byron/gitoxide/commit/bad5b48e4f0d865b0b0937f136d9a0041aa88046)) +
+ +## 0.36.1 (2024-04-13) + +### New Features + + - display path of included configuration file that couldn't be opened. + This will help with issues like these: https://github.com/o2sh/onefetch/issues/1277 + +### Commit Statistics + + + + - 10 commits contributed to the release over the course of 9 calendar days. + - 29 days passed between releases. + - 1 commit was understood as [conventional](https://www.conventionalcommits.org). + - 0 issues like '(#ID)' were seen in commit messages + +### Thanks Clippy + + + +[Clippy](https://github.com/rust-lang/rust-clippy) helped 1 time to make code idiomatic. + +### Commit Details + + + +
view details + + * **Uncategorized** + - Release gix-trace v0.1.9, gix-utils v0.1.12, gix-packetline-blocking v0.17.4, gix-filter v0.11.1, gix-fs v0.10.2, gix-traverse v0.39.0, gix-worktree-stream v0.12.0, gix-archive v0.12.0, gix-config v0.36.1, gix-url v0.27.3, gix-index v0.32.0, gix-worktree v0.33.0, gix-diff v0.43.0, gix-pathspec v0.7.3, gix-dir v0.4.0, gix-pack v0.50.0, gix-odb v0.60.0, gix-transport v0.42.0, gix-protocol v0.45.0, gix-status v0.9.0, gix-worktree-state v0.10.0, gix v0.62.0, gix-fsck v0.4.0, gitoxide-core v0.37.0, gitoxide v0.35.0, safety bump 14 crates ([`095c673`](https://github.com/Byron/gitoxide/commit/095c6739b2722a8b9af90776b435ef2da454c0e6)) + - Prepare changelogs prior to release ([`5755271`](https://github.com/Byron/gitoxide/commit/57552717f46f96c35ba4ddc0a64434354ef845e9)) + - Merge pull request #1341 from szepeviktor/typos ([`55f379b`](https://github.com/Byron/gitoxide/commit/55f379bc47065822d078393d83d30c0835a89782)) + - Fix typos ([`f72ecce`](https://github.com/Byron/gitoxide/commit/f72ecce45babcad2a0c9b73c79d01ff502907a57)) + - Merge branch 'add-topo-walk' ([`b590a9d`](https://github.com/Byron/gitoxide/commit/b590a9d2b6a273f76f0320d2b9fe1f679c08f549)) + - Thanks clippy ([`7f6bee5`](https://github.com/Byron/gitoxide/commit/7f6bee5452ee01638f89a0cec2d4ee2a6f0d0136)) + - Merge branch 'status' ([`45edd2e`](https://github.com/Byron/gitoxide/commit/45edd2ea66035adf526cb2f617873dcba60a2a9a)) + - Merge pull request #1337 from blinxen/main ([`52efa90`](https://github.com/Byron/gitoxide/commit/52efa90a2570e55aa266f0b6911a4c1dfcdd0f72)) + - [gix-config] Run parse::tests::section::size_of_events only on 64 bit architectures ([`0ce06a6`](https://github.com/Byron/gitoxide/commit/0ce06a696d738fdfc151a0ff1ad6e41130ebddd5)) + - Display path of included configuration file that couldn't be opened. ([`a5168aa`](https://github.com/Byron/gitoxide/commit/a5168aae699dca825cc0c33c0044f5465f20b02e)) +
+ +## 0.36.0 (2024-03-14) + +A maintenance release without user-facing changes. + +### Commit Statistics + + + + - 4 commits contributed to the release over the course of 4 calendar days. + - 18 days passed between releases. + - 0 commits were understood as [conventional](https://www.conventionalcommits.org). + - 0 issues like '(#ID)' were seen in commit messages + +### Commit Details + + + +
view details + + * **Uncategorized** + - Release gix-date v0.8.5, gix-hash v0.14.2, gix-trace v0.1.8, gix-utils v0.1.11, gix-features v0.38.1, gix-actor v0.31.0, gix-validate v0.8.4, gix-object v0.42.0, gix-path v0.10.7, gix-glob v0.16.2, gix-quote v0.4.12, gix-attributes v0.22.2, gix-command v0.3.6, gix-filter v0.11.0, gix-fs v0.10.1, gix-chunk v0.4.8, gix-commitgraph v0.24.2, gix-hashtable v0.5.2, gix-revwalk v0.13.0, gix-traverse v0.38.0, gix-worktree-stream v0.11.0, gix-archive v0.11.0, gix-config-value v0.14.6, gix-tempfile v13.1.1, gix-lock v13.1.1, gix-ref v0.43.0, gix-sec v0.10.6, gix-config v0.36.0, gix-prompt v0.8.4, gix-url v0.27.2, gix-credentials v0.24.2, gix-ignore v0.11.2, gix-bitmap v0.2.11, gix-index v0.31.0, gix-worktree v0.32.0, gix-diff v0.42.0, gix-discover v0.31.0, gix-pathspec v0.7.1, gix-dir v0.2.0, gix-macros v0.1.4, gix-mailmap v0.23.0, gix-negotiate v0.13.0, gix-pack v0.49.0, gix-odb v0.59.0, gix-packetline v0.17.4, gix-transport v0.41.2, gix-protocol v0.44.2, gix-revision v0.27.0, gix-refspec v0.23.0, gix-status v0.7.0, gix-submodule v0.10.0, gix-worktree-state v0.9.0, gix v0.60.0, safety bump 26 crates ([`b050327`](https://github.com/Byron/gitoxide/commit/b050327e76f234b19be921b78b7b28e034319fdb)) + - Prepare changelogs prior to release ([`52c3bbd`](https://github.com/Byron/gitoxide/commit/52c3bbd36b9e94a0f3a78b4ada84d0c08eba27f6)) + - Merge branch 'status' ([`3e5c974`](https://github.com/Byron/gitoxide/commit/3e5c974dd62ac134711c6c2f5a5490187a6ea55e)) + - Fix lints for nightly, and clippy ([`f8ce3d0`](https://github.com/Byron/gitoxide/commit/f8ce3d0721b6a53713a9392f2451874f520bc44c)) +
+ ## 0.35.0 (2024-02-25) A maintenance release without user-facing changes. @@ -13,7 +107,7 @@ A maintenance release without user-facing changes. - - 15 commits contributed to the release over the course of 33 calendar days. + - 16 commits contributed to the release over the course of 33 calendar days. - 36 days passed between releases. - 0 commits were understood as [conventional](https://www.conventionalcommits.org). - 0 issues like '(#ID)' were seen in commit messages @@ -31,6 +125,7 @@ A maintenance release without user-facing changes.
view details * **Uncategorized** + - Release gix-date v0.8.4, gix-utils v0.1.10, gix-actor v0.30.1, gix-object v0.41.1, gix-path v0.10.6, gix-glob v0.16.1, gix-quote v0.4.11, gix-attributes v0.22.1, gix-command v0.3.5, gix-filter v0.10.0, gix-commitgraph v0.24.1, gix-worktree-stream v0.10.0, gix-archive v0.10.0, gix-config-value v0.14.5, gix-ref v0.42.0, gix-sec v0.10.5, gix-config v0.35.0, gix-prompt v0.8.3, gix-url v0.27.1, gix-credentials v0.24.1, gix-ignore v0.11.1, gix-index v0.30.0, gix-worktree v0.31.0, gix-diff v0.41.0, gix-discover v0.30.0, gix-pathspec v0.7.0, gix-dir v0.1.0, gix-pack v0.48.0, gix-odb v0.58.0, gix-transport v0.41.1, gix-protocol v0.44.1, gix-revision v0.26.1, gix-refspec v0.22.1, gix-status v0.6.0, gix-submodule v0.9.0, gix-worktree-state v0.8.0, gix v0.59.0, gix-fsck v0.3.0, gitoxide-core v0.36.0, gitoxide v0.34.0, safety bump 10 crates ([`45b4470`](https://github.com/Byron/gitoxide/commit/45b447045bc826f252129c300c531acde2652c64)) - Prepare changelogs prior to release ([`f2e111f`](https://github.com/Byron/gitoxide/commit/f2e111f768fc1bc6182355261c20b63610cffec7)) - Merge pull request #1290 from epage/winnow ([`a663e9f`](https://github.com/Byron/gitoxide/commit/a663e9fcdb5a3aedc9200da77ebae17d5c3e7135)) - Update winnow to 0.6 ([`e175b20`](https://github.com/Byron/gitoxide/commit/e175b20d431faa6859fbcc52f78400e50f91cad1)) @@ -3006,9 +3101,93 @@ This is a maintenance release without functional changes. - `len` - `from_env` - `open` + - `len` + - `from_env` + - `open` + - `len` + - `from_env` + - `open` + - `len` + - `from_env` + - `open` + - `len` + - `from_env` + - `open` + - `len` + - `from_env` + - `open` + - `len` + - `from_env` + - `open` + - `len` + - `from_env` + - `open` + - `len` + - `from_env` + - `open` + - `len` + - `from_env` + - `open` + - `len` + - `from_env` + - `open` + - `len` + - `from_env` + - `open` + - `len` + - `from_env` + - `open` + - `len` + - `from_env` + - `open` + - `len` + - `from_env` + - `open` + - `len` + - `from_env` + - `open` + - `len` + - `from_env` + - `open` - `len` - `from_env` - `open` +- `len` +- `from_env` +- `open` +- `len` +- `from_env` +- `open` +- `len` +- `from_env` +- `open` +- `len` +- `from_env` +- `open` +- `len` +- `from_env` +- `open` +- `len` +- `from_env` +- `open` +- `len` +- `from_env` +- `open` +- `len` +- `from_env` +- `open` +- `len` +- `from_env` +- `open` +- `len` +- `from_env` +- `open` +- `len` +- `from_env` +- `open` +- `len` +- `from_env` +- `open` ### Changed @@ -3021,7 +3200,7 @@ This is a maintenance release without functional changes. - _None._ -lenfrom_envopenlenfrom_envopenlenfrom_envopenlenfrom_envopenlenfrom_envopenlenfrom_envopenlenfrom_envopenlenfrom_envopenlenfrom_envopenlenfrom_envopenlenfrom_envopenlenfrom_envopenlenfrom_envopenlenfrom_envopenlenfrom_envopenlenfrom_envopenlenfrom_envopenlenfrom_envopenlenfrom_envopenlenfrom_envopenlenfrom_envopenlenfrom_envopenlenfrom_envopenlenfrom_envopenlenfrom_envopenlenfrom_envopenlenfrom_envopenlenfrom_envopenlenfrom_envopenlenfrom_envopenlenfrom_envopenlenfrom_envopenlenfrom_envopenlenfrom_envopenlenfrom_envopenlenfrom_envopenlenfrom_envopenlenfrom_envopenlenfrom_envopenlenfrom_envopenlenfrom_envopenlenfrom_envopenlenfrom_envopenlenfrom_envopenlenfrom_envopenlenfrom_envopenlenfrom_envopenlenfrom_envopenlenfrom_envopenlenfrom_envopenlenfrom_envopenlenfrom_envopenlenfrom_envopenlenfrom_envopenlenfrom_envopenlenfrom_envopenlenfrom_envopenlenfrom_envopenlenfrom_envopen +lenfrom_envopenlenfrom_envopenlenfrom_envopenlenfrom_envopenlenfrom_envopenlenfrom_envopenlenfrom_envopenlenfrom_envopenlenfrom_envopenlenfrom_envopenlenfrom_envopenlenfrom_envopenlenfrom_envopenlenfrom_envopenlenfrom_envopenlenfrom_envopenlenfrom_envopenlenfrom_envopenlenfrom_envopenlenfrom_envopenlenfrom_envopenlenfrom_envopenlenfrom_envopenlenfrom_envopenlenfrom_envopenlenfrom_envopenlenfrom_envopenlenfrom_envopenlenfrom_envopenlenfrom_envopenlenfrom_envopenlenfrom_envopenlenfrom_envopenlenfrom_envopenlenfrom_envopenlenfrom_envopenlenfrom_envopenlenfrom_envopenlenfrom_envopenlenfrom_envopenlenfrom_envopenlenfrom_envopenlenfrom_envopenlenfrom_envopenlenfrom_envopenlenfrom_envopenlenfrom_envopenlenfrom_envopenlenfrom_envopenlenfrom_envopenlenfrom_envopenlenfrom_envopenlenfrom_envopenlenfrom_envopenlenfrom_envopenlenfrom_envopenlenfrom_envopenlenfrom_envopenlenfrom_envopenlenfrom_envopenlenfrom_envopenlenfrom_envopenlenfrom_envopenlenfrom_envopenlenfrom_envopenlenfrom_envopenlenfrom_envopenlenfrom_envopenlenfrom_envopenlenfrom_envopenlenfrom_envopenlenfrom_envopenlenfrom_envopenlenfrom_envopenlenfrom_envopenlenfrom_envopenlenfrom_envopenlenfrom_envopen ## v0.1.1 (2021-05-09) diff --git a/gix-config/Cargo.toml b/gix-config/Cargo.toml index 1695bd503ff..e3df00b99eb 100644 --- a/gix-config/Cargo.toml +++ b/gix-config/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "gix-config" -version = "0.35.0" +version = "0.37.0" repository = "https://github.com/Byron/gitoxide" description = "A git-config file parser and editor from the gitoxide project" license = "MIT OR Apache-2.0" @@ -17,12 +17,12 @@ autotests = false serde = ["dep:serde", "bstr/serde", "gix-sec/serde", "gix-ref/serde", "gix-glob/serde", "gix-config-value/serde"] [dependencies] -gix-features = { version = "^0.38.0", path = "../gix-features"} -gix-config-value = { version = "^0.14.5", path = "../gix-config-value" } -gix-path = { version = "^0.10.6", path = "../gix-path" } -gix-sec = { version = "^0.10.5", path = "../gix-sec" } -gix-ref = { version = "^0.42.0", path = "../gix-ref" } -gix-glob = { version = "^0.16.1", path = "../gix-glob" } +gix-features = { version = "^0.38.2", path = "../gix-features"} +gix-config-value = { version = "^0.14.6", path = "../gix-config-value" } +gix-path = { version = "^0.10.7", path = "../gix-path" } +gix-sec = { version = "^0.10.6", path = "../gix-sec" } +gix-ref = { version = "^0.44.1", path = "../gix-ref" } +gix-glob = { version = "^0.16.3", path = "../gix-glob" } winnow = { version = "0.6.0", features = ["simd"] } memchr = "2" diff --git a/gix-config/benches/large_config_file.rs b/gix-config/benches/large_config_file.rs index 51f6e1fd86b..ff42afb6f1c 100644 --- a/gix-config/benches/large_config_file.rs +++ b/gix-config/benches/large_config_file.rs @@ -1,5 +1,3 @@ -use std::convert::TryFrom; - use criterion::{black_box, criterion_group, criterion_main, Criterion}; use gix_config::{parse::Events, File}; diff --git a/gix-config/src/file/access/comfort.rs b/gix-config/src/file/access/comfort.rs index 588188608d6..c2a221a4028 100644 --- a/gix-config/src/file/access/comfort.rs +++ b/gix-config/src/file/access/comfort.rs @@ -1,4 +1,4 @@ -use std::{borrow::Cow, convert::TryFrom}; +use std::borrow::Cow; use bstr::BStr; diff --git a/gix-config/src/file/access/raw.rs b/gix-config/src/file/access/raw.rs index 1be5efe724b..26aaae39691 100644 --- a/gix-config/src/file/access/raw.rs +++ b/gix-config/src/file/access/raw.rs @@ -1,4 +1,4 @@ -use std::{borrow::Cow, collections::HashMap, convert::TryInto}; +use std::{borrow::Cow, collections::HashMap}; use bstr::BStr; use smallvec::ToSmallVec; diff --git a/gix-config/src/file/access/read_only.rs b/gix-config/src/file/access/read_only.rs index 15ed33ad491..96020a681ca 100644 --- a/gix-config/src/file/access/read_only.rs +++ b/gix-config/src/file/access/read_only.rs @@ -1,4 +1,4 @@ -use std::{borrow::Cow, convert::TryFrom}; +use std::borrow::Cow; use bstr::{BStr, ByteSlice}; use gix_features::threading::OwnShared; diff --git a/gix-config/src/file/impls.rs b/gix-config/src/file/impls.rs index c26df5fb81a..d6ac2b76a8f 100644 --- a/gix-config/src/file/impls.rs +++ b/gix-config/src/file/impls.rs @@ -1,4 +1,4 @@ -use std::{borrow::Cow, convert::TryFrom, fmt::Display, str::FromStr}; +use std::{borrow::Cow, fmt::Display, str::FromStr}; use bstr::{BStr, BString, ByteVec}; @@ -42,7 +42,7 @@ impl<'a> TryFrom<&'a BStr> for File<'a> { impl From> for BString { fn from(c: File<'_>) -> Self { - c.into() + c.to_bstring() } } diff --git a/gix-config/src/file/includes/mod.rs b/gix-config/src/file/includes/mod.rs index 8fd92725fba..98a68482558 100644 --- a/gix-config/src/file/includes/mod.rs +++ b/gix-config/src/file/includes/mod.rs @@ -99,7 +99,14 @@ fn append_followed_includes_recursively( } buf.clear(); - std::io::copy(&mut std::fs::File::open(&config_path)?, buf)?; + std::io::copy( + &mut std::fs::File::open(&config_path).map_err(|err| Error::Io { + source: err, + path: config_path.to_owned(), + })?, + buf, + ) + .map_err(Error::CopyBuffer)?; let config_meta = Metadata { path: Some(config_path), trust: meta.trust, diff --git a/gix-config/src/file/includes/types.rs b/gix-config/src/file/includes/types.rs index 64306bd9ca0..85e3a35db89 100644 --- a/gix-config/src/file/includes/types.rs +++ b/gix-config/src/file/includes/types.rs @@ -1,11 +1,14 @@ use crate::{parse, path::interpolate}; +use std::path::PathBuf; /// The error returned when following includes. #[derive(Debug, thiserror::Error)] #[allow(missing_docs)] pub enum Error { - #[error(transparent)] - Io(#[from] std::io::Error), + #[error("Failed to copy configuration file into buffer")] + CopyBuffer(#[source] std::io::Error), + #[error("Could not read included configuration file at '{}'", path.display())] + Io { path: PathBuf, source: std::io::Error }, #[error(transparent)] Parse(#[from] parse::Error), #[error(transparent)] @@ -115,6 +118,7 @@ impl Default for Options<'_> { } /// +#[allow(clippy::empty_docs)] pub mod conditional { /// Options to handle conditional includes like `includeIf..path`. #[derive(Clone, Copy, Default)] diff --git a/gix-config/src/file/init/comfort.rs b/gix-config/src/file/init/comfort.rs index 904dde9f74d..235a02d61d8 100644 --- a/gix-config/src/file/init/comfort.rs +++ b/gix-config/src/file/init/comfort.rs @@ -141,6 +141,7 @@ impl File<'static> { } /// +#[allow(clippy::empty_docs)] pub mod from_git_dir { use crate::file::init; diff --git a/gix-config/src/file/init/mod.rs b/gix-config/src/file/init/mod.rs index 90fa70cbcf9..1076011fcc6 100644 --- a/gix-config/src/file/init/mod.rs +++ b/gix-config/src/file/init/mod.rs @@ -10,8 +10,10 @@ pub use types::{Error, Options}; mod comfort; /// +#[allow(clippy::empty_docs)] pub mod from_env; /// +#[allow(clippy::empty_docs)] pub mod from_paths; impl<'a> File<'a> { diff --git a/gix-config/src/file/mod.rs b/gix-config/src/file/mod.rs index e99c6eb94d9..8611ad0c7e1 100644 --- a/gix-config/src/file/mod.rs +++ b/gix-config/src/file/mod.rs @@ -13,19 +13,23 @@ mod mutable; pub use mutable::{multi_value::MultiValueMut, section::SectionMut, value::ValueMut}; /// +#[allow(clippy::empty_docs)] pub mod init; mod access; mod impls; /// +#[allow(clippy::empty_docs)] pub mod includes; mod meta; mod util; /// +#[allow(clippy::empty_docs)] pub mod section; /// +#[allow(clippy::empty_docs)] pub mod rename_section { /// The error returned by [`File::rename_section(…)`][crate::File::rename_section()]. #[derive(Debug, thiserror::Error)] @@ -39,6 +43,7 @@ pub mod rename_section { } /// +#[allow(clippy::empty_docs)] pub mod set_raw_value { /// The error returned by [`File::set_raw_value(…)`][crate::File::set_raw_value()]. #[derive(Debug, thiserror::Error)] diff --git a/gix-config/src/file/section/mod.rs b/gix-config/src/file/section/mod.rs index f07a145e3d4..1dd4bd15ad3 100644 --- a/gix-config/src/file/section/mod.rs +++ b/gix-config/src/file/section/mod.rs @@ -75,7 +75,7 @@ impl<'a> Section<'a> { /// Stream ourselves to the given `out`, in order to reproduce this section mostly losslessly /// as it was parsed. pub fn write_to(&self, mut out: &mut dyn std::io::Write) -> std::io::Result<()> { - self.header.write_to(&mut out)?; + self.header.write_to(&mut *out)?; if self.body.0.is_empty() { return Ok(()); diff --git a/gix-config/src/file/tests.rs b/gix-config/src/file/tests.rs index 58f16ec1547..adb11e5a291 100644 --- a/gix-config/src/file/tests.rs +++ b/gix-config/src/file/tests.rs @@ -6,7 +6,7 @@ use crate::{ }; mod try_from { - use std::{borrow::Cow, collections::HashMap, convert::TryFrom}; + use std::{borrow::Cow, collections::HashMap}; use super::{bodies, headers}; use crate::{ diff --git a/gix-config/src/file/util.rs b/gix-config/src/file/util.rs index 5c60f1fd5a0..78bc4736f63 100644 --- a/gix-config/src/file/util.rs +++ b/gix-config/src/file/util.rs @@ -118,8 +118,7 @@ impl<'event> File<'event> { &'a self, section_name: &'a str, subsection_name: Option<&BStr>, - ) -> Result + ExactSizeIterator + DoubleEndedIterator + '_, lookup::existing::Error> - { + ) -> Result + DoubleEndedIterator + '_, lookup::existing::Error> { let section_name = section::Name::from_str_unchecked(section_name); let section_ids = self .section_lookup_tree diff --git a/gix-config/src/file/write.rs b/gix-config/src/file/write.rs index 772054f9529..f08f5db57a4 100644 --- a/gix-config/src/file/write.rs +++ b/gix-config/src/file/write.rs @@ -18,7 +18,7 @@ impl File<'_> { pub fn write_to_filter( &self, mut out: &mut dyn std::io::Write, - mut filter: &mut dyn FnMut(&Section<'_>) -> bool, + filter: &mut dyn FnMut(&Section<'_>) -> bool, ) -> std::io::Result<()> { let nl = self.detect_newline_style(); @@ -27,7 +27,8 @@ impl File<'_> { event.write_to(&mut out)?; } - if !ends_with_newline(self.frontmatter_events.as_ref(), nl, true) && self.sections.values().any(&mut filter) + if !ends_with_newline(self.frontmatter_events.as_ref(), nl, true) + && self.sections.values().any(&mut *filter) { out.write_all(nl)?; } diff --git a/gix-config/src/lib.rs b/gix-config/src/lib.rs index a51f6ec0ba1..76c485d0650 100644 --- a/gix-config/src/lib.rs +++ b/gix-config/src/lib.rs @@ -40,9 +40,11 @@ pub mod file; /// +#[allow(clippy::empty_docs)] pub mod lookup; pub mod parse; /// +#[allow(clippy::empty_docs)] pub mod value; pub use gix_config_value::{color, integer, path, Boolean, Color, Integer, Path}; @@ -52,6 +54,7 @@ pub use traits::Key; mod types; pub use types::{File, Source}; /// +#[allow(clippy::empty_docs)] pub mod source; #[cfg(test)] diff --git a/gix-config/src/lookup.rs b/gix-config/src/lookup.rs index 7814978125d..25d8d9d1751 100644 --- a/gix-config/src/lookup.rs +++ b/gix-config/src/lookup.rs @@ -9,6 +9,7 @@ pub enum Error { } /// +#[allow(clippy::empty_docs)] pub mod existing { /// The error when looking up a value that doesn't exist, for example via [`File::value()`][crate::File::value()]. #[derive(Debug, thiserror::Error)] diff --git a/gix-config/src/parse/comment.rs b/gix-config/src/parse/comment.rs index 6d4bb15ffb5..6d7ec145b65 100644 --- a/gix-config/src/parse/comment.rs +++ b/gix-config/src/parse/comment.rs @@ -39,7 +39,7 @@ impl Display for Comment<'_> { impl From> for BString { fn from(c: Comment<'_>) -> Self { - c.into() + c.to_bstring() } } diff --git a/gix-config/src/parse/event.rs b/gix-config/src/parse/event.rs index f528e2077d6..653a0c67751 100644 --- a/gix-config/src/parse/event.rs +++ b/gix-config/src/parse/event.rs @@ -33,7 +33,7 @@ impl Event<'_> { /// Stream ourselves to the given `out`, in order to reproduce this event mostly losslessly /// as it was parsed. - pub fn write_to(&self, mut out: &mut dyn std::io::Write) -> std::io::Result<()> { + pub fn write_to(&self, out: &mut dyn std::io::Write) -> std::io::Result<()> { match self { Self::ValueNotDone(e) => { out.write_all(e.as_ref())?; @@ -42,8 +42,8 @@ impl Event<'_> { Self::Whitespace(e) | Self::Newline(e) | Self::Value(e) | Self::ValueDone(e) => out.write_all(e.as_ref()), Self::KeyValueSeparator => out.write_all(b"="), Self::SectionKey(k) => out.write_all(k.0.as_ref()), - Self::SectionHeader(h) => h.write_to(&mut out), - Self::Comment(c) => c.write_to(&mut out), + Self::SectionHeader(h) => h.write_to(out), + Self::Comment(c) => c.write_to(out), } } @@ -72,7 +72,7 @@ impl Display for Event<'_> { impl From> for BString { fn from(event: Event<'_>) -> Self { - event.into() + event.to_bstring() } } diff --git a/gix-config/src/parse/events.rs b/gix-config/src/parse/events.rs index d742bafe04c..feda7a7ca6c 100644 --- a/gix-config/src/parse/events.rs +++ b/gix-config/src/parse/events.rs @@ -1,5 +1,3 @@ -use std::convert::TryFrom; - use smallvec::SmallVec; use crate::{ @@ -30,26 +28,26 @@ pub type FrontMatterEvents<'a> = SmallVec<[Event<'a>; 8]>; /// /// For concrete examples, some notable differences are: /// - `git-config` sections permit subsections via either a quoted string -/// (`[some-section "subsection"]`) or via the deprecated dot notation -/// (`[some-section.subsection]`). Successful parsing these section names is not -/// well defined in typical `.ini` parsers. This parser will handle these cases -/// perfectly. +/// (`[some-section "subsection"]`) or via the deprecated dot notation +/// (`[some-section.subsection]`). Successful parsing these section names is not +/// well defined in typical `.ini` parsers. This parser will handle these cases +/// perfectly. /// - Comment markers are not strictly defined either. This parser will always -/// and only handle a semicolon or octothorpe (also known as a hash or number -/// sign). +/// and only handle a semicolon or octothorpe (also known as a hash or number +/// sign). /// - Global properties may be allowed in `.ini` parsers, but is strictly -/// disallowed by this parser. +/// disallowed by this parser. /// - Only `\t`, `\n`, `\b` `\\` are valid escape characters. /// - Quoted and semi-quoted values will be parsed (but quotes will be included -/// in event outputs). An example of a semi-quoted value is `5"hello world"`, -/// which should be interpreted as `5hello world` after -/// [normalization][crate::value::normalize()]. +/// in event outputs). An example of a semi-quoted value is `5"hello world"`, +/// which should be interpreted as `5hello world` after +/// [normalization][crate::value::normalize()]. /// - Line continuations via a `\` character is supported (inside or outside of quotes) /// - Whitespace handling similarly follows the `git-config` specification as -/// closely as possible, where excess whitespace after a non-quoted value are -/// trimmed, and line continuations onto a new line with excess spaces are kept. +/// closely as possible, where excess whitespace after a non-quoted value are +/// trimmed, and line continuations onto a new line with excess spaces are kept. /// - Only equal signs (optionally padded by spaces) are valid name/value -/// delimiters. +/// delimiters. /// /// Note that things such as case-sensitivity or duplicate sections are /// _not_ handled. This parser is a low level _syntactic_ interpreter @@ -64,8 +62,8 @@ pub type FrontMatterEvents<'a> = SmallVec<[Event<'a>; 8]>; /// # Trait Implementations /// /// - This struct does _not_ implement [`FromStr`] due to lifetime -/// constraints implied on the required `from_str` method. Instead, it provides -/// [`From<&'_ str>`]. +/// constraints implied on the required `from_str` method. Instead, it provides +/// [`From<&'_ str>`]. /// /// # Idioms /// diff --git a/gix-config/src/parse/mod.rs b/gix-config/src/parse/mod.rs index 5127c9b6f44..3f30ad7f078 100644 --- a/gix-config/src/parse/mod.rs +++ b/gix-config/src/parse/mod.rs @@ -23,6 +23,7 @@ pub use events_type::{Events, FrontMatterEvents}; mod comment; mod error; /// +#[allow(clippy::empty_docs)] pub mod section; #[cfg(test)] diff --git a/gix-config/src/parse/nom/mod.rs b/gix-config/src/parse/nom/mod.rs index 4e8670d0209..adbeb4ab860 100644 --- a/gix-config/src/parse/nom/mod.rs +++ b/gix-config/src/parse/nom/mod.rs @@ -190,10 +190,10 @@ fn subsection_unescaped<'i>(i: &mut &'i [u8]) -> PResult<&'i [u8], NomError<&'i } fn subsection_escaped_char<'i>(i: &mut &'i [u8]) -> PResult<&'i [u8], NomError<&'i [u8]>> { - preceded('\\', one_of(is_subsection_escapeable_char).recognize()).parse_next(i) + preceded('\\', one_of(is_subsection_escapable_char).recognize()).parse_next(i) } -fn is_subsection_escapeable_char(c: u8) -> bool { +fn is_subsection_escapable_char(c: u8) -> bool { c != b'\n' } diff --git a/gix-config/src/parse/section/header.rs b/gix-config/src/parse/section/header.rs index ad1288f3fe0..5f4e382c90a 100644 --- a/gix-config/src/parse/section/header.rs +++ b/gix-config/src/parse/section/header.rs @@ -145,7 +145,7 @@ impl Display for Header<'_> { impl From> for BString { fn from(header: Header<'_>) -> Self { - header.into() + header.to_bstring() } } diff --git a/gix-config/src/parse/section/mod.rs b/gix-config/src/parse/section/mod.rs index fade4fa147f..5c4489ea670 100644 --- a/gix-config/src/parse/section/mod.rs +++ b/gix-config/src/parse/section/mod.rs @@ -5,6 +5,7 @@ use bstr::BStr; use crate::parse::{Event, Section}; /// +#[allow(clippy::empty_docs)] pub mod header; pub(crate) mod unvalidated; @@ -48,6 +49,7 @@ mod types { macro_rules! generate_case_insensitive { ($name:ident, $module:ident, $err_doc:literal, $validate:ident, $cow_inner_type:ty, $comment:literal) => { /// + #[allow(clippy::empty_docs)] pub mod $module { /// The error returned when `TryFrom` is invoked to create an instance. #[derive(Debug, thiserror::Error, Copy, Clone)] diff --git a/gix-config/src/parse/tests.rs b/gix-config/src/parse/tests.rs index edb5dee6ee3..e2fa8b3bb83 100644 --- a/gix-config/src/parse/tests.rs +++ b/gix-config/src/parse/tests.rs @@ -5,6 +5,7 @@ mod section { use std::borrow::Cow; #[test] + #[cfg(target_pointer_width = "64")] fn size_of_events() { assert_eq!( std::mem::size_of::>(), @@ -109,7 +110,7 @@ pub(crate) mod util { //! This module is only included for tests, and contains common unit test helper //! functions. - use std::{borrow::Cow, convert::TryFrom}; + use std::borrow::Cow; use crate::parse::{section, Comment, Event}; diff --git a/gix-config/tests/Cargo.toml b/gix-config/tests/Cargo.toml index 992589ad820..0448769115c 100644 --- a/gix-config/tests/Cargo.toml +++ b/gix-config/tests/Cargo.toml @@ -19,13 +19,13 @@ name = "mem" path = "mem.rs" [dev-dependencies] -gix-config = { path = ".."} -gix-testtools = { path = "../../tests/tools"} +gix-config = { path = ".." } +gix-testtools = { path = "../../tests/tools" } gix = { path = "../../gix", default-features = false } gix-ref = { path = "../../gix-ref" } gix-path = { path = "../../gix-path" } gix-sec = { path = "../../gix-sec" } -serial_test = { version = "2.0.0", default-features = false } +serial_test = { version = "3.1.0", default-features = false } bstr = { version = "1.3.0", default-features = false, features = ["std"] } bytesize = "1.3.0" diff --git a/gix-config/tests/file/access/mutate.rs b/gix-config/tests/file/access/mutate.rs index 0788a3a31a4..48826593f6c 100644 --- a/gix-config/tests/file/access/mutate.rs +++ b/gix-config/tests/file/access/mutate.rs @@ -1,6 +1,4 @@ mod remove_section { - use std::convert::TryFrom; - #[test] fn removal_of_all_sections_programmatically_with_sections_and_ids_by_name() { let mut file = gix_config::File::try_from("[core] \na = b\nb=c\n\n[core \"name\"]\nd = 1\ne = 2").unwrap(); @@ -47,7 +45,7 @@ mod remove_section { } } mod rename_section { - use std::{borrow::Cow, convert::TryFrom}; + use std::borrow::Cow; use gix_config::{file::rename_section, parse::section}; diff --git a/gix-config/tests/file/access/raw/raw_multi_value.rs b/gix-config/tests/file/access/raw/raw_multi_value.rs index ffdd955e82f..a0f1015a13e 100644 --- a/gix-config/tests/file/access/raw/raw_multi_value.rs +++ b/gix-config/tests/file/access/raw/raw_multi_value.rs @@ -1,5 +1,3 @@ -use std::convert::TryFrom; - use gix_config::{lookup, File}; use crate::file::cow_str; diff --git a/gix-config/tests/file/access/raw/raw_value.rs b/gix-config/tests/file/access/raw/raw_value.rs index 371df391ef1..2501de6db39 100644 --- a/gix-config/tests/file/access/raw/raw_value.rs +++ b/gix-config/tests/file/access/raw/raw_value.rs @@ -1,5 +1,3 @@ -use std::convert::TryFrom; - use gix_config::{lookup, File}; #[test] diff --git a/gix-config/tests/file/access/read_only.rs b/gix-config/tests/file/access/read_only.rs index 2fd71a5e6c0..2d8d9d0fd3b 100644 --- a/gix-config/tests/file/access/read_only.rs +++ b/gix-config/tests/file/access/read_only.rs @@ -1,4 +1,4 @@ -use std::{borrow::Cow, convert::TryFrom}; +use std::borrow::Cow; use bstr::BStr; use gix_config::{ diff --git a/gix-config/tests/file/impls/mod.rs b/gix-config/tests/file/impls/mod.rs index 885f326e1bb..5247d2ea7a2 100644 --- a/gix-config/tests/file/impls/mod.rs +++ b/gix-config/tests/file/impls/mod.rs @@ -1,5 +1,3 @@ -use std::convert::TryFrom; - use gix_config::File; #[test] diff --git a/gix-config/tests/file/init/from_paths/includes/conditional/onbranch.rs b/gix-config/tests/file/init/from_paths/includes/conditional/onbranch.rs index f2dbeb280dc..c6c2c7b2ffb 100644 --- a/gix-config/tests/file/init/from_paths/includes/conditional/onbranch.rs +++ b/gix-config/tests/file/init/from_paths/includes/conditional/onbranch.rs @@ -1,7 +1,4 @@ -use std::{ - convert::{TryFrom, TryInto}, - fs, -}; +use std::fs; use bstr::{BString, ByteSlice}; use gix_config::file::{ diff --git a/gix-config/tests/file/mutable/section.rs b/gix-config/tests/file/mutable/section.rs index 17ef1df586b..6ee00b30c87 100644 --- a/gix-config/tests/file/mutable/section.rs +++ b/gix-config/tests/file/mutable/section.rs @@ -90,8 +90,6 @@ mod pop { } mod set { - use std::convert::TryInto; - use super::multi_value_section; #[test] @@ -121,8 +119,6 @@ mod set { } mod push { - use std::convert::{TryFrom, TryInto}; - use gix_config::parse::section::Key; use crate::file::cow_str; @@ -230,7 +226,7 @@ mod push_with_comment { } mod set_leading_whitespace { - use std::{borrow::Cow, convert::TryFrom}; + use std::borrow::Cow; use bstr::BString; use gix_config::parse::section::Key; diff --git a/gix-config/tests/file/write.rs b/gix-config/tests/file/write.rs index 53b82cec079..99fce2d3aa1 100644 --- a/gix-config/tests/file/write.rs +++ b/gix-config/tests/file/write.rs @@ -1,5 +1,3 @@ -use std::convert::TryFrom; - use bstr::ByteVec; use gix_config::file::{init, Metadata}; diff --git a/gix-config/tests/fixtures/config_with_worktree_extension.sh b/gix-config/tests/fixtures/config_with_worktree_extension.sh index a3a5fe076de..31e6760a55e 100755 --- a/gix-config/tests/fixtures/config_with_worktree_extension.sh +++ b/gix-config/tests/fixtures/config_with_worktree_extension.sh @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash set -eu -o pipefail git init -q main-worktree diff --git a/gix-config/tests/fixtures/generated-archives/config_with_worktree_extension.tar.xz b/gix-config/tests/fixtures/generated-archives/config_with_worktree_extension.tar.xz index d9b03338679..55ed10a225c 100644 Binary files a/gix-config/tests/fixtures/generated-archives/config_with_worktree_extension.tar.xz and b/gix-config/tests/fixtures/generated-archives/config_with_worktree_extension.tar.xz differ diff --git a/gix-config/tests/fixtures/make_config_repo.sh b/gix-config/tests/fixtures/make_config_repo.sh index d4d3deb49d9..1d9b877f541 100755 --- a/gix-config/tests/fixtures/make_config_repo.sh +++ b/gix-config/tests/fixtures/make_config_repo.sh @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash set -eu -o pipefail git init -q diff --git a/gix-config/tests/mem.rs b/gix-config/tests/mem.rs index d8ceb761697..0ae0a1b829a 100644 --- a/gix-config/tests/mem.rs +++ b/gix-config/tests/mem.rs @@ -4,7 +4,7 @@ use std::alloc; use std::time::Instant; #[global_allocator] -static ALLOCATOR: Cap = Cap::new(alloc::System, usize::max_value()); +static ALLOCATOR: Cap = Cap::new(alloc::System, usize::MAX); #[test] fn usage() { diff --git a/gix-config/tests/parse/from_bytes.rs b/gix-config/tests/parse/from_bytes.rs index df58531ba01..736ddfedca0 100644 --- a/gix-config/tests/parse/from_bytes.rs +++ b/gix-config/tests/parse/from_bytes.rs @@ -1,5 +1,3 @@ -use gix_config::parse::Events; - use super::*; #[test] diff --git a/gix-config/tests/parse/mod.rs b/gix-config/tests/parse/mod.rs index 8219c985c61..4ce6d1a2f84 100644 --- a/gix-config/tests/parse/mod.rs +++ b/gix-config/tests/parse/mod.rs @@ -1,4 +1,4 @@ -use std::{borrow::Cow, convert::TryFrom}; +use std::borrow::Cow; use gix_config::parse::{Event, Events, Section}; diff --git a/gix-config/tests/parse/section.rs b/gix-config/tests/parse/section.rs index 872052e9031..afdff7d9350 100644 --- a/gix-config/tests/parse/section.rs +++ b/gix-config/tests/parse/section.rs @@ -70,8 +70,6 @@ mod header { } } mod name { - use std::convert::TryFrom; - use gix_config::parse::section::Name; #[test] @@ -90,7 +88,7 @@ mod name { } mod key { - use std::{cmp::Ordering, convert::TryFrom}; + use std::cmp::Ordering; use gix_config::parse::section::Key; diff --git a/gix-credentials/CHANGELOG.md b/gix-credentials/CHANGELOG.md index 8fb3daa97cb..8bb0eeb1d8e 100644 --- a/gix-credentials/CHANGELOG.md +++ b/gix-credentials/CHANGELOG.md @@ -5,6 +5,31 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## 0.24.2 (2024-03-14) + +A maintenance release without user-facing changes. + +### Commit Statistics + + + + - 3 commits contributed to the release over the course of 4 calendar days. + - 18 days passed between releases. + - 0 commits were understood as [conventional](https://www.conventionalcommits.org). + - 0 issues like '(#ID)' were seen in commit messages + +### Commit Details + + + +
view details + + * **Uncategorized** + - Prepare changelogs prior to release ([`52c3bbd`](https://github.com/Byron/gitoxide/commit/52c3bbd36b9e94a0f3a78b4ada84d0c08eba27f6)) + - Merge branch 'status' ([`3e5c974`](https://github.com/Byron/gitoxide/commit/3e5c974dd62ac134711c6c2f5a5490187a6ea55e)) + - Fix lints for nightly, and clippy ([`f8ce3d0`](https://github.com/Byron/gitoxide/commit/f8ce3d0721b6a53713a9392f2451874f520bc44c)) +
+ ## 0.24.1 (2024-02-25) A maintenance release without user-facing changes. @@ -13,7 +38,7 @@ A maintenance release without user-facing changes. - - 3 commits contributed to the release over the course of 30 calendar days. + - 4 commits contributed to the release over the course of 30 calendar days. - 36 days passed between releases. - 0 commits were understood as [conventional](https://www.conventionalcommits.org). - 0 issues like '(#ID)' were seen in commit messages @@ -25,6 +50,7 @@ A maintenance release without user-facing changes.
view details * **Uncategorized** + - Release gix-date v0.8.4, gix-utils v0.1.10, gix-actor v0.30.1, gix-object v0.41.1, gix-path v0.10.6, gix-glob v0.16.1, gix-quote v0.4.11, gix-attributes v0.22.1, gix-command v0.3.5, gix-filter v0.10.0, gix-commitgraph v0.24.1, gix-worktree-stream v0.10.0, gix-archive v0.10.0, gix-config-value v0.14.5, gix-ref v0.42.0, gix-sec v0.10.5, gix-config v0.35.0, gix-prompt v0.8.3, gix-url v0.27.1, gix-credentials v0.24.1, gix-ignore v0.11.1, gix-index v0.30.0, gix-worktree v0.31.0, gix-diff v0.41.0, gix-discover v0.30.0, gix-pathspec v0.7.0, gix-dir v0.1.0, gix-pack v0.48.0, gix-odb v0.58.0, gix-transport v0.41.1, gix-protocol v0.44.1, gix-revision v0.26.1, gix-refspec v0.22.1, gix-status v0.6.0, gix-submodule v0.9.0, gix-worktree-state v0.8.0, gix v0.59.0, gix-fsck v0.3.0, gitoxide-core v0.36.0, gitoxide v0.34.0, safety bump 10 crates ([`45b4470`](https://github.com/Byron/gitoxide/commit/45b447045bc826f252129c300c531acde2652c64)) - Prepare changelogs prior to release ([`f2e111f`](https://github.com/Byron/gitoxide/commit/f2e111f768fc1bc6182355261c20b63610cffec7)) - Release gix-command v0.3.4 ([`8a62fb5`](https://github.com/Byron/gitoxide/commit/8a62fb57f7751d3d57273d9430517487e555f999)) - Release gix-path v0.10.5 ([`b8cba96`](https://github.com/Byron/gitoxide/commit/b8cba96ce57f8b6b0067d6a8cf3e37eaf280a238)) diff --git a/gix-credentials/Cargo.toml b/gix-credentials/Cargo.toml index c58976912be..bb170e0e4d7 100644 --- a/gix-credentials/Cargo.toml +++ b/gix-credentials/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "gix-credentials" -version = "0.24.1" +version = "0.24.2" repository = "https://github.com/Byron/gitoxide" license = "MIT OR Apache-2.0" description = "A crate of the gitoxide project to interact with git credentials helpers" @@ -17,13 +17,13 @@ doctest = false serde = ["dep:serde", "bstr/serde", "gix-sec/serde"] [dependencies] -gix-sec = { version = "^0.10.5", path = "../gix-sec" } -gix-url = { version = "^0.27.1", path = "../gix-url" } -gix-path = { version = "^0.10.6", path = "../gix-path" } -gix-command = { version = "^0.3.5", path = "../gix-command" } -gix-config-value = { version = "^0.14.5", path = "../gix-config-value" } -gix-prompt = { version = "^0.8.3", path = "../gix-prompt" } -gix-trace = { version = "^0.1.7", path = "../gix-trace" } +gix-sec = { version = "^0.10.6", path = "../gix-sec" } +gix-url = { version = "^0.27.2", path = "../gix-url" } +gix-path = { version = "^0.10.7", path = "../gix-path" } +gix-command = { version = "^0.3.6", path = "../gix-command" } +gix-config-value = { version = "^0.14.6", path = "../gix-config-value" } +gix-prompt = { version = "^0.8.4", path = "../gix-prompt" } +gix-trace = { version = "^0.1.8", path = "../gix-trace" } thiserror = "1.0.32" serde = { version = "1.0.114", optional = true, default-features = false, features = ["derive"] } diff --git a/gix-credentials/examples/git-credential-lite.rs b/gix-credentials/examples/git-credential-lite.rs index c4e40c29177..c91dec18d3e 100644 --- a/gix-credentials/examples/git-credential-lite.rs +++ b/gix-credentials/examples/git-credential-lite.rs @@ -1,5 +1,3 @@ -use std::convert::TryInto; - /// Run like this `echo url=https://example.com | cargo run --example git-credential-light -- fill` pub fn main() -> Result<(), gix_credentials::program::main::Error> { gix_credentials::program::main( diff --git a/gix-credentials/examples/invoke-git-credential.rs b/gix-credentials/examples/invoke-git-credential.rs index cba3f5f706f..5d4f780f7aa 100644 --- a/gix-credentials/examples/invoke-git-credential.rs +++ b/gix-credentials/examples/invoke-git-credential.rs @@ -1,5 +1,3 @@ -use std::convert::TryInto; - /// Invokes `git credential` with the passed url as argument and prints obtained credentials. pub fn main() -> Result<(), Box> { let out = gix_credentials::builtin(gix_credentials::helper::Action::get_for_url( diff --git a/gix-credentials/src/helper/mod.rs b/gix-credentials/src/helper/mod.rs index 107d6db7a16..015ca0aaf9f 100644 --- a/gix-credentials/src/helper/mod.rs +++ b/gix-credentials/src/helper/mod.rs @@ -1,5 +1,3 @@ -use std::convert::TryFrom; - use bstr::{BStr, BString}; use crate::{protocol, protocol::Context, Program}; diff --git a/gix-credentials/src/lib.rs b/gix-credentials/src/lib.rs index 4e3e21d99a3..89e72e53d96 100644 --- a/gix-credentials/src/lib.rs +++ b/gix-credentials/src/lib.rs @@ -21,12 +21,15 @@ pub struct Program { } /// +#[allow(clippy::empty_docs)] pub mod helper; /// +#[allow(clippy::empty_docs)] pub mod program; /// +#[allow(clippy::empty_docs)] pub mod protocol; /// Call the `git credential` helper program performing the given `action`, which reads all context from the git configuration diff --git a/gix-credentials/src/program/main.rs b/gix-credentials/src/program/main.rs index b3417f92372..34f4163a01e 100644 --- a/gix-credentials/src/program/main.rs +++ b/gix-credentials/src/program/main.rs @@ -1,4 +1,4 @@ -use std::{convert::TryFrom, ffi::OsString}; +use std::ffi::OsString; use bstr::BString; @@ -60,7 +60,7 @@ pub enum Error { } pub(crate) mod function { - use std::{convert::TryInto, ffi::OsString}; + use std::ffi::OsString; use crate::{ program::main::{Action, Error}, diff --git a/gix-credentials/src/program/mod.rs b/gix-credentials/src/program/mod.rs index b7b1150d3dd..213c0272128 100644 --- a/gix-credentials/src/program/mod.rs +++ b/gix-credentials/src/program/mod.rs @@ -144,5 +144,6 @@ impl Program { } /// +#[allow(clippy::empty_docs)] pub mod main; pub use main::function::main; diff --git a/gix-credentials/src/protocol/context/serde.rs b/gix-credentials/src/protocol/context/serde.rs index 0f3b4b02487..76d1673a776 100644 --- a/gix-credentials/src/protocol/context/serde.rs +++ b/gix-credentials/src/protocol/context/serde.rs @@ -49,9 +49,8 @@ mod write { } /// +#[allow(clippy::empty_docs)] pub mod decode { - use std::convert::TryFrom; - use bstr::{BString, ByteSlice}; use crate::protocol::{context, context::serde::validate, Context}; diff --git a/gix-credentials/src/protocol/mod.rs b/gix-credentials/src/protocol/mod.rs index ec168ffb3fc..2386af8fe98 100644 --- a/gix-credentials/src/protocol/mod.rs +++ b/gix-credentials/src/protocol/mod.rs @@ -83,4 +83,5 @@ pub fn helper_outcome_to_result(outcome: Option, action: helper } /// +#[allow(clippy::empty_docs)] pub mod context; diff --git a/gix-credentials/tests/fixtures/all-but-credentials.sh b/gix-credentials/tests/fixtures/all-but-credentials.sh index 0610a71b025..934d3a0f0a7 100755 --- a/gix-credentials/tests/fixtures/all-but-credentials.sh +++ b/gix-credentials/tests/fixtures/all-but-credentials.sh @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash echo protocol=ftp echo host=example.com:8080 diff --git a/gix-credentials/tests/fixtures/custom-helper.sh b/gix-credentials/tests/fixtures/custom-helper.sh index 55d6b2f944c..340a760019b 100755 --- a/gix-credentials/tests/fixtures/custom-helper.sh +++ b/gix-credentials/tests/fixtures/custom-helper.sh @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash set -eu test "$1" = get && \ diff --git a/gix-credentials/tests/fixtures/fail.sh b/gix-credentials/tests/fixtures/fail.sh index 84b6391bcf2..c738efcae1a 100755 --- a/gix-credentials/tests/fixtures/fail.sh +++ b/gix-credentials/tests/fixtures/fail.sh @@ -1,3 +1,3 @@ -#!/bin/bash +#!/usr/bin/env bash exit 42 diff --git a/gix-credentials/tests/fixtures/last-pass.sh b/gix-credentials/tests/fixtures/last-pass.sh index 2f03f898667..fd533e5c984 100755 --- a/gix-credentials/tests/fixtures/last-pass.sh +++ b/gix-credentials/tests/fixtures/last-pass.sh @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash set -eu echo username=user diff --git a/gix-credentials/tests/fixtures/password.sh b/gix-credentials/tests/fixtures/password.sh index f75c4bc029a..c39db67618b 100755 --- a/gix-credentials/tests/fixtures/password.sh +++ b/gix-credentials/tests/fixtures/password.sh @@ -1,3 +1,3 @@ -#!/bin/bash +#!/usr/bin/env bash echo password=pass diff --git a/gix-credentials/tests/fixtures/reflect.sh b/gix-credentials/tests/fixtures/reflect.sh index e4079b793fd..e74fb85b88f 100755 --- a/gix-credentials/tests/fixtures/reflect.sh +++ b/gix-credentials/tests/fixtures/reflect.sh @@ -1,3 +1,3 @@ -#!/bin/bash +#!/usr/bin/env bash cat diff --git a/gix-credentials/tests/fixtures/url.sh b/gix-credentials/tests/fixtures/url.sh index 1eb585859e2..09900e4dbc9 100755 --- a/gix-credentials/tests/fixtures/url.sh +++ b/gix-credentials/tests/fixtures/url.sh @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash echo protocol=ftp echo host=github.com diff --git a/gix-credentials/tests/fixtures/username.sh b/gix-credentials/tests/fixtures/username.sh index f2bab6c287e..50877450981 100755 --- a/gix-credentials/tests/fixtures/username.sh +++ b/gix-credentials/tests/fixtures/username.sh @@ -1,3 +1,3 @@ -#!/bin/bash +#!/usr/bin/env bash echo username=user diff --git a/gix-credentials/tests/helper/cascade.rs b/gix-credentials/tests/helper/cascade.rs index f97a64dcb4e..32ffaaa7edb 100644 --- a/gix-credentials/tests/helper/cascade.rs +++ b/gix-credentials/tests/helper/cascade.rs @@ -1,6 +1,4 @@ mod invoke { - use std::convert::TryInto; - use bstr::{ByteSlice, ByteVec}; use gix_credentials::{ helper::{Action, Cascade}, diff --git a/gix-date/CHANGELOG.md b/gix-date/CHANGELOG.md index 288123841ec..f813af9bad4 100644 --- a/gix-date/CHANGELOG.md +++ b/gix-date/CHANGELOG.md @@ -5,6 +5,65 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## 0.8.6 (2024-05-14) + +### Bug Fixes + + - assure writing invalid dates doesn't panic. + +### Other + + - make clear that `Time::seconds` is UTC. + +### Commit Statistics + + + + - 4 commits contributed to the release over the course of 24 calendar days. + - 60 days passed between releases. + - 2 commits were understood as [conventional](https://www.conventionalcommits.org). + - 1 unique issue was worked on: [#1367](https://github.com/Byron/gitoxide/issues/1367) + +### Commit Details + + + +
view details + + * **[#1367](https://github.com/Byron/gitoxide/issues/1367)** + - Assure writing invalid dates doesn't panic. ([`3448fd9`](https://github.com/Byron/gitoxide/commit/3448fd9cc0edc93d7a5b511fd4ec0a8e84b87e51)) + * **Uncategorized** + - Merge branch 'status' ([`04ef31e`](https://github.com/Byron/gitoxide/commit/04ef31e9d6f5332d49037a5a4c248ebbb5aaf92b)) + - Merge branch 'status' ([`68fd5b3`](https://github.com/Byron/gitoxide/commit/68fd5b34e1214d5c2cc7d00dd06e19ee86c00c66)) + - Make clear that `Time::seconds` is UTC. ([`8d610ab`](https://github.com/Byron/gitoxide/commit/8d610ab8de7d3f62116632de6975f00c845dc842)) +
+ +## 0.8.5 (2024-03-14) + +A maintenance release without user-facing changes. + +### Commit Statistics + + + + - 4 commits contributed to the release over the course of 4 calendar days. + - 18 days passed between releases. + - 0 commits were understood as [conventional](https://www.conventionalcommits.org). + - 0 issues like '(#ID)' were seen in commit messages + +### Commit Details + + + +
view details + + * **Uncategorized** + - Release gix-date v0.8.5, gix-hash v0.14.2, gix-trace v0.1.8, gix-utils v0.1.11, gix-features v0.38.1, gix-actor v0.31.0, gix-validate v0.8.4, gix-object v0.42.0, gix-path v0.10.7, gix-glob v0.16.2, gix-quote v0.4.12, gix-attributes v0.22.2, gix-command v0.3.6, gix-filter v0.11.0, gix-fs v0.10.1, gix-chunk v0.4.8, gix-commitgraph v0.24.2, gix-hashtable v0.5.2, gix-revwalk v0.13.0, gix-traverse v0.38.0, gix-worktree-stream v0.11.0, gix-archive v0.11.0, gix-config-value v0.14.6, gix-tempfile v13.1.1, gix-lock v13.1.1, gix-ref v0.43.0, gix-sec v0.10.6, gix-config v0.36.0, gix-prompt v0.8.4, gix-url v0.27.2, gix-credentials v0.24.2, gix-ignore v0.11.2, gix-bitmap v0.2.11, gix-index v0.31.0, gix-worktree v0.32.0, gix-diff v0.42.0, gix-discover v0.31.0, gix-pathspec v0.7.1, gix-dir v0.2.0, gix-macros v0.1.4, gix-mailmap v0.23.0, gix-negotiate v0.13.0, gix-pack v0.49.0, gix-odb v0.59.0, gix-packetline v0.17.4, gix-transport v0.41.2, gix-protocol v0.44.2, gix-revision v0.27.0, gix-refspec v0.23.0, gix-status v0.7.0, gix-submodule v0.10.0, gix-worktree-state v0.9.0, gix v0.60.0, safety bump 26 crates ([`b050327`](https://github.com/Byron/gitoxide/commit/b050327e76f234b19be921b78b7b28e034319fdb)) + - Prepare changelogs prior to release ([`52c3bbd`](https://github.com/Byron/gitoxide/commit/52c3bbd36b9e94a0f3a78b4ada84d0c08eba27f6)) + - Merge branch 'status' ([`3e5c974`](https://github.com/Byron/gitoxide/commit/3e5c974dd62ac134711c6c2f5a5490187a6ea55e)) + - Fix lints for nightly, and clippy ([`f8ce3d0`](https://github.com/Byron/gitoxide/commit/f8ce3d0721b6a53713a9392f2451874f520bc44c)) +
+ ## 0.8.4 (2024-02-25) A maintenance release without user-facing changes. @@ -13,7 +72,7 @@ A maintenance release without user-facing changes. - - 3 commits contributed to the release over the course of 13 calendar days. + - 4 commits contributed to the release over the course of 13 calendar days. - 57 days passed between releases. - 0 commits were understood as [conventional](https://www.conventionalcommits.org). - 0 issues like '(#ID)' were seen in commit messages @@ -25,6 +84,7 @@ A maintenance release without user-facing changes.
view details * **Uncategorized** + - Release gix-date v0.8.4, gix-utils v0.1.10, gix-actor v0.30.1, gix-object v0.41.1, gix-path v0.10.6, gix-glob v0.16.1, gix-quote v0.4.11, gix-attributes v0.22.1, gix-command v0.3.5, gix-filter v0.10.0, gix-commitgraph v0.24.1, gix-worktree-stream v0.10.0, gix-archive v0.10.0, gix-config-value v0.14.5, gix-ref v0.42.0, gix-sec v0.10.5, gix-config v0.35.0, gix-prompt v0.8.3, gix-url v0.27.1, gix-credentials v0.24.1, gix-ignore v0.11.1, gix-index v0.30.0, gix-worktree v0.31.0, gix-diff v0.41.0, gix-discover v0.30.0, gix-pathspec v0.7.0, gix-dir v0.1.0, gix-pack v0.48.0, gix-odb v0.58.0, gix-transport v0.41.1, gix-protocol v0.44.1, gix-revision v0.26.1, gix-refspec v0.22.1, gix-status v0.6.0, gix-submodule v0.9.0, gix-worktree-state v0.8.0, gix v0.59.0, gix-fsck v0.3.0, gitoxide-core v0.36.0, gitoxide v0.34.0, safety bump 10 crates ([`45b4470`](https://github.com/Byron/gitoxide/commit/45b447045bc826f252129c300c531acde2652c64)) - Prepare changelogs prior to release ([`f2e111f`](https://github.com/Byron/gitoxide/commit/f2e111f768fc1bc6182355261c20b63610cffec7)) - Merge branch 'dirwalk' ([`face359`](https://github.com/Byron/gitoxide/commit/face359443ba33e8985ec1525d5ec38b743ea7a9)) - Adjust gitignore files with precious declarations ([`ae86a6a`](https://github.com/Byron/gitoxide/commit/ae86a6a206074b85ff1eba32aea9c8b40c087b17)) diff --git a/gix-date/Cargo.toml b/gix-date/Cargo.toml index 9a086475bf6..f739e75b416 100644 --- a/gix-date/Cargo.toml +++ b/gix-date/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "gix-date" -version = "0.8.4" +version = "0.8.6" repository = "https://github.com/Byron/gitoxide" license = "MIT OR Apache-2.0" description = "A crate of the gitoxide project parsing dates the way git does" diff --git a/gix-date/src/lib.rs b/gix-date/src/lib.rs index 0ada471473f..34ab8a93c93 100644 --- a/gix-date/src/lib.rs +++ b/gix-date/src/lib.rs @@ -11,9 +11,11 @@ #![forbid(unsafe_code)] /// +#[allow(clippy::empty_docs)] pub mod time; /// +#[allow(clippy::empty_docs)] pub mod parse; pub use parse::function::parse; @@ -21,11 +23,11 @@ pub use parse::function::parse; #[derive(PartialEq, Eq, Debug, Hash, Ord, PartialOrd, Clone, Copy)] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] pub struct Time { - /// time in seconds since epoch. + /// The seconds that passed since UNIX epoch. This makes it UTC, or `+0000`. pub seconds: SecondsSinceUnixEpoch, - /// time offset in seconds, may be negative to match the `sign` field. + /// The time's offset in seconds, which may be negative to match the `sign` field. pub offset: OffsetInSeconds, - /// the sign of `offset`, used to encode `-0000` which would otherwise loose sign information. + /// the sign of `offset`, used to encode `-0000` which would otherwise lose sign information. pub sign: time::Sign, } diff --git a/gix-date/src/parse.rs b/gix-date/src/parse.rs index 323292cba4c..da0691271b2 100644 --- a/gix-date/src/parse.rs +++ b/gix-date/src/parse.rs @@ -86,7 +86,7 @@ pub(crate) mod function { } mod relative { - use std::{convert::TryInto, str::FromStr, time::SystemTime}; + use std::{str::FromStr, time::SystemTime}; use time::{Duration, OffsetDateTime}; diff --git a/gix-date/src/time/mod.rs b/gix-date/src/time/mod.rs index 22c4e42f0a6..e2e1ff77afd 100644 --- a/gix-date/src/time/mod.rs +++ b/gix-date/src/time/mod.rs @@ -29,6 +29,7 @@ pub enum Format<'a> { } /// +#[allow(clippy::empty_docs)] pub mod format; mod init; mod write; diff --git a/gix-date/src/time/write.rs b/gix-date/src/time/write.rs index efc02cc1e3a..5489dd3871d 100644 --- a/gix-date/src/time/write.rs +++ b/gix-date/src/time/write.rs @@ -5,6 +5,10 @@ use crate::{time::Sign, Time}; /// Serialization with standard `git` format impl Time { /// Serialize this instance into memory, similar to what [`write_to()`][Self::write_to()] would do with arbitrary `Write` implementations. + /// + /// # Panics + /// + /// If the underlying call fails as this instance can't be represented, typically due to an invalid offset. pub fn to_bstring(&self) -> BString { let mut buf = Vec::with_capacity(64); self.write_to(&mut buf).expect("write to memory cannot fail"); @@ -13,6 +17,18 @@ impl Time { /// Serialize this instance to `out` in a format suitable for use in header fields of serialized git commits or tags. pub fn write_to(&self, out: &mut dyn std::io::Write) -> std::io::Result<()> { + const SECONDS_PER_HOUR: u32 = 60 * 60; + let offset = self.offset.unsigned_abs(); + let hours = offset / SECONDS_PER_HOUR; + let minutes = (offset - (hours * SECONDS_PER_HOUR)) / 60; + + if hours > 99 { + return Err(std::io::Error::new( + std::io::ErrorKind::Other, + "Cannot represent offsets larger than +-9900", + )); + } + let mut itoa = itoa::Buffer::new(); out.write_all(itoa.format(self.seconds).as_bytes())?; out.write_all(b" ")?; @@ -23,12 +39,6 @@ impl Time { const ZERO: &[u8; 1] = b"0"; - const SECONDS_PER_HOUR: u32 = 60 * 60; - let offset = self.offset.unsigned_abs(); - let hours = offset / SECONDS_PER_HOUR; - assert!(hours < 25, "offset is more than a day: {hours}"); - let minutes = (offset - (hours * SECONDS_PER_HOUR)) / 60; - if hours < 10 { out.write_all(ZERO)?; } diff --git a/gix-date/tests/fixtures/generate_git_date_baseline.sh b/gix-date/tests/fixtures/generate_git_date_baseline.sh index e5e34d99341..45d5217b6a9 100755 --- a/gix-date/tests/fixtures/generate_git_date_baseline.sh +++ b/gix-date/tests/fixtures/generate_git_date_baseline.sh @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash set -eu -o pipefail git init; diff --git a/gix-date/tests/fixtures/generated-archives/generate_git_date_baseline.tar.xz b/gix-date/tests/fixtures/generated-archives/generate_git_date_baseline.tar.xz index 951ea223c37..1ac61488236 100644 Binary files a/gix-date/tests/fixtures/generated-archives/generate_git_date_baseline.tar.xz and b/gix-date/tests/fixtures/generated-archives/generate_git_date_baseline.tar.xz differ diff --git a/gix-date/tests/time/mod.rs b/gix-date/tests/time/mod.rs index 0fb4befd058..82f1f37a81e 100644 --- a/gix-date/tests/time/mod.rs +++ b/gix-date/tests/time/mod.rs @@ -1,5 +1,4 @@ -use bstr::ByteSlice; -use gix_date::{time::Sign, SecondsSinceUnixEpoch, Time}; +use gix_date::Time; mod baseline; mod format; @@ -32,57 +31,114 @@ fn is_set() { .is_set()); } -#[test] -fn write_to() -> Result<(), Box> { - for (time, expected) in [ - ( - Time { - seconds: SecondsSinceUnixEpoch::MAX, - offset: 0, - sign: Sign::Minus, - }, - "9223372036854775807 -0000", - ), - ( - Time { - seconds: SecondsSinceUnixEpoch::MIN, - offset: 0, - sign: Sign::Minus, - }, - "-9223372036854775808 -0000", - ), - ( - Time { - seconds: 500, - offset: 9000, - sign: Sign::Plus, - }, - "500 +0230", - ), - ( - Time { - seconds: 189009009, - offset: -36000, - sign: Sign::Minus, - }, - "189009009 -1000", - ), - ( - Time { - seconds: 0, - offset: 0, - sign: Sign::Minus, - }, - "0 -0000", - ), - ] { - let mut output = Vec::new(); - time.write_to(&mut output)?; - assert_eq!(output.as_bstr(), expected); - assert_eq!(time.size(), output.len()); +mod write_to { + use bstr::ByteSlice; + use gix_date::time::Sign; + use gix_date::{SecondsSinceUnixEpoch, Time}; + + #[test] + fn invalid() { + let time = Time { + seconds: 0, + offset: (100 * 60 * 60) + 30 * 60, + sign: Sign::Plus, + }; + let err = time.write_to(&mut Vec::new()).unwrap_err(); + assert_eq!(err.to_string(), "Cannot represent offsets larger than +-9900"); + } - let actual = gix_date::parse(&output.as_bstr().to_string(), None).expect("round-trippable"); - assert_eq!(time, actual); + #[test] + fn valid_roundtrips() -> Result<(), Box> { + for (time, expected) in [ + ( + Time { + seconds: SecondsSinceUnixEpoch::MAX, + offset: 0, + sign: Sign::Minus, + }, + "9223372036854775807 -0000", + ), + ( + Time { + seconds: SecondsSinceUnixEpoch::MIN, + offset: 0, + sign: Sign::Minus, + }, + "-9223372036854775808 -0000", + ), + ( + Time { + seconds: 500, + offset: 9000, + sign: Sign::Plus, + }, + "500 +0230", + ), + ( + Time { + seconds: 189009009, + offset: -36000, + sign: Sign::Minus, + }, + "189009009 -1000", + ), + ( + Time { + seconds: 0, + offset: 0, + sign: Sign::Minus, + }, + "0 -0000", + ), + ( + Time { + seconds: 0, + offset: -24 * 60 * 60, + sign: Sign::Minus, + }, + "0 -2400", + ), + ( + Time { + seconds: 0, + offset: 24 * 60 * 60, + sign: Sign::Plus, + }, + "0 +2400", + ), + ( + Time { + seconds: 0, + offset: (25 * 60 * 60) + 30 * 60, + sign: Sign::Plus, + }, + "0 +2530", + ), + ( + Time { + seconds: 0, + offset: (-25 * 60 * 60) - 30 * 60, + sign: Sign::Minus, + }, + "0 -2530", + ), + ( + Time { + seconds: 0, + offset: (99 * 60 * 60) + 59 * 60, + sign: Sign::Plus, + }, + "0 +9959", + ), + ] { + let mut output = Vec::new(); + time.write_to(&mut output)?; + assert_eq!(output.as_bstr(), expected); + assert_eq!(time.size(), output.len()); + + let actual = gix_date::parse(&output.as_bstr().to_string(), None).expect("round-trippable"); + assert_eq!(time, actual); + } + Ok(()) } - Ok(()) } diff --git a/gix-date/tests/time/parse.rs b/gix-date/tests/time/parse.rs index 5b23fe1077a..8b96cb16ed2 100644 --- a/gix-date/tests/time/parse.rs +++ b/gix-date/tests/time/parse.rs @@ -91,6 +91,7 @@ fn bad_raw() { "123456 +060", "123456 -060", "123456 +06000", + "123456 +10030", "123456 06000", "123456 0600", "123456 +0600 extra", @@ -101,6 +102,26 @@ fn bad_raw() { } } +#[test] +fn double_negation_in_offset() { + let actual = gix_date::parse("1288373970 --700", None).unwrap(); + assert_eq!( + actual, + gix_date::Time { + seconds: 1288373970, + offset: 25200, + sign: Sign::Minus, + }, + "double-negation stays negative, and is parseable." + ); + + assert_eq!( + actual.to_bstring(), + "1288373970 -0700", + "serialization corrects the issue" + ); +} + #[test] fn git_default() { assert_eq!( diff --git a/gix-diff/CHANGELOG.md b/gix-diff/CHANGELOG.md index 18d8800830d..e0d8d389a10 100644 --- a/gix-diff/CHANGELOG.md +++ b/gix-diff/CHANGELOG.md @@ -5,6 +5,105 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## 0.44.0 (2024-05-22) + +A maintenance release without user-facing changes. + +### Commit Statistics + + + + - 8 commits contributed to the release over the course of 38 calendar days. + - 38 days passed between releases. + - 0 commits were understood as [conventional](https://www.conventionalcommits.org). + - 0 issues like '(#ID)' were seen in commit messages + +### Commit Details + + + +
view details + + * **Uncategorized** + - Adjust changelogs prior to release ([`9511416`](https://github.com/Byron/gitoxide/commit/9511416a6cd0c571233f958c165329c8705c2498)) + - Merge branch 'various-fixes' ([`d6cd449`](https://github.com/Byron/gitoxide/commit/d6cd44930fb204b06e2b70fc6965e7705530c47a)) + - Merge pull request from GHSA-7w47-3wg8-547c ([`79dce79`](https://github.com/Byron/gitoxide/commit/79dce79c62f6072aa2653780d590dc3993dfa401)) + - Adapt to changes in `gix-worktree` ([`1ca6a3c`](https://github.com/Byron/gitoxide/commit/1ca6a3ce22887c7eb42ec3e0a19f6e1202715745)) + - Merge branch 'cargo-fixes' ([`977346e`](https://github.com/Byron/gitoxide/commit/977346ee61de6207c66f3de003db6e8c722fb81c)) + - Release gix-index v0.32.1, gix-pathspec v0.7.4, gix-worktree v0.33.1, gix-dir v0.4.1 ([`54ac559`](https://github.com/Byron/gitoxide/commit/54ac55946bb04635cd74582a1ce2e4bee70f2e60)) + - Merge pull request #1345 from EliahKagan/shell-scripts ([`fe24c89`](https://github.com/Byron/gitoxide/commit/fe24c89e326670deaa3aaa643276d612d866072e)) + - Add missing +x bit on scripts that are run and not sourced ([`41bf65a`](https://github.com/Byron/gitoxide/commit/41bf65adef6f7d2cdd28fede262173ec7ba10822)) +
+ +## 0.43.0 (2024-04-13) + +### Bug Fixes (BREAKING) + + - Make `topo` more similar to `Ancestors`, but also rename `Ancestors` to `Simple` + +### Commit Statistics + + + + - 8 commits contributed to the release over the course of 5 calendar days. + - 29 days passed between releases. + - 1 commit was understood as [conventional](https://www.conventionalcommits.org). + - 0 issues like '(#ID)' were seen in commit messages + +### Commit Details + + + +
view details + + * **Uncategorized** + - Release gix-trace v0.1.9, gix-utils v0.1.12, gix-packetline-blocking v0.17.4, gix-filter v0.11.1, gix-fs v0.10.2, gix-traverse v0.39.0, gix-worktree-stream v0.12.0, gix-archive v0.12.0, gix-config v0.36.1, gix-url v0.27.3, gix-index v0.32.0, gix-worktree v0.33.0, gix-diff v0.43.0, gix-pathspec v0.7.3, gix-dir v0.4.0, gix-pack v0.50.0, gix-odb v0.60.0, gix-transport v0.42.0, gix-protocol v0.45.0, gix-status v0.9.0, gix-worktree-state v0.10.0, gix v0.62.0, gix-fsck v0.4.0, gitoxide-core v0.37.0, gitoxide v0.35.0, safety bump 14 crates ([`095c673`](https://github.com/Byron/gitoxide/commit/095c6739b2722a8b9af90776b435ef2da454c0e6)) + - Prepare changelogs prior to release ([`5755271`](https://github.com/Byron/gitoxide/commit/57552717f46f96c35ba4ddc0a64434354ef845e9)) + - Revert a typo ([`9680e57`](https://github.com/Byron/gitoxide/commit/9680e57952d8d6389c77bde6fa8de34592aa09a8)) + - Fix typos ([`f72ecce`](https://github.com/Byron/gitoxide/commit/f72ecce45babcad2a0c9b73c79d01ff502907a57)) + - Merge branch 'add-topo-walk' ([`b590a9d`](https://github.com/Byron/gitoxide/commit/b590a9d2b6a273f76f0320d2b9fe1f679c08f549)) + - Adapt to changes in `gix-traverse` ([`1cfeb11`](https://github.com/Byron/gitoxide/commit/1cfeb11f1fe9ad9c7b9084840ed7f5c5877f2f9a)) + - Make `topo` more similar to `Ancestors`, but also rename `Ancestors` to `Simple` ([`2a9c178`](https://github.com/Byron/gitoxide/commit/2a9c178326b7f13ba6bc1f89fc2b9d9facbecf48)) + - Adapt to changes in `gix-traverse` ([`6154bf3`](https://github.com/Byron/gitoxide/commit/6154bf3a346d69f9749271d50e4f3aacdcbad4d0)) +
+ +## 0.42.0 (2024-03-14) + +### New Features + + - add index-to-worktree status with rename tracking + +### New Features (BREAKING) + + - Provide more source information when emitting rewrites. + Previously, the source was entirely missing, now it's also made available. + Further, all the cloning of these resources is now left to the user, + which should safe time. + +### Commit Statistics + + + + - 6 commits contributed to the release over the course of 10 calendar days. + - 18 days passed between releases. + - 2 commits were understood as [conventional](https://www.conventionalcommits.org). + - 0 issues like '(#ID)' were seen in commit messages + +### Commit Details + + + +
view details + + * **Uncategorized** + - Release gix-date v0.8.5, gix-hash v0.14.2, gix-trace v0.1.8, gix-utils v0.1.11, gix-features v0.38.1, gix-actor v0.31.0, gix-validate v0.8.4, gix-object v0.42.0, gix-path v0.10.7, gix-glob v0.16.2, gix-quote v0.4.12, gix-attributes v0.22.2, gix-command v0.3.6, gix-filter v0.11.0, gix-fs v0.10.1, gix-chunk v0.4.8, gix-commitgraph v0.24.2, gix-hashtable v0.5.2, gix-revwalk v0.13.0, gix-traverse v0.38.0, gix-worktree-stream v0.11.0, gix-archive v0.11.0, gix-config-value v0.14.6, gix-tempfile v13.1.1, gix-lock v13.1.1, gix-ref v0.43.0, gix-sec v0.10.6, gix-config v0.36.0, gix-prompt v0.8.4, gix-url v0.27.2, gix-credentials v0.24.2, gix-ignore v0.11.2, gix-bitmap v0.2.11, gix-index v0.31.0, gix-worktree v0.32.0, gix-diff v0.42.0, gix-discover v0.31.0, gix-pathspec v0.7.1, gix-dir v0.2.0, gix-macros v0.1.4, gix-mailmap v0.23.0, gix-negotiate v0.13.0, gix-pack v0.49.0, gix-odb v0.59.0, gix-packetline v0.17.4, gix-transport v0.41.2, gix-protocol v0.44.2, gix-revision v0.27.0, gix-refspec v0.23.0, gix-status v0.7.0, gix-submodule v0.10.0, gix-worktree-state v0.9.0, gix v0.60.0, safety bump 26 crates ([`b050327`](https://github.com/Byron/gitoxide/commit/b050327e76f234b19be921b78b7b28e034319fdb)) + - Prepare changelogs prior to release ([`52c3bbd`](https://github.com/Byron/gitoxide/commit/52c3bbd36b9e94a0f3a78b4ada84d0c08eba27f6)) + - Merge branch 'status' ([`3e5c974`](https://github.com/Byron/gitoxide/commit/3e5c974dd62ac134711c6c2f5a5490187a6ea55e)) + - Fix lints for nightly, and clippy ([`f8ce3d0`](https://github.com/Byron/gitoxide/commit/f8ce3d0721b6a53713a9392f2451874f520bc44c)) + - Add index-to-worktree status with rename tracking ([`80a6cb8`](https://github.com/Byron/gitoxide/commit/80a6cb80fdb3a49c3461d286d62baf68755aa733)) + - Provide more source information when emitting rewrites. ([`e20b6b1`](https://github.com/Byron/gitoxide/commit/e20b6b1b9e767a07c5bc135c45cf7dccc878b99c)) +
+ ## 0.41.0 (2024-02-25) A maintenance release without user-facing changes. @@ -13,7 +112,7 @@ A maintenance release without user-facing changes. - - 9 commits contributed to the release over the course of 30 calendar days. + - 10 commits contributed to the release over the course of 30 calendar days. - 36 days passed between releases. - 0 commits were understood as [conventional](https://www.conventionalcommits.org). - 0 issues like '(#ID)' were seen in commit messages @@ -31,6 +130,7 @@ A maintenance release without user-facing changes.
view details * **Uncategorized** + - Release gix-date v0.8.4, gix-utils v0.1.10, gix-actor v0.30.1, gix-object v0.41.1, gix-path v0.10.6, gix-glob v0.16.1, gix-quote v0.4.11, gix-attributes v0.22.1, gix-command v0.3.5, gix-filter v0.10.0, gix-commitgraph v0.24.1, gix-worktree-stream v0.10.0, gix-archive v0.10.0, gix-config-value v0.14.5, gix-ref v0.42.0, gix-sec v0.10.5, gix-config v0.35.0, gix-prompt v0.8.3, gix-url v0.27.1, gix-credentials v0.24.1, gix-ignore v0.11.1, gix-index v0.30.0, gix-worktree v0.31.0, gix-diff v0.41.0, gix-discover v0.30.0, gix-pathspec v0.7.0, gix-dir v0.1.0, gix-pack v0.48.0, gix-odb v0.58.0, gix-transport v0.41.1, gix-protocol v0.44.1, gix-revision v0.26.1, gix-refspec v0.22.1, gix-status v0.6.0, gix-submodule v0.9.0, gix-worktree-state v0.8.0, gix v0.59.0, gix-fsck v0.3.0, gitoxide-core v0.36.0, gitoxide v0.34.0, safety bump 10 crates ([`45b4470`](https://github.com/Byron/gitoxide/commit/45b447045bc826f252129c300c531acde2652c64)) - Prepare changelogs prior to release ([`f2e111f`](https://github.com/Byron/gitoxide/commit/f2e111f768fc1bc6182355261c20b63610cffec7)) - Thanks clippy ([`13d5602`](https://github.com/Byron/gitoxide/commit/13d5602faa58aa6f520ebc6003ed54bc9c844f2b)) - Merge branch 'tempfile-permissions' ([`7b44c7f`](https://github.com/Byron/gitoxide/commit/7b44c7ff1dc0b8875214d2673c7f52948cf04ff0)) diff --git a/gix-diff/Cargo.toml b/gix-diff/Cargo.toml index 1e1f8757580..b669d3b6354 100644 --- a/gix-diff/Cargo.toml +++ b/gix-diff/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "gix-diff" -version = "0.41.0" +version = "0.44.0" repository = "https://github.com/Byron/gitoxide" license = "MIT OR Apache-2.0" description = "Calculate differences between various git objects" @@ -23,15 +23,15 @@ wasm = ["dep:getrandom"] doctest = false [dependencies] -gix-hash = { version = "^0.14.1", path = "../gix-hash" } -gix-object = { version = "^0.41.1", path = "../gix-object" } -gix-filter = { version = "^0.10.0", path = "../gix-filter", optional = true } -gix-worktree = { version = "^0.31.0", path = "../gix-worktree", default-features = false, features = ["attributes"], optional = true } -gix-command = { version = "^0.3.5", path = "../gix-command", optional = true } -gix-path = { version = "^0.10.6", path = "../gix-path", optional = true } -gix-fs = { version = "^0.10.0", path = "../gix-fs", optional = true } -gix-tempfile = { version = "^13.0.0", path = "../gix-tempfile", optional = true } -gix-trace = { version = "^0.1.7", path = "../gix-trace", optional = true } +gix-hash = { version = "^0.14.2", path = "../gix-hash" } +gix-object = { version = "^0.42.2", path = "../gix-object" } +gix-filter = { version = "^0.11.2", path = "../gix-filter", optional = true } +gix-worktree = { version = "^0.34.0", path = "../gix-worktree", default-features = false, features = ["attributes"], optional = true } +gix-command = { version = "^0.3.7", path = "../gix-command", optional = true } +gix-path = { version = "^0.10.7", path = "../gix-path", optional = true } +gix-fs = { version = "^0.11.1", path = "../gix-fs", optional = true } +gix-tempfile = { version = "^14.0.0", path = "../gix-tempfile", optional = true } +gix-trace = { version = "^0.1.9", path = "../gix-trace", optional = true } thiserror = "1.0.32" imara-diff = { version = "0.1.3", optional = true } diff --git a/gix-diff/src/blob/mod.rs b/gix-diff/src/blob/mod.rs index 0c76c2d918c..9af819c9877 100644 --- a/gix-diff/src/blob/mod.rs +++ b/gix-diff/src/blob/mod.rs @@ -6,9 +6,11 @@ use bstr::BString; pub use imara_diff::*; /// +#[allow(clippy::empty_docs)] pub mod pipeline; /// +#[allow(clippy::empty_docs)] pub mod platform; /// Information about the diff performed to detect similarity. diff --git a/gix-diff/src/blob/pipeline.rs b/gix-diff/src/blob/pipeline.rs index 45018218426..f67de77a14c 100644 --- a/gix-diff/src/blob/pipeline.rs +++ b/gix-diff/src/blob/pipeline.rs @@ -111,6 +111,7 @@ impl Mode { } /// +#[allow(clippy::empty_docs)] pub mod convert_to_diffable { use std::collections::TryReserveError; diff --git a/gix-diff/src/blob/platform.rs b/gix-diff/src/blob/platform.rs index 1c2d8fa0180..41b4cc928ea 100644 --- a/gix-diff/src/blob/platform.rs +++ b/gix-diff/src/blob/platform.rs @@ -90,6 +90,7 @@ pub struct Resource<'a> { } /// +#[allow(clippy::empty_docs)] pub mod resource { use crate::blob::{ pipeline, @@ -150,6 +151,7 @@ pub mod resource { } /// +#[allow(clippy::empty_docs)] pub mod set_resource { use bstr::BString; @@ -179,6 +181,7 @@ pub mod set_resource { } /// +#[allow(clippy::empty_docs)] pub mod prepare_diff { use bstr::BStr; @@ -245,6 +248,7 @@ pub mod prepare_diff { } /// +#[allow(clippy::empty_docs)] pub mod prepare_diff_command { use std::ops::{Deref, DerefMut}; @@ -579,14 +583,14 @@ impl Platform { if self.diff_cache.contains_key(storage) { return Ok(()); } - let entry = self - .attr_stack - .at_entry(rela_path, Some(false), objects) - .map_err(|err| set_resource::Error::Attributes { - source: err, - kind, - rela_path: rela_path.to_owned(), - })?; + let entry = + self.attr_stack + .at_entry(rela_path, None, objects) + .map_err(|err| set_resource::Error::Attributes { + source: err, + kind, + rela_path: rela_path.to_owned(), + })?; let mut buf = Vec::new(); let out = self.filter.convert_to_diffable( &id, diff --git a/gix-diff/src/lib.rs b/gix-diff/src/lib.rs index 1fe8d2e6bb4..ac528ccb2be 100644 --- a/gix-diff/src/lib.rs +++ b/gix-diff/src/lib.rs @@ -44,6 +44,7 @@ pub struct Rewrites { pub mod rewrites; /// +#[allow(clippy::empty_docs)] pub mod tree; /// diff --git a/gix-diff/src/rewrites/tracker.rs b/gix-diff/src/rewrites/tracker.rs index 4caefcd34d4..7a255c456a1 100644 --- a/gix-diff/src/rewrites/tracker.rs +++ b/gix-diff/src/rewrites/tracker.rs @@ -83,7 +83,7 @@ pub mod visit { /// The source of a rewrite, rename or copy. #[derive(Debug, Clone, PartialEq, PartialOrd)] - pub struct Source<'a> { + pub struct Source<'a, T> { /// The kind of entry. pub entry_mode: EntryMode, /// The hash of the state of the source as seen in the object database. @@ -92,6 +92,8 @@ pub mod visit { pub kind: SourceKind, /// The repository-relative location of this entry. pub location: &'a BStr, + /// The change that was registered as source. + pub change: &'a T, /// If this is a rewrite, indicate how many lines would need to change to turn this source into the destination. pub diff: Option, } @@ -116,6 +118,7 @@ pub mod visit { } /// +#[allow(clippy::empty_docs)] pub mod emit { /// The error returned by [Tracker::emit()](super::Tracker::emit()). #[derive(Debug, thiserror::Error)] @@ -193,7 +196,7 @@ impl Tracker { /// will panic if `change` is not a modification, and it's valid to not call `push` at all. pub fn emit( &mut self, - mut cb: impl FnMut(visit::Destination<'_, T>, Option>) -> crate::tree::visit::Action, + mut cb: impl FnMut(visit::Destination<'_, T>, Option>) -> crate::tree::visit::Action, diff_cache: &mut crate::blob::Platform, objects: &impl gix_object::FindObjectOrHeader, mut push_source_tree: PushSourceTreeFn, @@ -283,7 +286,7 @@ impl Tracker { fn match_pairs_of_kind( &mut self, kind: visit::SourceKind, - cb: &mut impl FnMut(visit::Destination<'_, T>, Option>) -> crate::tree::visit::Action, + cb: &mut impl FnMut(visit::Destination<'_, T>, Option>) -> crate::tree::visit::Action, percentage: Option, out: &mut Outcome, diff_cache: &mut crate::blob::Platform, @@ -326,7 +329,7 @@ impl Tracker { fn match_pairs( &mut self, - cb: &mut impl FnMut(visit::Destination<'_, T>, Option>) -> crate::tree::visit::Action, + cb: &mut impl FnMut(visit::Destination<'_, T>, Option>) -> crate::tree::visit::Action, percentage: Option, kind: visit::SourceKind, stats: &mut Outcome, @@ -360,6 +363,7 @@ impl Tracker { id, kind, location, + change: &src.change, diff, }, src_idx, @@ -371,11 +375,15 @@ impl Tracker { let location = dest.location(&self.path_backing); let change = dest.change.clone(); let dest = visit::Destination { change, location }; + let src_idx = src.as_ref().map(|t| t.1); + let res = cb(dest, src.map(|t| t.0)); + self.items[dest_idx].emitted = true; - if let Some(src_idx) = src.as_ref().map(|t| t.1) { + if let Some(src_idx) = src_idx { self.items[src_idx].emitted = true; } - if cb(dest, src.map(|t| t.0)) == crate::tree::visit::Action::Cancel { + + if res == crate::tree::visit::Action::Cancel { return Ok(crate::tree::visit::Action::Cancel); } } diff --git a/gix-diff/src/tree/mod.rs b/gix-diff/src/tree/mod.rs index 77e125e910e..0c87e203806 100644 --- a/gix-diff/src/tree/mod.rs +++ b/gix-diff/src/tree/mod.rs @@ -34,9 +34,11 @@ where } /// +#[allow(clippy::empty_docs)] pub mod changes; /// +#[allow(clippy::empty_docs)] pub mod visit; #[doc(inline)] pub use visit::Visit; diff --git a/gix-diff/tests/Cargo.toml b/gix-diff/tests/Cargo.toml index e63c3d5a320..695508cf1aa 100644 --- a/gix-diff/tests/Cargo.toml +++ b/gix-diff/tests/Cargo.toml @@ -25,3 +25,4 @@ gix-filter = { path = "../../gix-filter" } gix-traverse = { path = "../../gix-traverse" } gix-testtools = { path = "../../tests/tools" } shell-words = "1" +pretty_assertions = "1.4.0" diff --git a/gix-diff/tests/blob/pipeline.rs b/gix-diff/tests/blob/pipeline.rs index fb1ef355715..f4cfeba5d1b 100644 --- a/gix-diff/tests/blob/pipeline.rs +++ b/gix-diff/tests/blob/pipeline.rs @@ -507,7 +507,7 @@ pub(crate) mod convert_to_diffable { assert_eq!(out.data, Some(pipeline::Data::Binary { size: 11 })); assert_eq!(buf.len(), 0, "buffers are cleared even if we read them"); - let platform = attributes.at_entry("c", Some(false), &gix_object::find::Never)?; + let platform = attributes.at_entry("c", None, &gix_object::find::Never)?; let id = db.insert("b"); let out = filter.convert_to_diffable( @@ -589,7 +589,7 @@ pub(crate) mod convert_to_diffable { let mut db = ObjectDb::default(); let null = gix_hash::Kind::Sha1.null(); let mut buf = Vec::new(); - let platform = attributes.at_entry("a", Some(false), &gix_object::find::Never)?; + let platform = attributes.at_entry("a", None, &gix_object::find::Never)?; let worktree_modes = [ pipeline::Mode::ToWorktreeAndBinaryToText, pipeline::Mode::ToGitUnlessBinaryToTextIsPresent, @@ -672,7 +672,7 @@ pub(crate) mod convert_to_diffable { "no filter was applied in this mode, also when using the ODB" ); - let platform = attributes.at_entry("missing", Some(false), &gix_object::find::Never)?; + let platform = attributes.at_entry("missing", None, &gix_object::find::Never)?; for mode in all_modes { buf.push(1); let out = filter.convert_to_diffable( @@ -731,7 +731,7 @@ pub(crate) mod convert_to_diffable { ); } - let platform = attributes.at_entry("b", Some(false), &gix_object::find::Never)?; + let platform = attributes.at_entry("b", None, &gix_object::find::Never)?; for mode in all_modes { buf.push(1); let out = filter.convert_to_diffable( @@ -781,7 +781,7 @@ pub(crate) mod convert_to_diffable { assert_eq!(buf.len(), 0, "it's always cleared before any potential use"); } - let platform = attributes.at_entry("c", Some(false), &gix_object::find::Never)?; + let platform = attributes.at_entry("c", None, &gix_object::find::Never)?; for mode in worktree_modes { let out = filter.convert_to_diffable( &null, @@ -827,7 +827,7 @@ pub(crate) mod convert_to_diffable { ); } - let platform = attributes.at_entry("unset", Some(false), &gix_object::find::Never)?; + let platform = attributes.at_entry("unset", None, &gix_object::find::Never)?; for mode in all_modes { let out = filter.convert_to_diffable( &null, @@ -879,7 +879,7 @@ pub(crate) mod convert_to_diffable { assert_eq!(buf.len(), 0); } - let platform = attributes.at_entry("d", Some(false), &gix_object::find::Never)?; + let platform = attributes.at_entry("d", None, &gix_object::find::Never)?; let id = db.insert("d-in-db"); for mode in worktree_modes { let out = filter.convert_to_diffable( @@ -923,7 +923,7 @@ pub(crate) mod convert_to_diffable { ); } - let platform = attributes.at_entry("e-no-attr", Some(false), &gix_object::find::Never)?; + let platform = attributes.at_entry("e-no-attr", None, &gix_object::find::Never)?; let out = filter.convert_to_diffable( &null, EntryKind::Blob, diff --git a/gix-diff/tests/fixtures/generated-archives/make_blob_repo.tar.xz b/gix-diff/tests/fixtures/generated-archives/make_blob_repo.tar.xz index 1287c33ce4b..085dac5c3f7 100644 Binary files a/gix-diff/tests/fixtures/generated-archives/make_blob_repo.tar.xz and b/gix-diff/tests/fixtures/generated-archives/make_blob_repo.tar.xz differ diff --git a/gix-diff/tests/fixtures/generated-archives/make_diff_repo.tar.xz b/gix-diff/tests/fixtures/generated-archives/make_diff_repo.tar.xz index 18e3c75e651..2602d5df03c 100644 Binary files a/gix-diff/tests/fixtures/generated-archives/make_diff_repo.tar.xz and b/gix-diff/tests/fixtures/generated-archives/make_diff_repo.tar.xz differ diff --git a/gix-diff/tests/fixtures/make_blob_repo.sh b/gix-diff/tests/fixtures/make_blob_repo.sh old mode 100644 new mode 100755 index ac0079a68af..666a274efd8 --- a/gix-diff/tests/fixtures/make_blob_repo.sh +++ b/gix-diff/tests/fixtures/make_blob_repo.sh @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash set -eu -o pipefail git init -q diff --git a/gix-diff/tests/fixtures/make_diff_repo.sh b/gix-diff/tests/fixtures/make_diff_repo.sh index 7ae9ccab22a..64955c3bfe1 100755 --- a/gix-diff/tests/fixtures/make_diff_repo.sh +++ b/gix-diff/tests/fixtures/make_diff_repo.sh @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash set -eu -o pipefail diff --git a/gix-diff/tests/rewrites/mod.rs b/gix-diff/tests/rewrites/mod.rs index be44ef39eda..ddcb12dfc86 100644 --- a/gix-diff/tests/rewrites/mod.rs +++ b/gix-diff/tests/rewrites/mod.rs @@ -4,7 +4,7 @@ use gix_object::tree::{EntryKind, EntryMode}; mod tracker; -#[derive(Clone)] +#[derive(Debug, Clone, PartialEq, Eq)] pub struct Change { id: ObjectId, kind: ChangeKind, diff --git a/gix-diff/tests/rewrites/tracker.rs b/gix-diff/tests/rewrites/tracker.rs index f522fbec312..9b3f4f5a928 100644 --- a/gix-diff/tests/rewrites/tracker.rs +++ b/gix-diff/tests/rewrites/tracker.rs @@ -12,6 +12,7 @@ use gix_diff::{ Rewrites, }; use gix_object::tree::EntryKind; +use pretty_assertions::assert_eq; use crate::{ hex_to_id, @@ -51,6 +52,7 @@ fn rename_by_id() -> crate::Result { id: NULL_ID, kind: SourceKind::Rename, location: "b".into(), + change: &Change::deletion(), diff: None, } ); @@ -146,11 +148,16 @@ fn copy_by_id() -> crate::Result { let out = util::assert_emit_with_objects( &mut track, |dst, src| { + let id = hex_to_id("2e65efe2a145dda7ee51d1741299f848e5bf752e"); let source_a = Source { entry_mode: EntryKind::Blob.into(), - id: hex_to_id("2e65efe2a145dda7ee51d1741299f848e5bf752e"), + id, kind: SourceKind::Copy, location: "a".into(), + change: &Change { + id, + ..Change::modification() + }, diff: None, }; match calls { @@ -219,6 +226,10 @@ fn copy_by_id_search_in_all_sources() -> crate::Result { id: content_id, kind: SourceKind::Copy, location: "a-src".into(), + change: &Change { + id: content_id, + ..Change::modification() + }, diff: None, }; match calls { @@ -289,11 +300,16 @@ fn copy_by_50_percent_similarity() -> crate::Result { let out = util::assert_emit_with_objects( &mut track, |dst, src| { + let id = hex_to_id("78981922613b2afb6025042ff6bd878ac1994e85"); let source_a = Source { entry_mode: EntryKind::Blob.into(), - id: hex_to_id("78981922613b2afb6025042ff6bd878ac1994e85"), + id, kind: SourceKind::Copy, location: "a".into(), + change: &Change { + id, + ..Change::modification() + }, diff: Some(DiffLineStats { removals: 0, insertions: 1, @@ -459,13 +475,18 @@ fn rename_by_50_percent_similarity() -> crate::Result { |dst, src| { match calls { 0 => { + let id = hex_to_id("66a52ee7a1d803dc57859c3e95ac9dcdc87c0164"); assert_eq!( src.unwrap(), Source { entry_mode: EntryKind::Blob.into(), - id: hex_to_id("66a52ee7a1d803dc57859c3e95ac9dcdc87c0164"), + id, kind: SourceKind::Rename, location: "a".into(), + change: &Change { + id, + ..Change::deletion() + }, diff: Some(DiffLineStats { removals: 1, insertions: 1, @@ -532,7 +553,7 @@ fn add_only() -> crate::Result { let out = util::assert_emit(&mut track, |dst, src| { assert!(!called); called = true; - assert_eq!(src, None, "there is just a single deletion, no pair"); + assert!(src.is_none(), "there is just a single deletion, no pair"); assert_eq!(dst.location, "a"); assert_eq!(dst.change.kind, ChangeKind::Addition); Action::Continue @@ -569,14 +590,14 @@ mod util { pub fn assert_emit( tracker: &mut rewrites::Tracker, - cb: impl FnMut(Destination<'_, Change>, Option>) -> Action, + cb: impl FnMut(Destination<'_, Change>, Option>) -> Action, ) -> rewrites::Outcome { assert_emit_with_objects(tracker, cb, gix_object::find::Never) } pub fn assert_emit_with_objects( tracker: &mut rewrites::Tracker, - cb: impl FnMut(Destination<'_, Change>, Option>) -> Action, + cb: impl FnMut(Destination<'_, Change>, Option>) -> Action, objects: impl gix_object::FindObjectOrHeader, ) -> rewrites::Outcome { assert_emit_with_objects_and_sources(tracker, cb, objects, None) @@ -584,7 +605,7 @@ mod util { pub fn assert_emit_with_objects_and_sources<'a>( tracker: &mut rewrites::Tracker, - cb: impl FnMut(Destination<'_, Change>, Option>) -> Action, + cb: impl FnMut(Destination<'_, Change>, Option>) -> Action, objects: impl gix_object::FindObjectOrHeader, sources: impl IntoIterator, ) -> rewrites::Outcome { diff --git a/gix-diff/tests/tree/mod.rs b/gix-diff/tests/tree/mod.rs index 7df8f64c7c9..85fcbe6170c 100644 --- a/gix-diff/tests/tree/mod.rs +++ b/gix-diff/tests/tree/mod.rs @@ -129,11 +129,10 @@ mod changes { } fn all_commits(db: &gix_odb::Handle) -> HashMap { - use gix_traverse::commit; let mut buf = Vec::new(); let head = head_of(db); - commit::Ancestors::new(Some(head), commit::ancestors::State::default(), &db) + gix_traverse::commit::Simple::new(Some(head), &db) .collect::, _>>() .expect("valid iteration") .into_iter() diff --git a/gix-dir/CHANGELOG.md b/gix-dir/CHANGELOG.md index f6284a65f34..8a4be8c37da 100644 --- a/gix-dir/CHANGELOG.md +++ b/gix-dir/CHANGELOG.md @@ -5,6 +5,173 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## 0.5.0 (2024-05-22) + +A maintenance release without user-facing changes. + +### Commit Statistics + + + + - 8 commits contributed to the release over the course of 33 calendar days. + - 33 days passed between releases. + - 0 commits were understood as [conventional](https://www.conventionalcommits.org). + - 0 issues like '(#ID)' were seen in commit messages + +### Commit Details + + + +
view details + + * **Uncategorized** + - Adjust changelogs prior to release ([`9511416`](https://github.com/Byron/gitoxide/commit/9511416a6cd0c571233f958c165329c8705c2498)) + - Merge branch 'various-fixes' ([`d6cd449`](https://github.com/Byron/gitoxide/commit/d6cd44930fb204b06e2b70fc6965e7705530c47a)) + - Merge pull request from GHSA-7w47-3wg8-547c ([`79dce79`](https://github.com/Byron/gitoxide/commit/79dce79c62f6072aa2653780d590dc3993dfa401)) + - Address review comments ([`fcc3b69`](https://github.com/Byron/gitoxide/commit/fcc3b69867db1628f6a44d0e0dad8f7417f566bc)) + - Adapt to changes in `gix-worktree` ([`1ca6a3c`](https://github.com/Byron/gitoxide/commit/1ca6a3ce22887c7eb42ec3e0a19f6e1202715745)) + - Merge branch 'status' ([`68fd5b3`](https://github.com/Byron/gitoxide/commit/68fd5b34e1214d5c2cc7d00dd06e19ee86c00c66)) + - Cleanup path classificaiton after fixes in `gix-pathspec` ([`44a2e00`](https://github.com/Byron/gitoxide/commit/44a2e005ea8241d026ae542dd4a71cfb6cfd8308)) + - Merge branch 'cargo-fixes' ([`977346e`](https://github.com/Byron/gitoxide/commit/977346ee61de6207c66f3de003db6e8c722fb81c)) +
+ +## 0.4.1 (2024-04-18) + +### Bug Fixes + + - assure worktree-roots aren't pruned with pathspecs that are never meant for them. + Previously, when pathspecs were defined, the classification of the worktree-root + would also be using them. This means that depending on the pathspec, worktree-roots would + be pruned, which in turn makes it impossible to recurse into them. + + Now pathspecs are disabled when classifying the worktree-root directory. + +### Commit Statistics + + + + - 5 commits contributed to the release over the course of 4 calendar days. + - 5 days passed between releases. + - 1 commit was understood as [conventional](https://www.conventionalcommits.org). + - 0 issues like '(#ID)' were seen in commit messages + +### Commit Details + + + +
view details + + * **Uncategorized** + - Release gix-index v0.32.1, gix-pathspec v0.7.4, gix-worktree v0.33.1, gix-dir v0.4.1 ([`54ac559`](https://github.com/Byron/gitoxide/commit/54ac55946bb04635cd74582a1ce2e4bee70f2e60)) + - Prepare changelog prior to `gix-dir` patch release ([`6ca6fa6`](https://github.com/Byron/gitoxide/commit/6ca6fa69b5c21c8d8e9e07e21558e98201504cda)) + - Assure worktree-roots aren't pruned with pathspecs that are never meant for them. ([`7f2f3ff`](https://github.com/Byron/gitoxide/commit/7f2f3ff8adbecd631c2b4513995d6c94b21742eb)) + - Merge pull request #1345 from EliahKagan/shell-scripts ([`fe24c89`](https://github.com/Byron/gitoxide/commit/fe24c89e326670deaa3aaa643276d612d866072e)) + - Add missing +x bit on scripts that are run and not sourced ([`41bf65a`](https://github.com/Byron/gitoxide/commit/41bf65adef6f7d2cdd28fede262173ec7ba10822)) +
+ +## 0.4.0 (2024-04-13) + +A maintenance release without user-facing changes. + +### Commit Statistics + + + + - 6 commits contributed to the release over the course of 10 calendar days. + - 26 days passed between releases. + - 0 commits were understood as [conventional](https://www.conventionalcommits.org). + - 0 issues like '(#ID)' were seen in commit messages + +### Commit Details + + + +
view details + + * **Uncategorized** + - Release gix-trace v0.1.9, gix-utils v0.1.12, gix-packetline-blocking v0.17.4, gix-filter v0.11.1, gix-fs v0.10.2, gix-traverse v0.39.0, gix-worktree-stream v0.12.0, gix-archive v0.12.0, gix-config v0.36.1, gix-url v0.27.3, gix-index v0.32.0, gix-worktree v0.33.0, gix-diff v0.43.0, gix-pathspec v0.7.3, gix-dir v0.4.0, gix-pack v0.50.0, gix-odb v0.60.0, gix-transport v0.42.0, gix-protocol v0.45.0, gix-status v0.9.0, gix-worktree-state v0.10.0, gix v0.62.0, gix-fsck v0.4.0, gitoxide-core v0.37.0, gitoxide v0.35.0, safety bump 14 crates ([`095c673`](https://github.com/Byron/gitoxide/commit/095c6739b2722a8b9af90776b435ef2da454c0e6)) + - Prepare changelogs prior to release ([`5755271`](https://github.com/Byron/gitoxide/commit/57552717f46f96c35ba4ddc0a64434354ef845e9)) + - Merge pull request #1341 from szepeviktor/typos ([`55f379b`](https://github.com/Byron/gitoxide/commit/55f379bc47065822d078393d83d30c0835a89782)) + - Fix typos ([`f72ecce`](https://github.com/Byron/gitoxide/commit/f72ecce45babcad2a0c9b73c79d01ff502907a57)) + - Merge pull request #1334 from EliahKagan/nonstandard-worktree ([`37732fb`](https://github.com/Byron/gitoxide/commit/37732fb13efdff5a1b8a836943e9e575196724b5)) + - Let nonstandard worktree fixtures work even if Git < 2.37.2 ([`01d6be9`](https://github.com/Byron/gitoxide/commit/01d6be997fced8695b60d0dc206f87e542037a8d)) +
+ +## 0.3.0 (2024-03-18) + +### Bug Fixes + + - allow traversals to start from a symlink that points to a directory + Now symlinked repositories can be traversed as well. + +### New Features (BREAKING) + + - allow directory walk to be interrupted with `should_interrupt` flag. + That way, it can be much more responsive to interruption. + - assure symlinks to directories are ignored with `dir/` declarations in `.gitignore`. + Initially, symlinks appear like symlinks thanks to `lstat`, but to do + exclude handling correctly these need another `stat` call. + + However, this is also not done in Git, but done in `libgit2` only, + so a toggle was added to act like Git by default, but allow obtaining + the same behaviour as git2 for compatibility. + +### Commit Statistics + + + + - 6 commits contributed to the release. + - 3 days passed between releases. + - 3 commits were understood as [conventional](https://www.conventionalcommits.org). + - 0 issues like '(#ID)' were seen in commit messages + +### Commit Details + + + +
view details + + * **Uncategorized** + - Release gix-actor v0.31.1, gix-object v0.42.1, gix-index v0.31.1, gix-pathspec v0.7.2, gix-dir v0.3.0, gix-status v0.8.0, gix v0.61.0, safety bump 2 crates ([`155cc45`](https://github.com/Byron/gitoxide/commit/155cc45730b259e662d7c4be42a469a3af3750e1)) + - Prepare changelog prior to release ([`129ba3d`](https://github.com/Byron/gitoxide/commit/129ba3deccc9ada0dc571466458845939502763d)) + - Merge branch 'improvements-for-cargo' ([`41cd53e`](https://github.com/Byron/gitoxide/commit/41cd53e2af76e35e047aac4eca6324774df4cb50)) + - Allow directory walk to be interrupted with `should_interrupt` flag. ([`35b74e7`](https://github.com/Byron/gitoxide/commit/35b74e7992a5a732b5ae8dbdc264479a91b1d60d)) + - Allow traversals to start from a symlink that points to a directory ([`e7e91cf`](https://github.com/Byron/gitoxide/commit/e7e91cfaed6d40a773a65fc077b99d2e26bb28f5)) + - Assure symlinks to directories are ignored with `dir/` declarations in `.gitignore`. ([`cd0c8af`](https://github.com/Byron/gitoxide/commit/cd0c8af78fd7a4f06e33ec2ce06b094b5a490877)) +
+ +## 0.2.0 (2024-03-14) + +### Bug Fixes + + - make it possible to use a submodule root for a full walk. + Previously, it would not allow to enter the repository, making + a walk impossible. + +### Commit Statistics + + + + - 6 commits contributed to the release over the course of 4 calendar days. + - 18 days passed between releases. + - 1 commit was understood as [conventional](https://www.conventionalcommits.org). + - 0 issues like '(#ID)' were seen in commit messages + +### Commit Details + + + +
view details + + * **Uncategorized** + - Release gix-date v0.8.5, gix-hash v0.14.2, gix-trace v0.1.8, gix-utils v0.1.11, gix-features v0.38.1, gix-actor v0.31.0, gix-validate v0.8.4, gix-object v0.42.0, gix-path v0.10.7, gix-glob v0.16.2, gix-quote v0.4.12, gix-attributes v0.22.2, gix-command v0.3.6, gix-filter v0.11.0, gix-fs v0.10.1, gix-chunk v0.4.8, gix-commitgraph v0.24.2, gix-hashtable v0.5.2, gix-revwalk v0.13.0, gix-traverse v0.38.0, gix-worktree-stream v0.11.0, gix-archive v0.11.0, gix-config-value v0.14.6, gix-tempfile v13.1.1, gix-lock v13.1.1, gix-ref v0.43.0, gix-sec v0.10.6, gix-config v0.36.0, gix-prompt v0.8.4, gix-url v0.27.2, gix-credentials v0.24.2, gix-ignore v0.11.2, gix-bitmap v0.2.11, gix-index v0.31.0, gix-worktree v0.32.0, gix-diff v0.42.0, gix-discover v0.31.0, gix-pathspec v0.7.1, gix-dir v0.2.0, gix-macros v0.1.4, gix-mailmap v0.23.0, gix-negotiate v0.13.0, gix-pack v0.49.0, gix-odb v0.59.0, gix-packetline v0.17.4, gix-transport v0.41.2, gix-protocol v0.44.2, gix-revision v0.27.0, gix-refspec v0.23.0, gix-status v0.7.0, gix-submodule v0.10.0, gix-worktree-state v0.9.0, gix v0.60.0, safety bump 26 crates ([`b050327`](https://github.com/Byron/gitoxide/commit/b050327e76f234b19be921b78b7b28e034319fdb)) + - Prepare changelogs prior to release ([`52c3bbd`](https://github.com/Byron/gitoxide/commit/52c3bbd36b9e94a0f3a78b4ada84d0c08eba27f6)) + - Merge branch 'status' ([`3e5c974`](https://github.com/Byron/gitoxide/commit/3e5c974dd62ac134711c6c2f5a5490187a6ea55e)) + - Keep lower-bound of `thiserror` low in `gix-dir` ([`917634f`](https://github.com/Byron/gitoxide/commit/917634fa694a1e91d37f6407e57ae96b3b0aec4b)) + - Fix lints for nightly, and clippy ([`f8ce3d0`](https://github.com/Byron/gitoxide/commit/f8ce3d0721b6a53713a9392f2451874f520bc44c)) + - Make it possible to use a submodule root for a full walk. ([`434f543`](https://github.com/Byron/gitoxide/commit/434f5434d7242f7f3d6b595f767195c51a3acd86)) +
+ ## 0.1.0 (2024-02-25) ### Bug Fixes @@ -34,7 +201,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - - 18 commits contributed to the release over the course of 57 calendar days. + - 19 commits contributed to the release over the course of 57 calendar days. - 6 commits were understood as [conventional](https://www.conventionalcommits.org). - 0 issues like '(#ID)' were seen in commit messages @@ -45,6 +212,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
view details * **Uncategorized** + - Release gix-date v0.8.4, gix-utils v0.1.10, gix-actor v0.30.1, gix-object v0.41.1, gix-path v0.10.6, gix-glob v0.16.1, gix-quote v0.4.11, gix-attributes v0.22.1, gix-command v0.3.5, gix-filter v0.10.0, gix-commitgraph v0.24.1, gix-worktree-stream v0.10.0, gix-archive v0.10.0, gix-config-value v0.14.5, gix-ref v0.42.0, gix-sec v0.10.5, gix-config v0.35.0, gix-prompt v0.8.3, gix-url v0.27.1, gix-credentials v0.24.1, gix-ignore v0.11.1, gix-index v0.30.0, gix-worktree v0.31.0, gix-diff v0.41.0, gix-discover v0.30.0, gix-pathspec v0.7.0, gix-dir v0.1.0, gix-pack v0.48.0, gix-odb v0.58.0, gix-transport v0.41.1, gix-protocol v0.44.1, gix-revision v0.26.1, gix-refspec v0.22.1, gix-status v0.6.0, gix-submodule v0.9.0, gix-worktree-state v0.8.0, gix v0.59.0, gix-fsck v0.3.0, gitoxide-core v0.36.0, gitoxide v0.34.0, safety bump 10 crates ([`45b4470`](https://github.com/Byron/gitoxide/commit/45b447045bc826f252129c300c531acde2652c64)) - Prepare changelogs prior to release ([`f2e111f`](https://github.com/Byron/gitoxide/commit/f2e111f768fc1bc6182355261c20b63610cffec7)) - Merge branch 'status' ([`d53504a`](https://github.com/Byron/gitoxide/commit/d53504a1fad41cec7b6ca2a4abb7f185d8941e3f)) - Make it even harder to remove your own CWD ([`4d5767c`](https://github.com/Byron/gitoxide/commit/4d5767cd394d755104aa7f0c1ed5b8e01bf74b12)) diff --git a/gix-dir/Cargo.toml b/gix-dir/Cargo.toml index aa532d5f654..5ed3425ad32 100644 --- a/gix-dir/Cargo.toml +++ b/gix-dir/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "gix-dir" -version = "0.1.0" +version = "0.5.0" repository = "https://github.com/Byron/gitoxide" license = "MIT OR Apache-2.0" description = "A crate of the gitoxide project dealing with directory walks" @@ -12,19 +12,19 @@ rust-version = "1.65" doctest = false [dependencies] -gix-trace = { version = "^0.1.7", path = "../gix-trace" } -gix-index = { version = "^0.30.0", path = "../gix-index" } -gix-discover = { version = "^0.30.0", path = "../gix-discover" } -gix-fs = { version = "^0.10.0", path = "../gix-fs" } -gix-path = { version = "^0.10.6", path = "../gix-path" } -gix-pathspec = { version = "^0.7.0", path = "../gix-pathspec" } -gix-worktree = { version = "^0.31.0", path = "../gix-worktree", default-features = false } -gix-object = { version = "^0.41.1", path = "../gix-object" } -gix-ignore = { version = "^0.11.1", path = "../gix-ignore" } -gix-utils = { version = "^0.1.10", path = "../gix-utils", features = ["bstr"] } +gix-trace = { version = "^0.1.9", path = "../gix-trace" } +gix-index = { version = "^0.33.0", path = "../gix-index" } +gix-discover = { version = "^0.32.0", path = "../gix-discover" } +gix-fs = { version = "^0.11.1", path = "../gix-fs" } +gix-path = { version = "^0.10.7", path = "../gix-path" } +gix-pathspec = { version = "^0.7.5", path = "../gix-pathspec" } +gix-worktree = { version = "^0.34.0", path = "../gix-worktree", default-features = false } +gix-object = { version = "^0.42.2", path = "../gix-object" } +gix-ignore = { version = "^0.11.2", path = "../gix-ignore" } +gix-utils = { version = "^0.1.12", path = "../gix-utils", features = ["bstr"] } bstr = { version = "1.5.0", default-features = false } -thiserror = "1.0.56" +thiserror = "1.0.34" [dev-dependencies] gix-testtools = { path = "../tests/tools" } diff --git a/gix-dir/src/entry.rs b/gix-dir/src/entry.rs index 81c6fae5204..4bfc02e02a4 100644 --- a/gix-dir/src/entry.rs +++ b/gix-dir/src/entry.rs @@ -168,6 +168,9 @@ impl Status { /// This implements the default rules of `git status`, which is good for a minimal traversal through /// tracked and non-ignored portions of a worktree. /// `for_deletion` is used to determine if recursion into a directory is allowed even though it otherwise wouldn't be. + /// If `worktree_root_is_repository` is `true`, then this status is part of the root of an iteration, and the corresponding + /// worktree root is a repository itself. This typically happens for submodules. In this case, recursion rules are relaxed + /// to allow traversing submodule worktrees. /// /// Use `pathspec_match` to determine if a pathspec matches in any way, affecting the decision to recurse. pub fn can_recurse( @@ -175,8 +178,15 @@ impl Status { file_type: Option, pathspec_match: Option, for_deletion: Option, + worktree_root_is_repository: bool, ) -> bool { - let is_dir_on_disk = file_type.map_or(false, |ft| ft.is_recursable_dir()); + let is_dir_on_disk = file_type.map_or(false, |ft| { + if worktree_root_is_repository { + ft.is_dir() + } else { + ft.is_recursable_dir() + } + }); if !is_dir_on_disk { return false; } diff --git a/gix-dir/src/lib.rs b/gix-dir/src/lib.rs index fffe2d46eb4..79565773bd2 100644 --- a/gix-dir/src/lib.rs +++ b/gix-dir/src/lib.rs @@ -59,8 +59,10 @@ pub struct Entry { } /// +#[allow(clippy::empty_docs)] pub mod entry; /// +#[allow(clippy::empty_docs)] pub mod walk; pub use walk::function::walk; diff --git a/gix-dir/src/walk/classify.rs b/gix-dir/src/walk/classify.rs index 4b89911d3d1..9fb2b54a5a6 100644 --- a/gix-dir/src/walk/classify.rs +++ b/gix-dir/src/walk/classify.rs @@ -4,8 +4,7 @@ use std::borrow::Cow; use crate::entry::PathspecMatch; use crate::walk::{Context, Error, ForDeletionMode, Options}; use bstr::{BStr, BString, ByteSlice}; -use std::ffi::OsStr; -use std::path::{Component, Path, PathBuf}; +use std::path::{Path, PathBuf}; /// Classify the `worktree_relative_root` path and return the first `PathKind` that indicates that /// it isn't a directory, leaving `buf` with the path matching the returned `PathKind`, @@ -16,20 +15,18 @@ pub fn root( worktree_relative_root: &Path, options: Options, ctx: &mut Context<'_>, -) -> Result { +) -> Result<(Outcome, bool), Error> { buf.clear(); let mut last_length = None; let mut path_buf = worktree_root.to_owned(); // These initial values kick in if worktree_relative_root.is_empty(); - let mut out = None; - for component in worktree_relative_root - .components() - .chain(if worktree_relative_root.as_os_str().is_empty() { - Some(Component::Normal(OsStr::new(""))) - } else { - None - }) - { + let file_kind = path_buf.symlink_metadata().map(|m| m.file_type().into()).ok(); + let mut out = path(&mut path_buf, buf, 0, file_kind, || None, options, ctx)?; + let worktree_root_is_repository = out + .disk_kind + .map_or(false, |kind| matches!(kind, entry::Kind::Repository)); + + for component in worktree_relative_root.components() { if last_length.is_some() { buf.push(b'/'); } @@ -37,7 +34,7 @@ pub fn root( buf.extend_from_slice(gix_path::os_str_into_bstr(component.as_os_str()).expect("no illformed UTF8")); let file_kind = path_buf.symlink_metadata().map(|m| m.file_type().into()).ok(); - let res = path( + out = path( &mut path_buf, buf, last_length.map(|l| l + 1 /* slash */).unwrap_or_default(), @@ -46,16 +43,17 @@ pub fn root( options, ctx, )?; - out = Some(res); - if !res - .status - .can_recurse(res.disk_kind, res.pathspec_match, options.for_deletion) - { + if !out.status.can_recurse( + out.disk_kind, + out.pathspec_match, + options.for_deletion, + worktree_root_is_repository, + ) { break; } last_length = Some(buf.len()); } - Ok(out.expect("One iteration of the loop at least")) + Ok((out, worktree_root_is_repository)) } /// The product of [`path()`] calls. #[derive(Debug, Copy, Clone)] @@ -136,6 +134,7 @@ pub fn path( emit_ignored, for_deletion, classify_untracked_bare_repositories, + symlinks_to_directories_are_ignored_like_directories, .. }: Options, ctx: &mut Context<'_>, @@ -162,7 +161,11 @@ pub fn path( .as_mut() .map_or(Ok(None), |stack| { stack - .at_entry(rela_path.as_bstr(), disk_kind.map(|ft| ft.is_dir()), ctx.objects) + .at_entry( + rela_path.as_bstr(), + disk_kind.map(|ft| is_dir_to_mode(ft.is_dir())), + ctx.objects, + ) .map(|platform| platform.excluded_kind()) }) .map_err(Error::ExcludesAccess)? @@ -173,10 +176,9 @@ pub fn path( out.property = entry::Property::DotGit.into(); return Ok(out); } - let pathspec_could_match = rela_path.is_empty() - || ctx - .pathspec - .can_match_relative_path(rela_path.as_bstr(), disk_kind.map(|ft| ft.is_dir())); + let pathspec_could_match = ctx + .pathspec + .can_match_relative_path(rela_path.as_bstr(), disk_kind.map(|ft| ft.is_dir())); if !pathspec_could_match { return Ok(out.with_status(entry::Status::Pruned)); } @@ -201,6 +203,15 @@ pub fn path( .pattern_matching_relative_path(rela_path.as_bstr(), kind.map(|ft| ft.is_dir()), ctx.pathspec_attributes) .map(Into::into); + let is_dir = if symlinks_to_directories_are_ignored_like_directories + && ctx.excludes.is_some() + && kind.map_or(false, |ft| ft == entry::Kind::Symlink) + { + path.metadata().ok().map(|md| is_dir_to_mode(md.is_dir())) + } else { + kind.map(|ft| is_dir_to_mode(ft.is_dir())) + }; + let mut maybe_upgrade_to_repository = |current_kind, find_harder: bool| { if recurse_repositories { return current_kind; @@ -241,13 +252,13 @@ pub fn path( return Ok(out.with_status(status).with_kind(kind, index_kind)); } - debug_assert!(maybe_status.is_none(), "It only communicates a single stae right now"); + debug_assert!(maybe_status.is_none(), "It only communicates a single state right now"); if let Some(excluded) = ctx .excludes .as_mut() .map_or(Ok(None), |stack| { stack - .at_entry(rela_path.as_bstr(), kind.map(|ft| ft.is_dir()), ctx.objects) + .at_entry(rela_path.as_bstr(), is_dir, ctx.objects) .map(|platform| platform.excluded_kind()) }) .map_err(Error::ExcludesAccess)? @@ -401,3 +412,11 @@ fn is_eq(lhs: &BStr, rhs: impl AsRef, ignore_case: bool) -> bool { lhs == rhs.as_ref() } } + +fn is_dir_to_mode(is_dir: bool) -> gix_index::entry::Mode { + if is_dir { + gix_index::entry::Mode::DIR + } else { + gix_index::entry::Mode::FILE + } +} diff --git a/gix-dir/src/walk/function.rs b/gix-dir/src/walk/function.rs index a7ec42f7a3d..f49623d9b0e 100644 --- a/gix-dir/src/walk/function.rs +++ b/gix-dir/src/walk/function.rs @@ -60,15 +60,30 @@ pub fn walk( let (mut current, worktree_root_relative) = assure_no_symlink_in_root(worktree_root, &root)?; let mut out = Outcome::default(); let mut buf = BString::default(); - let root_info = classify::root( + let (root_info, worktree_root_is_repository) = classify::root( worktree_root, &mut buf, worktree_root_relative.as_ref(), options, &mut ctx, )?; - if !can_recurse(buf.as_bstr(), root_info, options.for_deletion, delegate) { - if buf.is_empty() && !matches!(root_info.disk_kind, Some(entry::Kind::Directory { .. })) { + + let can_recurse = can_recurse( + buf.as_bstr(), + if root == worktree_root && root_info.disk_kind == Some(entry::Kind::Symlink) && current.is_dir() { + classify::Outcome { + disk_kind: Some(entry::Kind::Directory), + ..root_info + } + } else { + root_info + }, + options.for_deletion, + worktree_root_is_repository, + delegate, + ); + if !can_recurse { + if buf.is_empty() && !root_info.disk_kind.map_or(false, |kind| kind.is_dir()) { return Err(Error::WorktreeRootIsFile { root: root.to_owned() }); } if options.precompose_unicode { @@ -141,12 +156,18 @@ pub(super) fn can_recurse( rela_path: &BStr, info: classify::Outcome, for_deletion: Option, + worktree_root_is_repository: bool, delegate: &mut dyn Delegate, ) -> bool { - if info.disk_kind.map_or(true, |k| !k.is_dir()) { + let is_dir = info.disk_kind.map_or(false, |k| k.is_dir()); + if !is_dir { return false; } - delegate.can_recurse(EntryRef::from_outcome(Cow::Borrowed(rela_path), info), for_deletion) + delegate.can_recurse( + EntryRef::from_outcome(Cow::Borrowed(rela_path), info), + for_deletion, + worktree_root_is_repository, + ) } /// Possibly emit an entry to `for_each` in case the provided information makes that possible. diff --git a/gix-dir/src/walk/mod.rs b/gix-dir/src/walk/mod.rs index 3f593c40d9b..15573a0003e 100644 --- a/gix-dir/src/walk/mod.rs +++ b/gix-dir/src/walk/mod.rs @@ -1,6 +1,7 @@ use crate::{entry, EntryRef}; use bstr::BStr; use std::path::PathBuf; +use std::sync::atomic::AtomicBool; /// A type returned by the [`Delegate::emit()`] as passed to [`walk()`](function::walk()). #[derive(Debug, Copy, Clone, Eq, PartialEq)] @@ -69,12 +70,24 @@ pub trait Delegate { /// Use `for_deletion` to specify if the seen entries should ultimately be deleted, which may affect the decision /// of whether to resource or not. /// + /// If `worktree_root_is_repository` is `true`, then this status is part of the root of an iteration, and the corresponding + /// worktree root is a repository itself. This typically happens for submodules. In this case, recursion rules are relaxed + /// to allow traversing submodule worktrees. + /// /// Note that this method will see all directories, even though not all of them may end up being [emitted](Self::emit()). /// If this method returns `false`, the `entry` will always be emitted. - fn can_recurse(&mut self, entry: EntryRef<'_>, for_deletion: Option) -> bool { - entry - .status - .can_recurse(entry.disk_kind, entry.pathspec_match, for_deletion) + fn can_recurse( + &mut self, + entry: EntryRef<'_>, + for_deletion: Option, + worktree_root_is_repository: bool, + ) -> bool { + entry.status.can_recurse( + entry.disk_kind, + entry.pathspec_match, + for_deletion, + worktree_root_is_repository, + ) } } @@ -85,7 +98,7 @@ pub trait Delegate { pub enum EmissionMode { /// Emit each entry as it matches exactly, without doing any kind of simplification. /// - /// Emissions in this mode are happening as they occour, without any buffering or ordering. + /// Emissions in this mode are happening as they occur, without any buffering or ordering. #[default] Matching, /// Emit only a containing directory if all of its entries are of the same type. @@ -171,10 +184,22 @@ pub struct Options { pub emit_empty_directories: bool, /// If `None`, no entries inside of collapsed directories are emitted. Otherwise, act as specified by `Some(mode)`. pub emit_collapsed: Option, + /// This is a `libgit2` compatibility flag, and if enabled, symlinks that point to directories will be considered a directory + /// when checking for exclusion. + /// + /// This is relevant if `src2` points to `src`, and is excluded with `src2/`. If `false`, `src2` will not be excluded, + /// if `true` it will be excluded as the symlink is considered a directory. + /// + /// In other words, for Git compatibility this flag should be `false`, the default, for `git2` compatibility it should be `true`. + pub symlinks_to_directories_are_ignored_like_directories: bool, } /// All information that is required to perform a dirwalk, and classify paths properly. pub struct Context<'a> { + /// If not `None`, it will be checked before entering any directory to trigger early interruption. + /// + /// If this flag is `true` at any point in the iteration, it will abort with an error. + pub should_interrupt: Option<&'a AtomicBool>, /// The `git_dir` of the parent repository, after a call to [`gix_path::realpath()`]. /// /// It's used to help us differentiate our own `.git` directory from nested unrelated repositories, @@ -190,7 +215,7 @@ pub struct Context<'a> { /// ### Important /// /// The index must have been validated so that each entry that is considered up-to-date will have the [gix_index::entry::Flags::UPTODATE] flag - /// set. Otherwise the index entry is not considered and a disk-access may occour which is costly. + /// set. Otherwise the index entry is not considered and a disk-access may occur which is costly. pub index: &'a gix_index::State, /// A utility to lookup index entries faster, and deal with ignore-case handling. /// @@ -249,6 +274,8 @@ pub struct Outcome { #[derive(Debug, thiserror::Error)] #[allow(missing_docs)] pub enum Error { + #[error("Interrupted")] + Interrupted, #[error("Worktree root at '{}' is not a directory", root.display())] WorktreeRootIsFile { root: PathBuf }, #[error("Traversal root '{}' contains relative path components and could not be normalized", root.display())] diff --git a/gix-dir/src/walk/readdir.rs b/gix-dir/src/walk/readdir.rs index 05bc5a93301..0618822a652 100644 --- a/gix-dir/src/walk/readdir.rs +++ b/gix-dir/src/walk/readdir.rs @@ -1,6 +1,7 @@ use bstr::{BStr, BString, ByteSlice}; use std::borrow::Cow; use std::path::{Path, PathBuf}; +use std::sync::atomic::Ordering; use crate::entry::{PathspecMatch, Status}; use crate::walk::function::{can_recurse, emit_entry}; @@ -23,6 +24,9 @@ pub(super) fn recursive( out: &mut Outcome, state: &mut State, ) -> Result<(Action, bool), Error> { + if ctx.should_interrupt.map_or(false, |flag| flag.load(Ordering::Relaxed)) { + return Err(Error::Interrupted); + } out.read_dir_calls += 1; let entries = gix_fs::read_dir(current, opts.precompose_unicode).map_err(|err| Error::ReadDir { path: current.to_owned(), @@ -63,7 +67,13 @@ pub(super) fn recursive( ctx, )?; - if can_recurse(current_bstr.as_bstr(), info, opts.for_deletion, delegate) { + if can_recurse( + current_bstr.as_bstr(), + info, + opts.for_deletion, + false, /* is root */ + delegate, + ) { let subdir_may_collapse = state.may_collapse(current); let (action, subdir_prevent_collapse) = recursive( subdir_may_collapse, diff --git a/gix-dir/tests/fixtures/many-symlinks.sh b/gix-dir/tests/fixtures/many-symlinks.sh old mode 100644 new mode 100755 index f38abf74d18..fdebe1021a7 --- a/gix-dir/tests/fixtures/many-symlinks.sh +++ b/gix-dir/tests/fixtures/many-symlinks.sh @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash set -eu -o pipefail # Note that symlink creation fails on Windows for some reason, @@ -17,3 +17,29 @@ git init immediate-breakout-symlink (cd immediate-breakout-symlink ln -s .. breakout ) + +git init excluded-symlinks-to-dir +(cd excluded-symlinks-to-dir + cat <.gitignore +src1 +src2/ +file1 +file2/ +ignored +ignored-must-be-dir/ +EOF + git add .gitignore && git commit -m "init" + + mkdir src + >src/file + + mkdir ignored-must-be-dir ignored + touch ignored-must-be-dir/file ignored/file + + ln -s src src1 + ln -s src src2 + ln -s src/file file1 + ln -s src/file file2 +) + +ln -s excluded-symlinks-to-dir worktree-root-is-symlink \ No newline at end of file diff --git a/gix-dir/tests/fixtures/many.sh b/gix-dir/tests/fixtures/many.sh old mode 100644 new mode 100755 index 0c17c4d3a3b..96b87022798 --- a/gix-dir/tests/fixtures/many.sh +++ b/gix-dir/tests/fixtures/many.sh @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash set -eu -o pipefail # Nothing here may use symlinks so these fixtures can be used on windows as well. @@ -29,6 +29,13 @@ git init dir-with-tracked-file git commit -m "init" ) +git init repo-with-submodule +(cd repo-with-submodule + git submodule add ../dir-with-tracked-file submodule + git commit -m "add submodule" + touch submodule/untracked +) + git init ignored-dir (cd ignored-dir mkdir dir @@ -103,24 +110,22 @@ git clone dir-with-tracked-file with-submodule git init nonstandard-worktree (cd nonstandard-worktree mkdir dir-with-dot-git - mv .git dir-with-dot-git - - git -C dir-with-dot-git config core.worktree "$PWD" touch dir-with-dot-git/inside touch seemingly-outside - git -C dir-with-dot-git add inside ../seemingly-outside + git add dir-with-dot-git/inside seemingly-outside + mv .git dir-with-dot-git + git -C dir-with-dot-git config core.worktree "$PWD" git -C dir-with-dot-git commit -m "init" ) git init nonstandard-worktree-untracked (cd nonstandard-worktree-untracked mkdir dir-with-dot-git - mv .git dir-with-dot-git - - git -C dir-with-dot-git config core.worktree "$PWD" touch dir-with-dot-git/inside touch seemingly-outside - git -C dir-with-dot-git add inside ../seemingly-outside + git add dir-with-dot-git/inside seemingly-outside + mv .git dir-with-dot-git + git -C dir-with-dot-git config core.worktree "$PWD" git -C dir-with-dot-git commit -m "init" rm dir-with-dot-git/.git/index diff --git a/gix-dir/tests/walk/mod.rs b/gix-dir/tests/walk/mod.rs index 823bf360370..dc880059856 100644 --- a/gix-dir/tests/walk/mod.rs +++ b/gix-dir/tests/walk/mod.rs @@ -1,5 +1,6 @@ use gix_dir::{walk, EntryRef}; use pretty_assertions::assert_eq; +use std::sync::atomic::AtomicBool; use crate::walk_utils::{ collect, collect_filtered, collect_filtered_with_cwd, entry, entry_dirstat, entry_nokind, entry_nomatch, entryps, @@ -16,6 +17,81 @@ use gix_dir::walk::EmissionMode::*; use gix_dir::walk::ForDeletionMode; use gix_ignore::Kind::*; +#[test] +#[cfg_attr(windows, ignore = "symlinks the way they are organized don't yet work on windows")] +fn symlink_to_dir_can_be_excluded() -> crate::Result { + let root = fixture_in("many-symlinks", "excluded-symlinks-to-dir"); + let ((out, _root), entries) = collect(&root, None, |keep, ctx| { + walk( + &root, + ctx, + gix_dir::walk::Options { + emit_ignored: Some(Matching), + ..options() + }, + keep, + ) + }); + assert_eq!( + out, + walk::Outcome { + read_dir_calls: 2, + returned_entries: entries.len(), + seen_entries: 9, + } + ); + + assert_eq!( + entries, + &[ + entry("file1", Ignored(Expendable), Symlink), + entry("file2", Untracked, Symlink), + entry("ignored", Ignored(Expendable), Directory), + entry("ignored-must-be-dir", Ignored(Expendable), Directory), + entry("src/file", Untracked, File), + entry("src1", Ignored(Expendable), Symlink), + entry("src2", Untracked, Symlink), /* marked as src2/ in .gitignore */ + ], + "by default, symlinks are counted as files only, even if they point to a directory, when handled by the exclude machinery" + ); + + let ((out, _root), entries) = collect(&root, None, |keep, ctx| { + walk( + &root, + ctx, + gix_dir::walk::Options { + emit_ignored: Some(Matching), + symlinks_to_directories_are_ignored_like_directories: true, + ..options() + }, + keep, + ) + }); + assert_eq!( + out, + walk::Outcome { + read_dir_calls: 2, + returned_entries: entries.len(), + seen_entries: 9, + } + ); + + assert_eq!( + entries, + &[ + entry("file1", Ignored(Expendable), Symlink), + entry("file2", Untracked, Symlink), + entry("ignored", Ignored(Expendable), Directory), + entry("ignored-must-be-dir", Ignored(Expendable), Directory), + entry("src/file", Untracked, File), + entry("src1", Ignored(Expendable), Symlink), + entry("src2", Ignored(Expendable), Symlink), /* marked as src2/ in .gitignore */ + ], + "with libgit2 compatibility enabled, symlinks to directories are treated like a directory, not symlink" + ); + Ok(()) +} + #[test] #[cfg_attr(windows, ignore = "symlinks the way they are organized don't yet work on windows")] fn root_may_not_lead_through_symlinks() -> crate::Result { @@ -43,6 +119,57 @@ fn root_may_not_lead_through_symlinks() -> crate::Result { Ok(()) } +#[test] +#[cfg_attr(windows, ignore = "symlinks the way they are organized don't yet work on windows")] +fn root_may_be_a_symlink_if_it_is_the_worktree() -> crate::Result { + let root = fixture_in("many-symlinks", "worktree-root-is-symlink"); + let ((_out, _root), entries) = collect(&root, None, |keep, ctx| { + walk( + &root, + ctx, + gix_dir::walk::Options { + emit_ignored: Some(Matching), + symlinks_to_directories_are_ignored_like_directories: true, + ..options() + }, + keep, + ) + }); + + assert_eq!( + entries, + &[ + entry("file1", Ignored(Expendable), Symlink), + entry("file2", Untracked, Symlink), + entry("ignored", Ignored(Expendable), Directory), + entry("ignored-must-be-dir", Ignored(Expendable), Directory), + entry("src/file", Untracked, File), + entry("src1", Ignored(Expendable), Symlink), + entry("src2", Ignored(Expendable), Symlink), /* marked as src2/ in .gitignore */ + ], + "it traversed the directory normally - without this capability, symlinked repos can't be traversed" + ); + Ok(()) +} + +#[test] +fn should_interrupt_works_even_in_empty_directories() { + let root = fixture("empty"); + let should_interrupt = AtomicBool::new(true); + let err = try_collect_filtered_opts_collect( + &root, + None, + |keep, ctx| walk(&root, ctx, gix_dir::walk::Options { ..options() }, keep), + None::<&str>, + Options { + should_interrupt: Some(&should_interrupt), + ..Default::default() + }, + ) + .unwrap_err(); + assert!(matches!(err, gix_dir::walk::Error::Interrupted)); +} + #[test] fn empty_root() -> crate::Result { let root = fixture("empty"); @@ -1478,7 +1605,7 @@ fn expendable_and_precious_in_ignored_dir_with_pathspec() -> crate::Result { "with pathspec, we match what's inside and expect to have all the lowest-level paths that have 'ignored' in them.\ It seems strange that 'precious' isn't precious, while 'all-precious' is. However, the ignore-search is special as it goes backward through directories (using directory-declarations), and aborts if it matched. Thus it finds - that '$/all-precious/' matched, but in the other cases it maches 'ignored/'. + that '$/all-precious/' matched, but in the other cases it matches 'ignored/'. 'other' gets folded and inherits, just like before. Also, look how the ignore-state overrides the prune-default for DotGit kinds, to have more finegrained classification." ); @@ -2625,12 +2752,136 @@ fn root_may_not_go_through_dot_git() -> crate::Result { e.0.pathspec_match = expected_pathspec; e }], - "no traversal happened as root passes though .git" + "{dir}: no traversal happened as root passes though .git" ); } Ok(()) } +#[test] +fn root_at_submodule_repository_allows_walk() -> crate::Result { + let root = fixture("repo-with-submodule"); + let troot = root.join("submodule"); + let ((out, _root), entries) = try_collect_filtered_opts_collect_with_root( + &troot, + None, + Some(&troot), + |keep, ctx| { + walk( + &troot, + ctx, + walk::Options { + emit_tracked: true, + emit_untracked: Matching, + ..options() + }, + keep, + ) + }, + None::<&str>, + Options::git_dir("../.git/modules/submodule"), + )?; + + assert_eq!( + out, + walk::Outcome { + read_dir_calls: 2, + returned_entries: entries.len(), + seen_entries: 3, + } + ); + + assert_eq!( + entries, + [entry("dir/file", Tracked, File), entry("untracked", Untracked, File)], + "this is a special case to allow walking submodules specifically, like a normal repository" + ); + Ok(()) +} + +#[test] +fn root_in_submodule_repository_allows_walk() -> crate::Result { + let root = fixture("repo-with-submodule"); + let troot = root.join("submodule"); + let ((out, _root), entries) = try_collect_filtered_opts_collect_with_root( + &troot, + None, + Some(&troot.join("dir")), + |keep, ctx| { + walk( + &troot, + ctx, + walk::Options { + emit_tracked: true, + emit_untracked: Matching, + ..options() + }, + keep, + ) + }, + None::<&str>, + Options::git_dir("../.git/modules/submodule"), + )?; + + assert_eq!( + out, + walk::Outcome { + read_dir_calls: 1, + returned_entries: entries.len(), + seen_entries: 1, + } + ); + + assert_eq!( + entries, + [entry("dir/file", Tracked, File)], + "it's also working if the traversal root is inside the subdmodule" + ); + Ok(()) +} + +#[test] +fn root_in_submodule_from_superproject_repository_allows_walk() -> crate::Result { + let root = fixture("repo-with-submodule"); + let troot = root.join("submodule").join("dir"); + let ((out, _root), entries) = try_collect_filtered_opts_collect_with_root( + &root, + None, + Some(&troot), + |keep, ctx| { + walk( + &troot, + ctx, + walk::Options { + emit_tracked: true, + emit_untracked: Matching, + ..options() + }, + keep, + ) + }, + None::<&str>, + Default::default(), + )?; + + assert_eq!( + out, + walk::Outcome { + read_dir_calls: 1, + returned_entries: entries.len(), + seen_entries: 1, + } + ); + + assert_eq!( + entries, + [entry("file", Untracked, File)], + "there is no index that has 'file' in it (it's 'dir/file'), hence it's untracked.\ + But the traversal is possible, even though it might not make the most sense." + ); + Ok(()) +} + #[test] fn root_enters_directory_with_dot_git_in_reconfigured_worktree_tracked() -> crate::Result { let root = fixture("nonstandard-worktree"); @@ -2797,7 +3048,8 @@ fn root_may_not_go_through_submodule() -> crate::Result { assert_eq!( entries, [entry("submodule", Tracked, Repository)], - "it refuses to start traversal in a submodule, thus it ends in the directory that is the submodule" + "it refuses to start traversal in a submodule, thus it ends in the directory that is the submodule, \ + if the root is another repository" ); Ok(()) } @@ -3705,7 +3957,7 @@ fn root_cannot_pass_through_case_altered_capital_dot_git_if_case_insensitive() - e.0.pathspec_match = expected_pathspec; e }], - "no traversal happened as root passes though .git, it compares in a case-insensitive fashion" + "{dir}: no traversal happened as root passes though .git, it compares in a case-insensitive fashion" ); } diff --git a/gix-dir/tests/walk_utils/mod.rs b/gix-dir/tests/walk_utils/mod.rs index 13ffcb36851..ee279d4d857 100644 --- a/gix-dir/tests/walk_utils/mod.rs +++ b/gix-dir/tests/walk_utils/mod.rs @@ -2,6 +2,7 @@ use bstr::BStr; use gix_dir::{entry, walk, Entry}; use gix_testtools::scripted_fixture_read_only; use std::path::{Path, PathBuf}; +use std::sync::atomic::AtomicBool; pub fn fixture_in(filename: &str, name: &str) -> PathBuf { let root = scripted_fixture_read_only(format!("{filename}.sh")).expect("script works"); @@ -31,6 +32,7 @@ pub fn options_emit_all() -> walk::Options { emit_untracked: walk::EmissionMode::Matching, emit_empty_directories: true, emit_collapsed: None, + symlinks_to_directories_are_ignored_like_directories: false, } } @@ -268,7 +270,11 @@ pub fn try_collect_filtered_opts( cb: impl FnOnce(&mut dyn walk::Delegate, walk::Context) -> Result<(walk::Outcome, PathBuf), walk::Error>, patterns: impl IntoIterator>, delegate: &mut dyn gix_dir::walk::Delegate, - Options { fresh_index, git_dir }: Options<'_>, + Options { + fresh_index, + git_dir, + should_interrupt, + }: Options<'_>, ) -> Result<(walk::Outcome, PathBuf), walk::Error> { let git_dir = worktree_root.join(git_dir.unwrap_or(".git")); let mut index = std::fs::read(git_dir.join("index")).ok().map_or_else( @@ -342,6 +348,7 @@ pub fn try_collect_filtered_opts( excludes: Some(&mut stack), objects: &gix_object::find::Never, explicit_traversal_root, + should_interrupt, }, ) } @@ -349,6 +356,7 @@ pub fn try_collect_filtered_opts( pub struct Options<'a> { pub fresh_index: bool, pub git_dir: Option<&'a str>, + pub should_interrupt: Option<&'a AtomicBool>, } impl<'a> Options<'a> { @@ -365,6 +373,7 @@ impl<'a> Default for Options<'a> { Options { fresh_index: true, git_dir: None, + should_interrupt: None, } } } diff --git a/gix-discover/CHANGELOG.md b/gix-discover/CHANGELOG.md index ef446d78bae..5725e035b5d 100644 --- a/gix-discover/CHANGELOG.md +++ b/gix-discover/CHANGELOG.md @@ -5,6 +5,59 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## 0.32.0 (2024-05-22) + +A maintenance release without user-facing changes. + +### Commit Statistics + + + + - 5 commits contributed to the release. + - 68 days passed between releases. + - 0 commits were understood as [conventional](https://www.conventionalcommits.org). + - 0 issues like '(#ID)' were seen in commit messages + +### Commit Details + + + +
view details + + * **Uncategorized** + - Adjust changelogs prior to release ([`9511416`](https://github.com/Byron/gitoxide/commit/9511416a6cd0c571233f958c165329c8705c2498)) + - Merge branch 'various-fixes' ([`d6cd449`](https://github.com/Byron/gitoxide/commit/d6cd44930fb204b06e2b70fc6965e7705530c47a)) + - Update dependencies ([`cd4de83`](https://github.com/Byron/gitoxide/commit/cd4de8327fc195eb862ab6e138f2315a87374f85)) + - Merge pull request from GHSA-7w47-3wg8-547c ([`79dce79`](https://github.com/Byron/gitoxide/commit/79dce79c62f6072aa2653780d590dc3993dfa401)) + - Adapt to changes in `gix-ref` ([`d2ae9d5`](https://github.com/Byron/gitoxide/commit/d2ae9d5f11be9f2561f6799d88804d0d8eae33ef)) +
+ +## 0.31.0 (2024-03-14) + +A maintenance release without user-facing changes. + +### Commit Statistics + + + + - 4 commits contributed to the release over the course of 4 calendar days. + - 18 days passed between releases. + - 0 commits were understood as [conventional](https://www.conventionalcommits.org). + - 0 issues like '(#ID)' were seen in commit messages + +### Commit Details + + + +
view details + + * **Uncategorized** + - Release gix-date v0.8.5, gix-hash v0.14.2, gix-trace v0.1.8, gix-utils v0.1.11, gix-features v0.38.1, gix-actor v0.31.0, gix-validate v0.8.4, gix-object v0.42.0, gix-path v0.10.7, gix-glob v0.16.2, gix-quote v0.4.12, gix-attributes v0.22.2, gix-command v0.3.6, gix-filter v0.11.0, gix-fs v0.10.1, gix-chunk v0.4.8, gix-commitgraph v0.24.2, gix-hashtable v0.5.2, gix-revwalk v0.13.0, gix-traverse v0.38.0, gix-worktree-stream v0.11.0, gix-archive v0.11.0, gix-config-value v0.14.6, gix-tempfile v13.1.1, gix-lock v13.1.1, gix-ref v0.43.0, gix-sec v0.10.6, gix-config v0.36.0, gix-prompt v0.8.4, gix-url v0.27.2, gix-credentials v0.24.2, gix-ignore v0.11.2, gix-bitmap v0.2.11, gix-index v0.31.0, gix-worktree v0.32.0, gix-diff v0.42.0, gix-discover v0.31.0, gix-pathspec v0.7.1, gix-dir v0.2.0, gix-macros v0.1.4, gix-mailmap v0.23.0, gix-negotiate v0.13.0, gix-pack v0.49.0, gix-odb v0.59.0, gix-packetline v0.17.4, gix-transport v0.41.2, gix-protocol v0.44.2, gix-revision v0.27.0, gix-refspec v0.23.0, gix-status v0.7.0, gix-submodule v0.10.0, gix-worktree-state v0.9.0, gix v0.60.0, safety bump 26 crates ([`b050327`](https://github.com/Byron/gitoxide/commit/b050327e76f234b19be921b78b7b28e034319fdb)) + - Prepare changelogs prior to release ([`52c3bbd`](https://github.com/Byron/gitoxide/commit/52c3bbd36b9e94a0f3a78b4ada84d0c08eba27f6)) + - Merge branch 'status' ([`3e5c974`](https://github.com/Byron/gitoxide/commit/3e5c974dd62ac134711c6c2f5a5490187a6ea55e)) + - Fix lints for nightly, and clippy ([`f8ce3d0`](https://github.com/Byron/gitoxide/commit/f8ce3d0721b6a53713a9392f2451874f520bc44c)) +
+ ## 0.30.0 (2024-02-25) A maintenance release without user-facing changes. @@ -13,7 +66,7 @@ A maintenance release without user-facing changes. - - 4 commits contributed to the release over the course of 30 calendar days. + - 5 commits contributed to the release over the course of 30 calendar days. - 36 days passed between releases. - 0 commits were understood as [conventional](https://www.conventionalcommits.org). - 0 issues like '(#ID)' were seen in commit messages @@ -25,6 +78,7 @@ A maintenance release without user-facing changes.
view details * **Uncategorized** + - Release gix-date v0.8.4, gix-utils v0.1.10, gix-actor v0.30.1, gix-object v0.41.1, gix-path v0.10.6, gix-glob v0.16.1, gix-quote v0.4.11, gix-attributes v0.22.1, gix-command v0.3.5, gix-filter v0.10.0, gix-commitgraph v0.24.1, gix-worktree-stream v0.10.0, gix-archive v0.10.0, gix-config-value v0.14.5, gix-ref v0.42.0, gix-sec v0.10.5, gix-config v0.35.0, gix-prompt v0.8.3, gix-url v0.27.1, gix-credentials v0.24.1, gix-ignore v0.11.1, gix-index v0.30.0, gix-worktree v0.31.0, gix-diff v0.41.0, gix-discover v0.30.0, gix-pathspec v0.7.0, gix-dir v0.1.0, gix-pack v0.48.0, gix-odb v0.58.0, gix-transport v0.41.1, gix-protocol v0.44.1, gix-revision v0.26.1, gix-refspec v0.22.1, gix-status v0.6.0, gix-submodule v0.9.0, gix-worktree-state v0.8.0, gix v0.59.0, gix-fsck v0.3.0, gitoxide-core v0.36.0, gitoxide v0.34.0, safety bump 10 crates ([`45b4470`](https://github.com/Byron/gitoxide/commit/45b447045bc826f252129c300c531acde2652c64)) - Prepare changelogs prior to release ([`f2e111f`](https://github.com/Byron/gitoxide/commit/f2e111f768fc1bc6182355261c20b63610cffec7)) - Merge branch 'tempfile-permissions' ([`7b44c7f`](https://github.com/Byron/gitoxide/commit/7b44c7ff1dc0b8875214d2673c7f52948cf04ff0)) - Release gix-tempfile v13.1.0, gix-lock v13.1.0, safety bump 12 crates ([`8430442`](https://github.com/Byron/gitoxide/commit/84304427dfe4d170c7732161b126961719f70059)) diff --git a/gix-discover/Cargo.toml b/gix-discover/Cargo.toml index b5c93867bde..f5902cafa49 100644 --- a/gix-discover/Cargo.toml +++ b/gix-discover/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "gix-discover" -version = "0.30.0" +version = "0.32.0" repository = "https://github.com/Byron/gitoxide" license = "MIT OR Apache-2.0" description = "Discover git repositories and check if a directory is a git repository" @@ -13,11 +13,11 @@ rust-version = "1.65" doctest = false [dependencies] -gix-sec = { version = "^0.10.5", path = "../gix-sec" } -gix-path = { version = "^0.10.6", path = "../gix-path" } -gix-ref = { version = "^0.42.0", path = "../gix-ref" } -gix-hash = { version = "^0.14.1", path = "../gix-hash" } -gix-fs = { version = "^0.10.0", path = "../gix-fs" } +gix-sec = { version = "^0.10.6", path = "../gix-sec" } +gix-path = { version = "^0.10.7", path = "../gix-path" } +gix-ref = { version = "^0.44.1", path = "../gix-ref" } +gix-hash = { version = "^0.14.2", path = "../gix-hash" } +gix-fs = { version = "^0.11.1", path = "../gix-fs" } bstr = { version = "1.3.0", default-features = false, features = ["std", "unicode"] } thiserror = "1.0.26" @@ -27,11 +27,11 @@ dunce = "1.0.3" [dev-dependencies] gix-testtools = { path = "../tests/tools" } -serial_test = { version = "2.0.0", default-features = false } +serial_test = { version = "3.1.0", default-features = false } is_ci = "1.1.1" [target.'cfg(target_os = "macos")'.dev-dependencies] -defer = "0.1.0" +defer = "0.2.1" [target.'cfg(any(unix, windows))'.dev-dependencies] tempfile = "3.2.0" diff --git a/gix-discover/src/is.rs b/gix-discover/src/is.rs index 055a68e67cd..c0172ef9a06 100644 --- a/gix-discover/src/is.rs +++ b/gix-discover/src/is.rs @@ -172,13 +172,7 @@ pub(crate) fn git_with_metadata( // We expect to be able to parse any ref-hash, so we shouldn't have to know the repos hash here. // With ref-table, the has is probably stored as part of the ref-db itself, so we can handle it from there. // In other words, it's important not to fail on detached heads here because we guessed the hash kind wrongly. - let object_hash_should_not_matter_here = gix_hash::Kind::Sha1; - let refs = gix_ref::file::Store::at( - dot_git.as_ref().into(), - gix_ref::store::WriteReflog::Normal, - object_hash_should_not_matter_here, - false, - ); + let refs = gix_ref::file::Store::at(dot_git.as_ref().into(), Default::default()); let head = refs.find_loose("HEAD")?; if head.name.as_bstr() != "HEAD" { return Err(crate::is_git::Error::MisplacedHead { diff --git a/gix-discover/src/lib.rs b/gix-discover/src/lib.rs index a035c5cf6bb..d86fa0e1f75 100644 --- a/gix-discover/src/lib.rs +++ b/gix-discover/src/lib.rs @@ -11,9 +11,11 @@ pub const DOT_GIT_DIR: &str = ".git"; pub const MODULES: &str = "modules"; /// +#[allow(clippy::empty_docs)] pub mod repository; /// +#[allow(clippy::empty_docs)] pub mod is_git { use std::path::PathBuf; @@ -46,11 +48,14 @@ mod is; pub use is::{bare as is_bare, git as is_git, submodule_git_dir as is_submodule_git_dir}; /// +#[allow(clippy::empty_docs)] pub mod upwards; pub use upwards::function::{discover as upwards, discover_opts as upwards_opts}; /// +#[allow(clippy::empty_docs)] pub mod path; /// +#[allow(clippy::empty_docs)] pub mod parse; diff --git a/gix-discover/src/parse.rs b/gix-discover/src/parse.rs index 58971832755..7ff11fb7da2 100644 --- a/gix-discover/src/parse.rs +++ b/gix-discover/src/parse.rs @@ -3,6 +3,7 @@ use std::path::PathBuf; use bstr::ByteSlice; /// +#[allow(clippy::empty_docs)] pub mod gitdir { use bstr::BString; diff --git a/gix-discover/src/path.rs b/gix-discover/src/path.rs index f1e446f15d2..2eb841b3d79 100644 --- a/gix-discover/src/path.rs +++ b/gix-discover/src/path.rs @@ -3,6 +3,7 @@ use std::{io::Read, path::PathBuf}; use crate::DOT_GIT_DIR; /// +#[allow(clippy::empty_docs)] pub mod from_gitdir_file { /// The error returned by [`from_gitdir_file()`][crate::path::from_gitdir_file()]. #[derive(Debug, thiserror::Error)] diff --git a/gix-discover/tests/fixtures/generated-archives/make_exfat_repo_darwin.tar.xz b/gix-discover/tests/fixtures/generated-archives/make_exfat_repo_darwin.tar.xz index dfb904ef920..9f70a620482 100644 Binary files a/gix-discover/tests/fixtures/generated-archives/make_exfat_repo_darwin.tar.xz and b/gix-discover/tests/fixtures/generated-archives/make_exfat_repo_darwin.tar.xz differ diff --git a/gix-discover/tests/fixtures/generated-archives/make_submodules.tar.xz b/gix-discover/tests/fixtures/generated-archives/make_submodules.tar.xz index ef8b87c68dc..98f4c73ea35 100644 Binary files a/gix-discover/tests/fixtures/generated-archives/make_submodules.tar.xz and b/gix-discover/tests/fixtures/generated-archives/make_submodules.tar.xz differ diff --git a/gix-discover/tests/fixtures/make_basic_repo.sh b/gix-discover/tests/fixtures/make_basic_repo.sh index bdc1be08e30..b0c79baf404 100755 --- a/gix-discover/tests/fixtures/make_basic_repo.sh +++ b/gix-discover/tests/fixtures/make_basic_repo.sh @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash set -eu -o pipefail git init -q diff --git a/gix-discover/tests/fixtures/make_exfat_repo_darwin.sh b/gix-discover/tests/fixtures/make_exfat_repo_darwin.sh index 88709300044..322aad35197 100755 --- a/gix-discover/tests/fixtures/make_exfat_repo_darwin.sh +++ b/gix-discover/tests/fixtures/make_exfat_repo_darwin.sh @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash set -eu -o pipefail [[ $(uname) == Darwin ]] || exit 1 diff --git a/gix-discover/tests/fixtures/make_submodules.sh b/gix-discover/tests/fixtures/make_submodules.sh index 24e28a5ec3c..9994d6d50e1 100755 --- a/gix-discover/tests/fixtures/make_submodules.sh +++ b/gix-discover/tests/fixtures/make_submodules.sh @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash set -eu -o pipefail git init -q module1 diff --git a/gix-discover/tests/path/mod.rs b/gix-discover/tests/path/mod.rs index b047810bbbd..b0bac5468fa 100644 --- a/gix-discover/tests/path/mod.rs +++ b/gix-discover/tests/path/mod.rs @@ -4,7 +4,7 @@ mod from_git_dir_file { path::{Path, PathBuf}, }; - use gix_testtools::{tempfile, tempfile::NamedTempFile}; + use gix_testtools::tempfile::NamedTempFile; #[cfg(not(windows))] #[test] diff --git a/gix-features/CHANGELOG.md b/gix-features/CHANGELOG.md index f02d099fc13..594c18be054 100644 --- a/gix-features/CHANGELOG.md +++ b/gix-features/CHANGELOG.md @@ -5,6 +5,61 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## 0.38.2 (2024-05-22) + +### Bug Fixes + + - assure high-speed SHA1 assembly is only used in not on Windows + +### Commit Statistics + + + + - 4 commits contributed to the release over the course of 1 calendar day. + - 68 days passed between releases. + - 1 commit was understood as [conventional](https://www.conventionalcommits.org). + - 1 unique issue was worked on: [#917](https://github.com/Byron/gitoxide/issues/917) + +### Commit Details + + + +
view details + + * **[#917](https://github.com/Byron/gitoxide/issues/917)** + - Assure high-speed SHA1 assembly is only used in not on Windows ([`2683235`](https://github.com/Byron/gitoxide/commit/268323587faeada1abdd7f933d616af3165a37cf)) + * **Uncategorized** + - Adjust changelogs prior to release ([`9511416`](https://github.com/Byron/gitoxide/commit/9511416a6cd0c571233f958c165329c8705c2498)) + - Merge branch 'various-fixes' ([`d6cd449`](https://github.com/Byron/gitoxide/commit/d6cd44930fb204b06e2b70fc6965e7705530c47a)) + - Merge pull request from GHSA-7w47-3wg8-547c ([`79dce79`](https://github.com/Byron/gitoxide/commit/79dce79c62f6072aa2653780d590dc3993dfa401)) +
+ +## 0.38.1 (2024-03-14) + +A maintenance release without user-facing changes. + +### Commit Statistics + + + + - 4 commits contributed to the release over the course of 4 calendar days. + - 54 days passed between releases. + - 0 commits were understood as [conventional](https://www.conventionalcommits.org). + - 0 issues like '(#ID)' were seen in commit messages + +### Commit Details + + + +
view details + + * **Uncategorized** + - Release gix-date v0.8.5, gix-hash v0.14.2, gix-trace v0.1.8, gix-utils v0.1.11, gix-features v0.38.1, gix-actor v0.31.0, gix-validate v0.8.4, gix-object v0.42.0, gix-path v0.10.7, gix-glob v0.16.2, gix-quote v0.4.12, gix-attributes v0.22.2, gix-command v0.3.6, gix-filter v0.11.0, gix-fs v0.10.1, gix-chunk v0.4.8, gix-commitgraph v0.24.2, gix-hashtable v0.5.2, gix-revwalk v0.13.0, gix-traverse v0.38.0, gix-worktree-stream v0.11.0, gix-archive v0.11.0, gix-config-value v0.14.6, gix-tempfile v13.1.1, gix-lock v13.1.1, gix-ref v0.43.0, gix-sec v0.10.6, gix-config v0.36.0, gix-prompt v0.8.4, gix-url v0.27.2, gix-credentials v0.24.2, gix-ignore v0.11.2, gix-bitmap v0.2.11, gix-index v0.31.0, gix-worktree v0.32.0, gix-diff v0.42.0, gix-discover v0.31.0, gix-pathspec v0.7.1, gix-dir v0.2.0, gix-macros v0.1.4, gix-mailmap v0.23.0, gix-negotiate v0.13.0, gix-pack v0.49.0, gix-odb v0.59.0, gix-packetline v0.17.4, gix-transport v0.41.2, gix-protocol v0.44.2, gix-revision v0.27.0, gix-refspec v0.23.0, gix-status v0.7.0, gix-submodule v0.10.0, gix-worktree-state v0.9.0, gix v0.60.0, safety bump 26 crates ([`b050327`](https://github.com/Byron/gitoxide/commit/b050327e76f234b19be921b78b7b28e034319fdb)) + - Prepare changelogs prior to release ([`52c3bbd`](https://github.com/Byron/gitoxide/commit/52c3bbd36b9e94a0f3a78b4ada84d0c08eba27f6)) + - Merge branch 'status' ([`3e5c974`](https://github.com/Byron/gitoxide/commit/3e5c974dd62ac134711c6c2f5a5490187a6ea55e)) + - Fix lints for nightly, and clippy ([`f8ce3d0`](https://github.com/Byron/gitoxide/commit/f8ce3d0721b6a53713a9392f2451874f520bc44c)) +
+ ## 0.38.0 (2024-01-20) ### New Features @@ -20,7 +75,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - - 4 commits contributed to the release over the course of 2 calendar days. + - 5 commits contributed to the release over the course of 2 calendar days. - 4 days passed between releases. - 2 commits were understood as [conventional](https://www.conventionalcommits.org). - 0 issues like '(#ID)' were seen in commit messages @@ -32,6 +87,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
view details * **Uncategorized** + - Release gix-utils v0.1.9, gix-features v0.38.0, gix-actor v0.30.0, gix-object v0.41.0, gix-path v0.10.4, gix-glob v0.16.0, gix-attributes v0.22.0, gix-command v0.3.3, gix-packetline-blocking v0.17.3, gix-filter v0.9.0, gix-fs v0.10.0, gix-commitgraph v0.24.0, gix-revwalk v0.12.0, gix-traverse v0.37.0, gix-worktree-stream v0.9.0, gix-archive v0.9.0, gix-config-value v0.14.4, gix-tempfile v13.0.0, gix-lock v13.0.0, gix-ref v0.41.0, gix-sec v0.10.4, gix-config v0.34.0, gix-url v0.27.0, gix-credentials v0.24.0, gix-ignore v0.11.0, gix-index v0.29.0, gix-worktree v0.30.0, gix-diff v0.40.0, gix-discover v0.29.0, gix-mailmap v0.22.0, gix-negotiate v0.12.0, gix-pack v0.47.0, gix-odb v0.57.0, gix-pathspec v0.6.0, gix-packetline v0.17.3, gix-transport v0.41.0, gix-protocol v0.44.0, gix-revision v0.26.0, gix-refspec v0.22.0, gix-status v0.5.0, gix-submodule v0.8.0, gix-worktree-state v0.7.0, gix v0.58.0, safety bump 39 crates ([`eb6aa8f`](https://github.com/Byron/gitoxide/commit/eb6aa8f502314f886fc4ea3d52ab220763968208)) - Prepare changelogs prior to release ([`6a2e0be`](https://github.com/Byron/gitoxide/commit/6a2e0bebfdf012dc2ed0ff2604086081f2a0f96d)) - Merge branch 'dirwalk' ([`5d176fc`](https://github.com/Byron/gitoxide/commit/5d176fc5ab82bfc7c194b4d929e73da9659ae8b8)) - Add `fs::read_dir::DirEntry` with precompose_unicode support. ([`24d081a`](https://github.com/Byron/gitoxide/commit/24d081a0861cca1f3fa657297ad58a937b2d903d)) @@ -70,12 +126,12 @@ A maintenance release without user-facing changes. ### Chore - - change `rust-version` manifest field back to 1.65. - They didn't actually need to be higher to work, and changing them - unecessarily can break downstream CI. - - Let's keep this value as low as possible, and only increase it when - more recent features are actually used. +- change `rust-version` manifest field back to 1.65. + They didn't actually need to be higher to work, and changing them + unecessarily can break downstream CI. + + Let's keep this value as low as possible, and only increase it when + more recent features are actually used. ### Commit Statistics @@ -103,8 +159,8 @@ A maintenance release without user-facing changes. ### Chore - - upgrade MSRV to v1.70 - Our MSRV follows the one of `helix`, which in turn follows Firefox. +- upgrade MSRV to v1.70 + Our MSRV follows the one of `helix`, which in turn follows Firefox. ### Commit Statistics @@ -257,9 +313,9 @@ A maintenance release without user-facing changes. ### Chore (BREAKING) - - update to the latest `prodash` - It makes proper usage of `Progress` types easier and allows them to be used - as `dyn` traits as well. +- update to the latest `prodash` + It makes proper usage of `Progress` types easier and allows them to be used + as `dyn` traits as well. ### New Features (BREAKING) @@ -419,7 +475,7 @@ A maintenance release without user-facing changes. ### Chore - - Add `clippy::redundant-closure-for-method-calls` lint +- Add `clippy::redundant-closure-for-method-calls` lint ### New Features @@ -621,10 +677,10 @@ A maintenance release without any user-facing changes. ### Chore - - replace `quick-error` with `thiserror` - This increases the compile time of the crate alone if there is no proc-macro - in the dependency tree, but will ever so slightly improve compile times for `gix` - as a whole. +- replace `quick-error` with `thiserror` + This increases the compile time of the crate alone if there is no proc-macro + in the dependency tree, but will ever so slightly improve compile times for `gix` + as a whole. ### New Features @@ -742,7 +798,7 @@ A maintenance release without any user-facing changes. ### Chore (BREAKING) - - upgrade to prodash v23 +- upgrade to prodash v23 ### Changed (BREAKING) @@ -775,8 +831,8 @@ A maintenance release without any user-facing changes. ### Other - - try-join with static typing works, but… - …seems like a lot of effort. Probably not worth continuing here +- try-join with static typing works, but… + …seems like a lot of effort. Probably not worth continuing here ### Bug Fixes @@ -791,8 +847,8 @@ A maintenance release without any user-facing changes. ### Chore - - uniformize deny attributes - - update sha-1 dependency to 0.10 +- uniformize deny attributes +- update sha-1 dependency to 0.10 ### New Features @@ -1432,7 +1488,7 @@ A maintenance release without user-facing changes. ### Chore (BREAKING) - - upgrade to prodash v23 +- upgrade to prodash v23 ## 0.25.1 (2022-12-26) @@ -1533,7 +1589,7 @@ A maintenance release without breaking changes. ### Chore - - uniformize deny attributes +- uniformize deny attributes ### New Features @@ -1643,7 +1699,7 @@ A maintenance release thanks to upgrade to `prodash` 18. ### Chore - - update sha-1 dependency to 0.10 +- update sha-1 dependency to 0.10 ## 0.19.0 (2022-01-19) @@ -1651,7 +1707,7 @@ A maintenance release thanks to upgrade to `prodash` 18. ### Chore - - update sha-1 dependency to 0.10 +- update sha-1 dependency to 0.10 ### New Features @@ -1749,6 +1805,6 @@ This release contains no functional changes. ### Other - - try-join with static typing works, but… - …seems like a lot of effort. Probably not worth continuing here +- try-join with static typing works, but… + …seems like a lot of effort. Probably not worth continuing here diff --git a/gix-features/Cargo.toml b/gix-features/Cargo.toml index af18eecaac2..2b0d532bdb2 100644 --- a/gix-features/Cargo.toml +++ b/gix-features/Cargo.toml @@ -2,7 +2,7 @@ name = "gix-features" description = "A crate to integrate various capabilities using compile-time feature flags" repository = "https://github.com/Byron/gitoxide" -version = "0.38.0" +version = "0.38.2" authors = ["Sebastian Thiel "] license = "MIT OR Apache-2.0" edition = "2021" @@ -25,7 +25,7 @@ progress-unit-human-numbers = ["prodash?/unit-human"] progress-unit-bytes = ["dep:bytesize", "prodash?/unit-bytes"] ## If set, walkdir iterators will be multi-threaded. -fs-walkdir-parallel = [ "dep:jwalk", "dep:gix-utils" ] +fs-walkdir-parallel = ["dep:jwalk", "dep:gix-utils"] ## Provide utilities suitable for working with the `std::fs::read_dir()`. fs-read-dir = ["dep:gix-utils"] @@ -34,10 +34,10 @@ fs-read-dir = ["dep:gix-utils"] ## ## Note that this may have overhead as well, thus instrumentations should be used stategically, only providing coarse tracing by default and adding details ## only where needed while marking them with the appropriate level. -tracing = [ "gix-trace/tracing" ] +tracing = ["gix-trace/tracing"] ## If enabled, detailed tracing is also emitted, which can greatly increase insights but at a cost. -tracing-detail = [ "gix-trace/tracing-detail" ] +tracing-detail = ["gix-trace/tracing-detail"] ## Use scoped threads and channels to parallelize common workloads on multiple objects. If enabled, it is used everywhere ## where it makes sense. @@ -45,7 +45,7 @@ tracing-detail = [ "gix-trace/tracing-detail" ] ## The `threading` module will contain thread-safe primitives for shared ownership and mutation, otherwise these will be their single threaded counterparts. ## This way, single-threaded applications don't have to pay for threaded primitives. parallel = ["dep:crossbeam-channel", - "dep:parking_lot"] + "dep:parking_lot"] ## If enabled, OnceCell will be made available for interior mutability either in sync or unsync forms. once_cell = ["dep:once_cell"] ## Makes facilities of the `walkdir` crate partially available. @@ -117,11 +117,11 @@ path = "tests/pipe.rs" required-features = ["io-pipe"] [dependencies] -gix-hash = { version = "^0.14.1", path = "../gix-hash" } -gix-trace = { version = "^0.1.7", path = "../gix-trace" } +gix-hash = { version = "^0.14.2", path = "../gix-hash" } +gix-trace = { version = "^0.1.8", path = "../gix-trace" } # for walkdir -gix-utils = { version = "^0.1.9", path = "../gix-utils", optional = true } +gix-utils = { version = "^0.1.11", path = "../gix-utils", optional = true } # 'parallel' feature crossbeam-channel = { version = "0.5.0", optional = true } @@ -159,7 +159,7 @@ bstr = { version = "1.3.0", default-features = false } # Assembly doesn't yet compile on MSVC on windows, but does on GNU, see https://github.com/RustCrypto/asm-hashes/issues/17 # At this time, only aarch64, x86 and x86_64 are supported. -[target.'cfg(all(any(target_arch = "aarch64", target_arch = "x86", target_arch = "x86_64"), not(target_env = "msvc")))'.dependencies] +[target.'cfg(all(any(target_arch = "aarch64", target_arch = "x86", target_arch = "x86_64"), not(target_os = "windows")))'.dependencies] sha1 = { version = "0.10.0", optional = true, features = ["asm"] } [package.metadata.docs.rs] diff --git a/gix-features/src/fs.rs b/gix-features/src/fs.rs index 6f9d2191d67..97f40c367f9 100644 --- a/gix-features/src/fs.rs +++ b/gix-features/src/fs.rs @@ -116,6 +116,7 @@ mod walkdir_precompose { } /// +#[allow(clippy::empty_docs)] #[cfg(feature = "fs-read-dir")] pub mod read_dir { use std::borrow::Cow; @@ -142,6 +143,7 @@ pub mod read_dir { } /// +#[allow(clippy::empty_docs)] #[cfg(feature = "fs-walkdir-parallel")] pub mod walkdir { use std::borrow::Cow; @@ -263,6 +265,7 @@ pub mod walkdir { } /// +#[allow(clippy::empty_docs)] #[cfg(all(feature = "walkdir", not(feature = "fs-walkdir-parallel")))] pub mod walkdir { use std::borrow::Cow; diff --git a/gix-features/src/lib.rs b/gix-features/src/lib.rs index 18342b1d92e..a5674d28206 100644 --- a/gix-features/src/lib.rs +++ b/gix-features/src/lib.rs @@ -15,8 +15,10 @@ #![deny(missing_docs, rust_2018_idioms, unsafe_code)] /// +#[allow(clippy::empty_docs)] pub mod cache; /// +#[allow(clippy::empty_docs)] pub mod decode; pub mod fs; pub mod hash; @@ -30,10 +32,12 @@ pub mod threading; pub use gix_trace as trace; /// +#[allow(clippy::empty_docs)] #[cfg(feature = "zlib")] pub mod zlib; /// +#[allow(clippy::empty_docs)] pub mod iter { /// An iterator over chunks of input, producing `Vec` with a size of `size`, with the last chunk being the remainder and thus /// potentially smaller than `size`. diff --git a/gix-features/src/parallel/mod.rs b/gix-features/src/parallel/mod.rs index 5a0a4b5890d..a3000c86c31 100644 --- a/gix-features/src/parallel/mod.rs +++ b/gix-features/src/parallel/mod.rs @@ -174,5 +174,6 @@ where } /// +#[allow(clippy::empty_docs)] pub mod reduce; pub use reduce::Reduce; diff --git a/gix-features/src/zlib/mod.rs b/gix-features/src/zlib/mod.rs index f55660075eb..cc977b4faa8 100644 --- a/gix-features/src/zlib/mod.rs +++ b/gix-features/src/zlib/mod.rs @@ -49,4 +49,5 @@ impl Inflate { } /// +#[allow(clippy::empty_docs)] pub mod stream; diff --git a/gix-features/src/zlib/stream/mod.rs b/gix-features/src/zlib/stream/mod.rs index 7fb239d3644..c944412e093 100644 --- a/gix-features/src/zlib/stream/mod.rs +++ b/gix-features/src/zlib/stream/mod.rs @@ -1,4 +1,6 @@ /// +#[allow(clippy::empty_docs)] pub mod deflate; /// +#[allow(clippy::empty_docs)] pub mod inflate; diff --git a/gix-filter/CHANGELOG.md b/gix-filter/CHANGELOG.md index 13c34b266bb..26edbad856a 100644 --- a/gix-filter/CHANGELOG.md +++ b/gix-filter/CHANGELOG.md @@ -5,6 +5,85 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## 0.11.2 (2024-05-22) + +A maintenance release without user-facing changes. + +### Commit Statistics + + + + - 5 commits contributed to the release over the course of 3 calendar days. + - 38 days passed between releases. + - 0 commits were understood as [conventional](https://www.conventionalcommits.org). + - 0 issues like '(#ID)' were seen in commit messages + +### Commit Details + + + +
view details + + * **Uncategorized** + - Adjust changelogs prior to release ([`9511416`](https://github.com/Byron/gitoxide/commit/9511416a6cd0c571233f958c165329c8705c2498)) + - Merge branch 'various-fixes' ([`d6cd449`](https://github.com/Byron/gitoxide/commit/d6cd44930fb204b06e2b70fc6965e7705530c47a)) + - Update dependencies ([`cd4de83`](https://github.com/Byron/gitoxide/commit/cd4de8327fc195eb862ab6e138f2315a87374f85)) + - Merge pull request from GHSA-7w47-3wg8-547c ([`79dce79`](https://github.com/Byron/gitoxide/commit/79dce79c62f6072aa2653780d590dc3993dfa401)) + - Adapt to changes in `gix-worktree` ([`1ca6a3c`](https://github.com/Byron/gitoxide/commit/1ca6a3ce22887c7eb42ec3e0a19f6e1202715745)) +
+ +## 0.11.1 (2024-04-13) + +A maintenance release without user-facing changes. + +### Commit Statistics + + + + - 4 commits contributed to the release over the course of 2 calendar days. + - 29 days passed between releases. + - 0 commits were understood as [conventional](https://www.conventionalcommits.org). + - 0 issues like '(#ID)' were seen in commit messages + +### Commit Details + + + +
view details + + * **Uncategorized** + - Release gix-trace v0.1.9, gix-utils v0.1.12, gix-packetline-blocking v0.17.4, gix-filter v0.11.1, gix-fs v0.10.2, gix-traverse v0.39.0, gix-worktree-stream v0.12.0, gix-archive v0.12.0, gix-config v0.36.1, gix-url v0.27.3, gix-index v0.32.0, gix-worktree v0.33.0, gix-diff v0.43.0, gix-pathspec v0.7.3, gix-dir v0.4.0, gix-pack v0.50.0, gix-odb v0.60.0, gix-transport v0.42.0, gix-protocol v0.45.0, gix-status v0.9.0, gix-worktree-state v0.10.0, gix v0.62.0, gix-fsck v0.4.0, gitoxide-core v0.37.0, gitoxide v0.35.0, safety bump 14 crates ([`095c673`](https://github.com/Byron/gitoxide/commit/095c6739b2722a8b9af90776b435ef2da454c0e6)) + - Prepare changelogs prior to release ([`5755271`](https://github.com/Byron/gitoxide/commit/57552717f46f96c35ba4ddc0a64434354ef845e9)) + - Merge pull request #1341 from szepeviktor/typos ([`55f379b`](https://github.com/Byron/gitoxide/commit/55f379bc47065822d078393d83d30c0835a89782)) + - Fix typos ([`f72ecce`](https://github.com/Byron/gitoxide/commit/f72ecce45babcad2a0c9b73c79d01ff502907a57)) +
+ +## 0.11.0 (2024-03-14) + +A maintenance release without user-facing changes. + +### Commit Statistics + + + + - 4 commits contributed to the release over the course of 4 calendar days. + - 18 days passed between releases. + - 0 commits were understood as [conventional](https://www.conventionalcommits.org). + - 0 issues like '(#ID)' were seen in commit messages + +### Commit Details + + + +
view details + + * **Uncategorized** + - Release gix-date v0.8.5, gix-hash v0.14.2, gix-trace v0.1.8, gix-utils v0.1.11, gix-features v0.38.1, gix-actor v0.31.0, gix-validate v0.8.4, gix-object v0.42.0, gix-path v0.10.7, gix-glob v0.16.2, gix-quote v0.4.12, gix-attributes v0.22.2, gix-command v0.3.6, gix-filter v0.11.0, gix-fs v0.10.1, gix-chunk v0.4.8, gix-commitgraph v0.24.2, gix-hashtable v0.5.2, gix-revwalk v0.13.0, gix-traverse v0.38.0, gix-worktree-stream v0.11.0, gix-archive v0.11.0, gix-config-value v0.14.6, gix-tempfile v13.1.1, gix-lock v13.1.1, gix-ref v0.43.0, gix-sec v0.10.6, gix-config v0.36.0, gix-prompt v0.8.4, gix-url v0.27.2, gix-credentials v0.24.2, gix-ignore v0.11.2, gix-bitmap v0.2.11, gix-index v0.31.0, gix-worktree v0.32.0, gix-diff v0.42.0, gix-discover v0.31.0, gix-pathspec v0.7.1, gix-dir v0.2.0, gix-macros v0.1.4, gix-mailmap v0.23.0, gix-negotiate v0.13.0, gix-pack v0.49.0, gix-odb v0.59.0, gix-packetline v0.17.4, gix-transport v0.41.2, gix-protocol v0.44.2, gix-revision v0.27.0, gix-refspec v0.23.0, gix-status v0.7.0, gix-submodule v0.10.0, gix-worktree-state v0.9.0, gix v0.60.0, safety bump 26 crates ([`b050327`](https://github.com/Byron/gitoxide/commit/b050327e76f234b19be921b78b7b28e034319fdb)) + - Prepare changelogs prior to release ([`52c3bbd`](https://github.com/Byron/gitoxide/commit/52c3bbd36b9e94a0f3a78b4ada84d0c08eba27f6)) + - Merge branch 'status' ([`3e5c974`](https://github.com/Byron/gitoxide/commit/3e5c974dd62ac134711c6c2f5a5490187a6ea55e)) + - Fix lints for nightly, and clippy ([`f8ce3d0`](https://github.com/Byron/gitoxide/commit/f8ce3d0721b6a53713a9392f2451874f520bc44c)) +
+ ## 0.10.0 (2024-02-25) ### Bug Fixes (BREAKING) @@ -15,7 +94,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - - 7 commits contributed to the release over the course of 30 calendar days. + - 8 commits contributed to the release over the course of 30 calendar days. - 36 days passed between releases. - 1 commit was understood as [conventional](https://www.conventionalcommits.org). - 0 issues like '(#ID)' were seen in commit messages @@ -27,6 +106,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
view details * **Uncategorized** + - Release gix-date v0.8.4, gix-utils v0.1.10, gix-actor v0.30.1, gix-object v0.41.1, gix-path v0.10.6, gix-glob v0.16.1, gix-quote v0.4.11, gix-attributes v0.22.1, gix-command v0.3.5, gix-filter v0.10.0, gix-commitgraph v0.24.1, gix-worktree-stream v0.10.0, gix-archive v0.10.0, gix-config-value v0.14.5, gix-ref v0.42.0, gix-sec v0.10.5, gix-config v0.35.0, gix-prompt v0.8.3, gix-url v0.27.1, gix-credentials v0.24.1, gix-ignore v0.11.1, gix-index v0.30.0, gix-worktree v0.31.0, gix-diff v0.41.0, gix-discover v0.30.0, gix-pathspec v0.7.0, gix-dir v0.1.0, gix-pack v0.48.0, gix-odb v0.58.0, gix-transport v0.41.1, gix-protocol v0.44.1, gix-revision v0.26.1, gix-refspec v0.22.1, gix-status v0.6.0, gix-submodule v0.9.0, gix-worktree-state v0.8.0, gix v0.59.0, gix-fsck v0.3.0, gitoxide-core v0.36.0, gitoxide v0.34.0, safety bump 10 crates ([`45b4470`](https://github.com/Byron/gitoxide/commit/45b447045bc826f252129c300c531acde2652c64)) - Prepare changelogs prior to release ([`f2e111f`](https://github.com/Byron/gitoxide/commit/f2e111f768fc1bc6182355261c20b63610cffec7)) - Merge branch 'entryoom' ([`684fa5c`](https://github.com/Byron/gitoxide/commit/684fa5caf82fc38dd238361d6482e77901ca0265)) - Allow buffer operations to fail gracefully on out-of-memory. ([`fc68140`](https://github.com/Byron/gitoxide/commit/fc6814024215441a9cea331f81a823a360d3f622)) @@ -69,12 +149,12 @@ A maintenance release without user-facing changes. ### Chore - - change `rust-version` manifest field back to 1.65. - They didn't actually need to be higher to work, and changing them - unecessarily can break downstream CI. - - Let's keep this value as low as possible, and only increase it when - more recent features are actually used. +- change `rust-version` manifest field back to 1.65. + They didn't actually need to be higher to work, and changing them + unecessarily can break downstream CI. + + Let's keep this value as low as possible, and only increase it when + more recent features are actually used. ### Commit Statistics @@ -102,8 +182,8 @@ A maintenance release without user-facing changes. ### Chore - - upgrade MSRV to v1.70 - Our MSRV follows the one of `helix`, which in turn follows Firefox. +- upgrade MSRV to v1.70 + Our MSRV follows the one of `helix`, which in turn follows Firefox. ### Commit Statistics @@ -326,7 +406,7 @@ A maintenance release without user-facing changes. ### Chore - - don't call crate 'WIP' in manifest anymore. +- don't call crate 'WIP' in manifest anymore. ### Commit Statistics @@ -471,8 +551,8 @@ An empty crate without any content to reserve the name for the gitoxide project. ### Chore - - uniformize deny attributes - - remove default link to cargo doc everywhere +- uniformize deny attributes +- remove default link to cargo doc everywhere ### Commit Statistics diff --git a/gix-filter/Cargo.toml b/gix-filter/Cargo.toml index 928066b5889..95e411243f4 100644 --- a/gix-filter/Cargo.toml +++ b/gix-filter/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "gix-filter" -version = "0.10.0" +version = "0.11.2" repository = "https://github.com/Byron/gitoxide" license = "MIT OR Apache-2.0" description = "A crate of the gitoxide project implementing git filters" @@ -13,15 +13,15 @@ include = ["src/**/*", "LICENSE-*"] doctest = false [dependencies] -gix-hash = { version = "^0.14.1", path = "../gix-hash" } -gix-trace = { version = "^0.1.7", path = "../gix-trace" } -gix-object = { version = "^0.41.1", path = "../gix-object" } -gix-command = { version = "^0.3.5", path = "../gix-command" } -gix-quote = { version = "^0.4.11", path = "../gix-quote" } -gix-utils = { version = "^0.1.10", path = "../gix-utils" } -gix-path = { version = "^0.10.6", path = "../gix-path" } -gix-packetline = { package = "gix-packetline-blocking", version = "^0.17.3", path = "../gix-packetline-blocking" } -gix-attributes = { version = "^0.22.1", path = "../gix-attributes" } +gix-hash = { version = "^0.14.2", path = "../gix-hash" } +gix-trace = { version = "^0.1.9", path = "../gix-trace" } +gix-object = { version = "^0.42.2", path = "../gix-object" } +gix-command = { version = "^0.3.7", path = "../gix-command" } +gix-quote = { version = "^0.4.12", path = "../gix-quote" } +gix-utils = { version = "^0.1.12", path = "../gix-utils" } +gix-path = { version = "^0.10.7", path = "../gix-path" } +gix-packetline = { package = "gix-packetline-blocking", version = "^0.17.4", path = "../gix-packetline-blocking" } +gix-attributes = { version = "^0.22.2", path = "../gix-attributes" } encoding_rs = "0.8.32" bstr = { version = "1.5.0", default-features = false, features = ["std"] } @@ -30,6 +30,6 @@ smallvec = "1.10.0" [dev-dependencies] -serial_test = { version = "2.0.0", default-features = false } +serial_test = { version = "3.1.0", default-features = false } gix-testtools = { path = "../tests/tools" } gix-worktree = { path = "../gix-worktree", default-features = false, features = ["attributes"] } diff --git a/gix-filter/src/driver/delayed.rs b/gix-filter/src/driver/delayed.rs index 4599ac61098..434afb38c81 100644 --- a/gix-filter/src/driver/delayed.rs +++ b/gix-filter/src/driver/delayed.rs @@ -6,6 +6,7 @@ use crate::{ }; /// +#[allow(clippy::empty_docs)] pub mod list { use crate::driver; @@ -23,6 +24,7 @@ pub mod list { } /// +#[allow(clippy::empty_docs)] pub mod fetch { use crate::driver; diff --git a/gix-filter/src/driver/mod.rs b/gix-filter/src/driver/mod.rs index 43d3edb2f8b..f85c996ee0c 100644 --- a/gix-filter/src/driver/mod.rs +++ b/gix-filter/src/driver/mod.rs @@ -3,18 +3,23 @@ use std::collections::HashMap; use bstr::{BStr, BString, ByteSlice, ByteVec}; /// +#[allow(clippy::empty_docs)] pub mod init; /// +#[allow(clippy::empty_docs)] pub mod apply; /// +#[allow(clippy::empty_docs)] pub mod shutdown; /// +#[allow(clippy::empty_docs)] pub mod delayed; /// +#[allow(clippy::empty_docs)] pub mod process; /// A literal driver process. diff --git a/gix-filter/src/driver/process/client.rs b/gix-filter/src/driver/process/client.rs index 912b1629091..40a3c97e3c8 100644 --- a/gix-filter/src/driver/process/client.rs +++ b/gix-filter/src/driver/process/client.rs @@ -8,6 +8,7 @@ use crate::driver::{ }; /// +#[allow(clippy::empty_docs)] pub mod handshake { /// The error returned by [Client::handshake()][super::Client::handshake()]. #[derive(Debug, thiserror::Error)] @@ -23,6 +24,7 @@ pub mod handshake { } /// +#[allow(clippy::empty_docs)] pub mod invoke { /// The error returned by [Client::invoke()][super::Client::invoke()]. #[derive(Debug, thiserror::Error)] @@ -33,6 +35,7 @@ pub mod invoke { } /// + #[allow(clippy::empty_docs)] pub mod without_content { /// The error returned by [Client::invoke_without_content()][super::super::Client::invoke_without_content()]. #[derive(Debug, thiserror::Error)] diff --git a/gix-filter/src/driver/process/mod.rs b/gix-filter/src/driver/process/mod.rs index c2f62ddd260..d8e95806ef7 100644 --- a/gix-filter/src/driver/process/mod.rs +++ b/gix-filter/src/driver/process/mod.rs @@ -104,9 +104,11 @@ impl Status { } /// +#[allow(clippy::empty_docs)] pub mod client; /// +#[allow(clippy::empty_docs)] pub mod server; type PacketlineReader<'a, T = std::process::ChildStdout> = diff --git a/gix-filter/src/driver/process/server.rs b/gix-filter/src/driver/process/server.rs index 9a634e5ad38..7b2ad17afd7 100644 --- a/gix-filter/src/driver/process/server.rs +++ b/gix-filter/src/driver/process/server.rs @@ -14,6 +14,7 @@ pub struct Request<'a> { } /// +#[allow(clippy::empty_docs)] pub mod next_request { use bstr::BString; @@ -31,6 +32,7 @@ pub mod next_request { } /// +#[allow(clippy::empty_docs)] pub mod handshake { /// The error returned by [Server::handshake()][super::Server::handshake()]. #[derive(Debug, thiserror::Error)] diff --git a/gix-filter/src/eol/mod.rs b/gix-filter/src/eol/mod.rs index ad1553826e0..7cd7be7eb3f 100644 --- a/gix-filter/src/eol/mod.rs +++ b/gix-filter/src/eol/mod.rs @@ -1,4 +1,5 @@ /// +#[allow(clippy::empty_docs)] pub mod convert_to_git; pub use convert_to_git::function::convert_to_git; diff --git a/gix-filter/src/lib.rs b/gix-filter/src/lib.rs index fbe250607e5..b816237b80c 100644 --- a/gix-filter/src/lib.rs +++ b/gix-filter/src/lib.rs @@ -29,6 +29,7 @@ pub mod worktree; pub mod driver; /// +#[allow(clippy::empty_docs)] pub mod pipeline; /// The standard git filter pipeline comprised of multiple standard filters and support for external filters. diff --git a/gix-filter/src/pipeline/convert.rs b/gix-filter/src/pipeline/convert.rs index 0572dd451b1..8154f98e40b 100644 --- a/gix-filter/src/pipeline/convert.rs +++ b/gix-filter/src/pipeline/convert.rs @@ -5,6 +5,7 @@ use bstr::BStr; use crate::{driver, eol, ident, pipeline::util::Configuration, worktree, Pipeline}; /// +#[allow(clippy::empty_docs)] pub mod configuration { use bstr::BString; @@ -20,6 +21,7 @@ pub mod configuration { } /// +#[allow(clippy::empty_docs)] pub mod to_git { /// A function that fills `buf` `fn(&mut buf)` with the data stored in the index of the file that should be converted. pub type IndexObjectFn<'a> = dyn FnMut(&mut Vec) -> Result, gix_object::find::Error> + 'a; @@ -44,6 +46,7 @@ pub mod to_git { } /// +#[allow(clippy::empty_docs)] pub mod to_worktree { /// The error returned by [Pipeline::convert_to_worktree()][super::Pipeline::convert_to_worktree()]. #[derive(Debug, thiserror::Error)] diff --git a/gix-filter/src/pipeline/mod.rs b/gix-filter/src/pipeline/mod.rs index 7dff070a4ce..a5f556ee8d5 100644 --- a/gix-filter/src/pipeline/mod.rs +++ b/gix-filter/src/pipeline/mod.rs @@ -110,6 +110,7 @@ impl Pipeline { } /// +#[allow(clippy::empty_docs)] pub mod convert; pub(crate) mod util; diff --git a/gix-filter/src/worktree/encoding.rs b/gix-filter/src/worktree/encoding.rs index 0b75adc96aa..df4b1ee7985 100644 --- a/gix-filter/src/worktree/encoding.rs +++ b/gix-filter/src/worktree/encoding.rs @@ -2,6 +2,7 @@ use bstr::BStr; use encoding_rs::Encoding; /// +#[allow(clippy::empty_docs)] pub mod for_label { use bstr::BString; diff --git a/gix-filter/src/worktree/mod.rs b/gix-filter/src/worktree/mod.rs index 3b13ea49ede..167d1266e32 100644 --- a/gix-filter/src/worktree/mod.rs +++ b/gix-filter/src/worktree/mod.rs @@ -5,12 +5,15 @@ //! can be taken, which we do not yet take unless there is specific examples or problems to solve. /// +#[allow(clippy::empty_docs)] pub mod encoding; /// +#[allow(clippy::empty_docs)] pub mod encode_to_git; pub use encode_to_git::function::encode_to_git; /// +#[allow(clippy::empty_docs)] pub mod encode_to_worktree; pub use encode_to_worktree::function::encode_to_worktree; diff --git a/gix-filter/tests/eol/convert_to_worktree.rs b/gix-filter/tests/eol/convert_to_worktree.rs index 25e50312133..d61a1ea96c7 100644 --- a/gix-filter/tests/eol/convert_to_worktree.rs +++ b/gix-filter/tests/eol/convert_to_worktree.rs @@ -59,7 +59,7 @@ fn no_conversion_if_nothing_to_do() -> crate::Result { "mixed crlf and lf is avoided", ), ( - &b"elligible-but-disabled\nhere"[..], + &b"eligible-but-disabled\nhere"[..], AttributesDigest::Binary, "designated binary is never handled", ), diff --git a/gix-filter/tests/fixtures/baseline.sh b/gix-filter/tests/fixtures/baseline.sh index c02b3c22aef..c27d559bb28 100755 --- a/gix-filter/tests/fixtures/baseline.sh +++ b/gix-filter/tests/fixtures/baseline.sh @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash set -eu -o pipefail driver=${1:?First argument is the driver program supporting both process mode and clean/smudge} @@ -17,7 +17,7 @@ function repo_assertions() { git config filter.arrow.clean "$driver clean %f" git config filter.arrow.smudge "$driver smudge %f" - git config filter.arrow.requred true + git config filter.arrow.required true repo_assertions ) @@ -26,7 +26,7 @@ function repo_assertions() { git init process && cd process git config filter.arrow.process "$driver process" - git config filter.arrow.requred true + git config filter.arrow.required true repo_assertions ) @@ -35,7 +35,7 @@ function repo_assertions() { git init process-no-delay && cd process-no-delay git config filter.arrow.process "$driver process disallow-delay" - git config filter.arrow.requred true + git config filter.arrow.required true repo_assertions ) diff --git a/gix-filter/tests/fixtures/generated-archives/pipeline_repos.tar.xz b/gix-filter/tests/fixtures/generated-archives/pipeline_repos.tar.xz index 2079bf649fc..549b249f39e 100644 Binary files a/gix-filter/tests/fixtures/generated-archives/pipeline_repos.tar.xz and b/gix-filter/tests/fixtures/generated-archives/pipeline_repos.tar.xz differ diff --git a/gix-filter/tests/fixtures/pipeline_repos.sh b/gix-filter/tests/fixtures/pipeline_repos.sh index e2f64f99e62..c6360c7a090 100755 --- a/gix-filter/tests/fixtures/pipeline_repos.sh +++ b/gix-filter/tests/fixtures/pipeline_repos.sh @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash set -eu -o pipefail (mkdir all-filters && cd all-filters diff --git a/gix-filter/tests/pipeline/convert_to_git.rs b/gix-filter/tests/pipeline/convert_to_git.rs index 79d79993238..5cff5c888e3 100644 --- a/gix-filter/tests/pipeline/convert_to_git.rs +++ b/gix-filter/tests/pipeline/convert_to_git.rs @@ -53,7 +53,7 @@ fn all_stages_mean_streaming_is_impossible() -> gix_testtools::Result { Path::new("any.txt"), &mut |path, attrs| { cache - .at_entry(path, Some(false), &gix_object::find::Never) + .at_entry(path, None, &gix_object::find::Never) .expect("cannot fail") .matching_attributes(attrs); }, @@ -82,7 +82,7 @@ fn only_driver_means_streaming_is_possible() -> gix_testtools::Result { Path::new("subdir/doesnot/matter/any.txt"), &mut |path, attrs| { cache - .at_entry(path, Some(false), &gix_object::find::Never) + .at_entry(path, None, &gix_object::find::Never) .expect("cannot fail") .matching_attributes(attrs); }, @@ -112,7 +112,7 @@ fn no_filter_means_reader_is_returned_unchanged() -> gix_testtools::Result { Path::new("other.txt"), &mut |path, attrs| { cache - .at_entry(path, Some(false), &gix_object::find::Never) + .at_entry(path, None, &gix_object::find::Never) .expect("cannot fail") .matching_attributes(attrs); }, diff --git a/gix-filter/tests/pipeline/convert_to_worktree.rs b/gix-filter/tests/pipeline/convert_to_worktree.rs index 2449c50304a..be757e01ab8 100644 --- a/gix-filter/tests/pipeline/convert_to_worktree.rs +++ b/gix-filter/tests/pipeline/convert_to_worktree.rs @@ -21,7 +21,7 @@ fn all_stages() -> gix_testtools::Result { "any.txt".into(), &mut |path, attrs| { cache - .at_entry(path, Some(false), &gix_object::find::Never) + .at_entry(path, None, &gix_object::find::Never) .expect("cannot fail") .matching_attributes(attrs); }, @@ -54,7 +54,7 @@ fn all_stages_no_filter() -> gix_testtools::Result { "other.txt".into(), &mut |path, attrs| { cache - .at_entry(path, Some(false), &gix_object::find::Never) + .at_entry(path, None, &gix_object::find::Never) .expect("cannot fail") .matching_attributes(attrs); }, @@ -86,7 +86,7 @@ fn no_filter() -> gix_testtools::Result { "other.txt".into(), &mut |path, attrs| { cache - .at_entry(path, Some(false), &gix_object::find::Never) + .at_entry(path, None, &gix_object::find::Never) .expect("cannot fail") .matching_attributes(attrs); }, diff --git a/gix-fs/CHANGELOG.md b/gix-fs/CHANGELOG.md index b7d36bf40f2..8b8918e1792 100644 --- a/gix-fs/CHANGELOG.md +++ b/gix-fs/CHANGELOG.md @@ -5,6 +5,137 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## 0.11.1 (2024-05-28) + +A maintenance release without user-facing changes. + +### Commit Statistics + + + + - 3 commits contributed to the release. + - 6 days passed between releases. + - 0 commits were understood as [conventional](https://www.conventionalcommits.org). + - 0 issues like '(#ID)' were seen in commit messages + +### Commit Details + + + +
view details + + * **Uncategorized** + - Prepare gix-fs changelog prior to release ([`eb7880b`](https://github.com/Byron/gitoxide/commit/eb7880b32803d5d91a1be563da07f80d3213f7d0)) + - Merge branch 'dir-as-ignore' ([`31d2f0a`](https://github.com/Byron/gitoxide/commit/31d2f0a742257d7031df114f0c92197a0781921e)) + - Adapt to changes in `gix-glob` ([`5fbbaaa`](https://github.com/Byron/gitoxide/commit/5fbbaaa10d919dd216badb05b2fae32d5dd955c9)) +
+ +## 0.11.0 (2024-05-22) + +### Bug Fixes + + - multi-process safe parallel filesystem capabilities probing + This is achieved by making filenames unique so they won't clash. + - default to creating file-symlinks if it is dangling on Windows + This behaviour is the same as in Git. + +### Bug Fixes (BREAKING) + + - validate all components pushed onto the stack when creating leading paths. + This way, everyone using the stack with the purpose of + altering the working tree will run additional checks to prevent callers + from sneaking in forbidden paths. + + Note that these checks don't run otherwise, so one has to be careful + to not forget to run these checks whenever needed. + +### Commit Statistics + + + + - 15 commits contributed to the release over the course of 8 calendar days. + - 38 days passed between releases. + - 3 commits were understood as [conventional](https://www.conventionalcommits.org). + - 2 unique issues were worked on: [#1354](https://github.com/Byron/gitoxide/issues/1354), [#1373](https://github.com/Byron/gitoxide/issues/1373) + +### Commit Details + + + +
view details + + * **[#1354](https://github.com/Byron/gitoxide/issues/1354)** + - Default to creating file-symlinks if it is dangling on Windows ([`31d02a8`](https://github.com/Byron/gitoxide/commit/31d02a89ee15a0df463a5b5c34864497f8adfae1)) + * **[#1373](https://github.com/Byron/gitoxide/issues/1373)** + - Multi-process safe parallel filesystem capabilities probing ([`bec648d`](https://github.com/Byron/gitoxide/commit/bec648dc5790186cddbe12277978baf572f8e164)) + * **Uncategorized** + - Release gix-features v0.38.2, gix-actor v0.31.2, gix-validate v0.8.5, gix-object v0.42.2, gix-command v0.3.7, gix-filter v0.11.2, gix-fs v0.11.0, gix-revwalk v0.13.1, gix-traverse v0.39.1, gix-worktree-stream v0.13.0, gix-archive v0.13.0, gix-tempfile v14.0.0, gix-lock v14.0.0, gix-ref v0.44.0, gix-config v0.37.0, gix-prompt v0.8.5, gix-index v0.33.0, gix-worktree v0.34.0, gix-diff v0.44.0, gix-discover v0.32.0, gix-pathspec v0.7.5, gix-dir v0.5.0, gix-macros v0.1.5, gix-mailmap v0.23.1, gix-negotiate v0.13.1, gix-pack v0.51.0, gix-odb v0.61.0, gix-transport v0.42.1, gix-protocol v0.45.1, gix-revision v0.27.1, gix-status v0.10.0, gix-submodule v0.11.0, gix-worktree-state v0.11.0, gix v0.63.0, gitoxide-core v0.38.0, gitoxide v0.36.0, safety bump 19 crates ([`4f98e94`](https://github.com/Byron/gitoxide/commit/4f98e94e0e8b79ed2899b35bef40f3c30b3025b0)) + - Adjust changelogs prior to release ([`9511416`](https://github.com/Byron/gitoxide/commit/9511416a6cd0c571233f958c165329c8705c2498)) + - Merge branch 'various-fixes' ([`d6cd449`](https://github.com/Byron/gitoxide/commit/d6cd44930fb204b06e2b70fc6965e7705530c47a)) + - Merge pull request from GHSA-7w47-3wg8-547c ([`79dce79`](https://github.com/Byron/gitoxide/commit/79dce79c62f6072aa2653780d590dc3993dfa401)) + - Apply suggestions from code review ([`1242151`](https://github.com/Byron/gitoxide/commit/1242151079004ae99fae7b80966de151961a6159)) + - Adapt to changes in `gix-index` ([`5f86e6b`](https://github.com/Byron/gitoxide/commit/5f86e6b11bb73921b458ffee9091bc028a7d6204)) + - Apply suggestions from code review ([`ccbc119`](https://github.com/Byron/gitoxide/commit/ccbc1197b6dcb7e7118e206183582d6b46fc5ebc)) + - Address review comments ([`fcc3b69`](https://github.com/Byron/gitoxide/commit/fcc3b69867db1628f6a44d0e0dad8f7417f566bc)) + - Apply suggestions from code review ([`bad9a79`](https://github.com/Byron/gitoxide/commit/bad9a797b99880ce9d1c20e11c801bd0e741db64)) + - Validate all components pushed onto the stack when creating leading paths. ([`874cfd6`](https://github.com/Byron/gitoxide/commit/874cfd6dd7e371f178ec5f63368220b272608805)) + - Add validation for path components and tree-names ([`0d78db2`](https://github.com/Byron/gitoxide/commit/0d78db2440c3866bfa972c8773aa7d8e7b245f2e)) + - Merge branch 'status' ([`04ef31e`](https://github.com/Byron/gitoxide/commit/04ef31e9d6f5332d49037a5a4c248ebbb5aaf92b)) + - Improve the symlink probing by simplifying it ([`7a3c583`](https://github.com/Byron/gitoxide/commit/7a3c583d51629697804c9f6d122b505b0afcefc8)) +
+ +## 0.10.2 (2024-04-13) + +A maintenance release without user-facing changes. + +### Commit Statistics + + + + - 4 commits contributed to the release over the course of 2 calendar days. + - 29 days passed between releases. + - 0 commits were understood as [conventional](https://www.conventionalcommits.org). + - 0 issues like '(#ID)' were seen in commit messages + +### Commit Details + + + +
view details + + * **Uncategorized** + - Release gix-trace v0.1.9, gix-utils v0.1.12, gix-packetline-blocking v0.17.4, gix-filter v0.11.1, gix-fs v0.10.2, gix-traverse v0.39.0, gix-worktree-stream v0.12.0, gix-archive v0.12.0, gix-config v0.36.1, gix-url v0.27.3, gix-index v0.32.0, gix-worktree v0.33.0, gix-diff v0.43.0, gix-pathspec v0.7.3, gix-dir v0.4.0, gix-pack v0.50.0, gix-odb v0.60.0, gix-transport v0.42.0, gix-protocol v0.45.0, gix-status v0.9.0, gix-worktree-state v0.10.0, gix v0.62.0, gix-fsck v0.4.0, gitoxide-core v0.37.0, gitoxide v0.35.0, safety bump 14 crates ([`095c673`](https://github.com/Byron/gitoxide/commit/095c6739b2722a8b9af90776b435ef2da454c0e6)) + - Prepare changelogs prior to release ([`5755271`](https://github.com/Byron/gitoxide/commit/57552717f46f96c35ba4ddc0a64434354ef845e9)) + - Merge pull request #1341 from szepeviktor/typos ([`55f379b`](https://github.com/Byron/gitoxide/commit/55f379bc47065822d078393d83d30c0835a89782)) + - Fix typos ([`f72ecce`](https://github.com/Byron/gitoxide/commit/f72ecce45babcad2a0c9b73c79d01ff502907a57)) +
+ +## 0.10.1 (2024-03-14) + +A maintenance release without user-facing changes. + +### Commit Statistics + + + + - 4 commits contributed to the release over the course of 4 calendar days. + - 54 days passed between releases. + - 0 commits were understood as [conventional](https://www.conventionalcommits.org). + - 0 issues like '(#ID)' were seen in commit messages + +### Commit Details + + + +
view details + + * **Uncategorized** + - Release gix-date v0.8.5, gix-hash v0.14.2, gix-trace v0.1.8, gix-utils v0.1.11, gix-features v0.38.1, gix-actor v0.31.0, gix-validate v0.8.4, gix-object v0.42.0, gix-path v0.10.7, gix-glob v0.16.2, gix-quote v0.4.12, gix-attributes v0.22.2, gix-command v0.3.6, gix-filter v0.11.0, gix-fs v0.10.1, gix-chunk v0.4.8, gix-commitgraph v0.24.2, gix-hashtable v0.5.2, gix-revwalk v0.13.0, gix-traverse v0.38.0, gix-worktree-stream v0.11.0, gix-archive v0.11.0, gix-config-value v0.14.6, gix-tempfile v13.1.1, gix-lock v13.1.1, gix-ref v0.43.0, gix-sec v0.10.6, gix-config v0.36.0, gix-prompt v0.8.4, gix-url v0.27.2, gix-credentials v0.24.2, gix-ignore v0.11.2, gix-bitmap v0.2.11, gix-index v0.31.0, gix-worktree v0.32.0, gix-diff v0.42.0, gix-discover v0.31.0, gix-pathspec v0.7.1, gix-dir v0.2.0, gix-macros v0.1.4, gix-mailmap v0.23.0, gix-negotiate v0.13.0, gix-pack v0.49.0, gix-odb v0.59.0, gix-packetline v0.17.4, gix-transport v0.41.2, gix-protocol v0.44.2, gix-revision v0.27.0, gix-refspec v0.23.0, gix-status v0.7.0, gix-submodule v0.10.0, gix-worktree-state v0.9.0, gix v0.60.0, safety bump 26 crates ([`b050327`](https://github.com/Byron/gitoxide/commit/b050327e76f234b19be921b78b7b28e034319fdb)) + - Prepare changelogs prior to release ([`52c3bbd`](https://github.com/Byron/gitoxide/commit/52c3bbd36b9e94a0f3a78b4ada84d0c08eba27f6)) + - Merge branch 'status' ([`3e5c974`](https://github.com/Byron/gitoxide/commit/3e5c974dd62ac134711c6c2f5a5490187a6ea55e)) + - Fix lints for nightly, and clippy ([`f8ce3d0`](https://github.com/Byron/gitoxide/commit/f8ce3d0721b6a53713a9392f2451874f520bc44c)) +
+ ## 0.10.0 (2024-01-20) ### New Features @@ -19,7 +150,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - - 4 commits contributed to the release over the course of 2 calendar days. + - 5 commits contributed to the release over the course of 2 calendar days. - 20 days passed between releases. - 2 commits were understood as [conventional](https://www.conventionalcommits.org). - 0 issues like '(#ID)' were seen in commit messages @@ -31,6 +162,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
view details * **Uncategorized** + - Release gix-utils v0.1.9, gix-features v0.38.0, gix-actor v0.30.0, gix-object v0.41.0, gix-path v0.10.4, gix-glob v0.16.0, gix-attributes v0.22.0, gix-command v0.3.3, gix-packetline-blocking v0.17.3, gix-filter v0.9.0, gix-fs v0.10.0, gix-commitgraph v0.24.0, gix-revwalk v0.12.0, gix-traverse v0.37.0, gix-worktree-stream v0.9.0, gix-archive v0.9.0, gix-config-value v0.14.4, gix-tempfile v13.0.0, gix-lock v13.0.0, gix-ref v0.41.0, gix-sec v0.10.4, gix-config v0.34.0, gix-url v0.27.0, gix-credentials v0.24.0, gix-ignore v0.11.0, gix-index v0.29.0, gix-worktree v0.30.0, gix-diff v0.40.0, gix-discover v0.29.0, gix-mailmap v0.22.0, gix-negotiate v0.12.0, gix-pack v0.47.0, gix-odb v0.57.0, gix-pathspec v0.6.0, gix-packetline v0.17.3, gix-transport v0.41.0, gix-protocol v0.44.0, gix-revision v0.26.0, gix-refspec v0.22.0, gix-status v0.5.0, gix-submodule v0.8.0, gix-worktree-state v0.7.0, gix v0.58.0, safety bump 39 crates ([`eb6aa8f`](https://github.com/Byron/gitoxide/commit/eb6aa8f502314f886fc4ea3d52ab220763968208)) - Prepare changelogs prior to release ([`6a2e0be`](https://github.com/Byron/gitoxide/commit/6a2e0bebfdf012dc2ed0ff2604086081f2a0f96d)) - Merge branch 'dirwalk' ([`5d176fc`](https://github.com/Byron/gitoxide/commit/5d176fc5ab82bfc7c194b4d929e73da9659ae8b8)) - Add `read_dir(precompose_unicode)` ([`f6e4bba`](https://github.com/Byron/gitoxide/commit/f6e4bba28062faf29795a87e0e569366df0ab4f1)) @@ -43,12 +175,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Chore - - change `rust-version` manifest field back to 1.65. - They didn't actually need to be higher to work, and changing them - unecessarily can break downstream CI. - - Let's keep this value as low as possible, and only increase it when - more recent features are actually used. +- change `rust-version` manifest field back to 1.65. + They didn't actually need to be higher to work, and changing them + unecessarily can break downstream CI. + + Let's keep this value as low as possible, and only increase it when + more recent features are actually used. ### Commit Statistics @@ -76,8 +208,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Chore - - upgrade MSRV to v1.70 - Our MSRV follows the one of `helix`, which in turn follows Firefox. +- upgrade MSRV to v1.70 + Our MSRV follows the one of `helix`, which in turn follows Firefox. ### Commit Statistics @@ -335,7 +467,7 @@ A maintenance release without user-facing changes. ### Chore - - inline format args +- inline format args ### Commit Statistics diff --git a/gix-fs/Cargo.toml b/gix-fs/Cargo.toml index 931226f9b42..830b23bbfef 100644 --- a/gix-fs/Cargo.toml +++ b/gix-fs/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "gix-fs" -version = "0.10.0" +version = "0.11.1" repository = "https://github.com/Byron/gitoxide" license = "MIT OR Apache-2.0" description = "A crate providing file system specific utilities to `gitoxide`" @@ -17,9 +17,13 @@ doctest = false serde = ["dep:serde"] [dependencies] -gix-features = { version = "^0.38.0", path = "../gix-features", features = ["fs-read-dir"] } -gix-utils = { version = "^0.1.9", path = "../gix-utils" } +gix-features = { version = "^0.38.2", path = "../gix-features", features = ["fs-read-dir"] } +gix-utils = { version = "^0.1.12", path = "../gix-utils" } serde = { version = "1.0.114", optional = true, default-features = false, features = ["std", "derive"] } +# For `Capabilities` to assure parallel operation works. +fastrand = { version = "2.1.0", default-features = false, features = ["std"] } + [dev-dependencies] tempfile = "3.5.0" +crossbeam-channel = "0.5.0" diff --git a/gix-fs/src/capabilities.rs b/gix-fs/src/capabilities.rs index 4c0d2f8d95e..4fa6892eed7 100644 --- a/gix-fs/src/capabilities.rs +++ b/gix-fs/src/capabilities.rs @@ -60,7 +60,8 @@ impl Capabilities { use std::os::unix::fs::{MetadataExt, OpenOptionsExt}; // test it exactly as we typically create executable files, not using chmod. - let test_path = root.join("_test_executable_bit"); + let rand = fastrand::usize(..); + let test_path = root.join(format!("_test_executable_bit{rand}")); let res = std::fs::OpenOptions::new() .create_new(true) .write(true) @@ -87,8 +88,9 @@ impl Capabilities { } fn probe_precompose_unicode(root: &Path) -> std::io::Result { - let precomposed = "ä"; - let decomposed = "a\u{308}"; + let rand = fastrand::usize(..); + let precomposed = format!("ä{rand}"); + let decomposed = format!("a\u{308}{rand}"); let precomposed = root.join(precomposed); std::fs::OpenOptions::new() @@ -101,22 +103,14 @@ impl Capabilities { } fn probe_symlink(root: &Path) -> std::io::Result { - let src_path = root.join("__link_src_file"); - std::fs::OpenOptions::new() - .create_new(true) - .write(true) - .open(&src_path)?; - let link_path = root.join("__file_link"); - if crate::symlink::create(&src_path, &link_path).is_err() { - std::fs::remove_file(&src_path)?; + let rand = fastrand::usize(..); + let link_path = root.join(format!("__file_link{rand}")); + if crate::symlink::create("dangling".as_ref(), &link_path).is_err() { return Ok(false); } let res = std::fs::symlink_metadata(&link_path).map(|m| m.file_type().is_symlink()); - - let cleanup = crate::symlink::remove(&link_path).or_else(|_| std::fs::remove_file(&link_path)); - std::fs::remove_file(&src_path).and(cleanup)?; - + crate::symlink::remove(&link_path).or_else(|_| std::fs::remove_file(&link_path))?; res } } diff --git a/gix-fs/src/dir/mod.rs b/gix-fs/src/dir/mod.rs index 4c709a6a88a..ae181a3f01f 100644 --- a/gix-fs/src/dir/mod.rs +++ b/gix-fs/src/dir/mod.rs @@ -1,4 +1,6 @@ /// +#[allow(clippy::empty_docs)] pub mod create; /// +#[allow(clippy::empty_docs)] pub mod remove; diff --git a/gix-fs/src/lib.rs b/gix-fs/src/lib.rs index 9d02ea33426..2b8a209e9a7 100644 --- a/gix-fs/src/lib.rs +++ b/gix-fs/src/lib.rs @@ -8,7 +8,7 @@ use std::path::PathBuf; #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] #[derive(PartialEq, Eq, Debug, Hash, Ord, PartialOrd, Clone, Copy)] pub struct Capabilities { - /// If `true`, the filesystem will consider the precomposed umlaut `ä` similiar to its decomposed form `"a\u{308}"` and consider them the same. + /// If `true`, the filesystem will consider the precomposed umlaut `ä` similar to its decomposed form `"a\u{308}"` and consider them the same. /// If `false`, the filesystem will only see bytes which means that the above example could live side-by-side. /// /// Even though a filesystem that treats both forms the same will still reproduce the exact same byte sequence during traversal for instance, @@ -36,13 +36,16 @@ mod snapshot; pub use snapshot::{FileSnapshot, SharedFileSnapshot, SharedFileSnapshotMut}; /// +#[allow(clippy::empty_docs)] pub mod symlink; /// +#[allow(clippy::empty_docs)] pub mod read_dir; pub use read_dir::function::read_dir; /// +#[allow(clippy::empty_docs)] pub mod dir; /// Like [`std::env::current_dir()`], but it will `precompose_unicode` if that value is true, if the current directory @@ -90,4 +93,5 @@ pub fn is_executable(_metadata: &std::fs::Metadata) -> bool { } /// +#[allow(clippy::empty_docs)] pub mod stack; diff --git a/gix-fs/src/stack.rs b/gix-fs/src/stack.rs index 5d3dfeccd34..c5cf73ca459 100644 --- a/gix-fs/src/stack.rs +++ b/gix-fs/src/stack.rs @@ -1,4 +1,4 @@ -use std::path::{Path, PathBuf}; +use std::path::{Component, Path, PathBuf}; use crate::Stack; @@ -22,20 +22,22 @@ impl Stack { /// A delegate for use in a [`Stack`]. pub trait Delegate { - /// Called whenever we push a directory on top of the stack, after the fact. + /// Called whenever we push a directory on top of the stack, and after the respective call to [`push()`](Self::push). /// - /// It is also called if the currently acted on path is a directory in itself. - /// Use `stack.current()` to see the directory. + /// It is only called if the currently acted on path is a directory in itself, which is determined by knowing + /// that it's not the last component of the path. + /// Use [`Stack::current()`] to see the directory. fn push_directory(&mut self, stack: &Stack) -> std::io::Result<()>; - /// Called after any component was pushed, with the path available at `stack.current()`. + /// Called after any component was pushed, with the path available at [`Stack::current()`]. /// - /// `is_last_component` is true if the path is completely built. + /// `is_last_component` is `true` if the path is completely built, which typically means it's not a directory. fn push(&mut self, is_last_component: bool, stack: &Stack) -> std::io::Result<()>; /// Called right after a directory-component was popped off the stack. /// - /// Use it to pop information off internal data structures. + /// Use it to pop information off internal data structures. Note that no equivalent call exists for popping + /// the file-component. fn pop_directory(&mut self); } @@ -58,12 +60,15 @@ impl Stack { /// The full path to `relative` will be returned along with the data returned by `push_comp`. /// Note that this only works correctly for the delegate's `push_directory()` and `pop_directory()` methods if /// `relative` paths are terminal, so point to their designated file or directory. + /// The path is also expected to be normalized, and should not contain extra separators, and must not contain `..` + /// or have leading or trailing slashes (or additionally backslashes on Windows). pub fn make_relative_path_current(&mut self, relative: &Path, delegate: &mut dyn Delegate) -> std::io::Result<()> { - debug_assert!( - relative.is_relative(), - "only index paths are handled correctly here, must be relative" - ); - + if self.valid_components != 0 && relative.as_os_str().is_empty() { + return Err(std::io::Error::new( + std::io::ErrorKind::Other, + "empty inputs are not allowed", + )); + } if self.valid_components == 0 { delegate.push_directory(self)?; } @@ -95,6 +100,15 @@ impl Stack { } while let Some(comp) = components.next() { + if !matches!(comp, Component::Normal(_)) { + return Err(std::io::Error::new( + std::io::ErrorKind::Other, + format!( + "Input path \"{}\" contains relative or absolute components", + relative.display() + ), + )); + } let is_last_component = components.peek().is_none(); self.current_is_directory = !is_last_component; self.current.push(comp); diff --git a/gix-fs/src/symlink.rs b/gix-fs/src/symlink.rs index 55798daa3e9..ce639c48f65 100644 --- a/gix-fs/src/symlink.rs +++ b/gix-fs/src/symlink.rs @@ -1,15 +1,17 @@ use std::{io, io::ErrorKind::AlreadyExists, path::Path}; -#[cfg(not(windows))] /// Create a new symlink at `link` which points to `original`. +/// +/// Note that `original` doesn't have to exist. +#[cfg(not(windows))] pub fn create(original: &Path, link: &Path) -> io::Result<()> { std::os::unix::fs::symlink(original, link) } -#[cfg(not(windows))] /// Remove a symlink. /// /// Note that on only on windows this is special. +#[cfg(not(windows))] pub fn remove(path: &Path) -> io::Result<()> { std::fs::remove_file(path) } @@ -29,21 +31,31 @@ pub fn remove(path: &Path) -> io::Result<()> { } } -#[cfg(windows)] /// Create a new symlink at `link` which points to `original`. +/// +/// Note that if a symlink target (the `original`) isn't present on disk, it's assumed to be a +/// file, creating a dangling file symlink. This is similar to a dangling symlink on Unix, +/// which doesn't have to care about the target type though. +#[cfg(windows)] pub fn create(original: &Path, link: &Path) -> io::Result<()> { use std::os::windows::fs::{symlink_dir, symlink_file}; // TODO: figure out if links to links count as files or whatever they point at - if std::fs::metadata(link.parent().expect("dir for link").join(original))?.is_dir() { + let orig_abs = link.parent().expect("dir for link").join(original); + let is_dir = match std::fs::metadata(orig_abs) { + Ok(m) => m.is_dir(), + Err(err) if err.kind() == io::ErrorKind::NotFound => false, + Err(err) => return Err(err), + }; + if is_dir { symlink_dir(original, link) } else { symlink_file(original, link) } } -#[cfg(not(windows))] /// Return true if `err` indicates that a file collision happened, i.e. a symlink couldn't be created as the `link` /// already exists as filesystem object. +#[cfg(not(windows))] pub fn is_collision_error(err: &std::io::Error) -> bool { // TODO: use ::IsDirectory as well when stabilized instead of raw_os_error(), and ::FileSystemLoop respectively err.kind() == AlreadyExists @@ -52,9 +64,9 @@ pub fn is_collision_error(err: &std::io::Error) -> bool { || err.raw_os_error() == Some(40) // no-follow on symlnk on ubuntu } -#[cfg(windows)] /// Return true if `err` indicates that a file collision happened, i.e. a symlink couldn't be created as the `link` /// already exists as filesystem object. +#[cfg(windows)] pub fn is_collision_error(err: &std::io::Error) -> bool { err.kind() == AlreadyExists || err.kind() == std::io::ErrorKind::PermissionDenied } diff --git a/gix-fs/tests/capabilities/mod.rs b/gix-fs/tests/capabilities/mod.rs index edff3b022f8..749bfb3a61f 100644 --- a/gix-fs/tests/capabilities/mod.rs +++ b/gix-fs/tests/capabilities/mod.rs @@ -2,7 +2,7 @@ fn probe() { let dir = tempfile::tempdir().unwrap(); std::fs::File::create(dir.path().join("config")).unwrap(); - gix_fs::Capabilities::probe(dir.path()); + let caps = gix_fs::Capabilities::probe(dir.path()); let entries: Vec<_> = std::fs::read_dir(dir.path()) .unwrap() @@ -15,4 +15,34 @@ fn probe() { 0, "there should be no left-over files after probing, found {entries:?}" ); + if cfg!(unix) { + assert!(caps.symlink, "Unix should always be able to create symlinks"); + assert!(caps.executable_bit, "Unix should always honor executable bits"); + } +} + +#[test] +fn parallel_probe() { + let dir = tempfile::tempdir().unwrap(); + std::fs::File::create(dir.path().join("config")).unwrap(); + let baseline = gix_fs::Capabilities::probe(dir.path()); + + let (tx, rx) = crossbeam_channel::unbounded::<()>(); + let threads: Vec<_> = (0..10) + .map(|_id| { + std::thread::spawn({ + let dir = dir.path().to_owned(); + let rx = rx.clone(); + move || { + for _ in rx {} + let actual = gix_fs::Capabilities::probe(&dir); + assert_eq!(actual, baseline); + } + }) + }) + .collect(); + drop((rx, tx)); + for thread in threads { + thread.join().expect("no panic"); + } } diff --git a/gix-fs/tests/stack/mod.rs b/gix-fs/tests/stack/mod.rs index 5e122cdb007..c79db87dbf9 100644 --- a/gix-fs/tests/stack/mod.rs +++ b/gix-fs/tests/stack/mod.rs @@ -1,3 +1,4 @@ +#![allow(clippy::join_absolute_paths)] use std::path::{Path, PathBuf}; use gix_fs::Stack; @@ -26,6 +27,287 @@ impl gix_fs::stack::Delegate for Record { } } +fn p(s: &str) -> &Path { + s.as_ref() +} + +/// Just to learn the specialities of `Path::join()`, which boils down to `Path::push(component)`. +#[test] +#[cfg(windows)] +fn path_join_handling() { + let absolute = p("/absolute"); + assert!( + absolute.is_relative(), + "on Windows, absolute Linux paths are considered relative (and relative to the current drive)" + ); + let bs_absolute = p("\\absolute"); + assert!( + absolute.is_relative(), + "on Windows, strange single-backslash paths are relative (and relative to the current drive)" + ); + assert_eq!( + p("relative").join(absolute), + absolute, + "relative + absolute = absolute - however, they kind of act like they are absolute in conjunction with relative base paths" + ); + assert_eq!( + p("relative").join(bs_absolute), + bs_absolute, + "relative + absolute = absolute - backslashes aren't special here, and it just acts like it's absolute" + ); + + assert_eq!( + p("c:").join("relative"), + p("c:relative"), + "drive + relative = strange joined result with missing backslash, but it's a valid path that works just like `c:\relative`" + ); + assert_eq!( + p("c:\\").join("relative"), + p("c:\\relative"), + "absolute + relative = joined result" + ); + + assert_eq!( + p("\\\\?\\base").join(absolute), + p("\\\\?\\base\\absolute"), + "absolute1 + unix-absolute2 = joined result with backslash" + ); + assert_eq!( + p("\\\\.\\base").join(absolute), + p("\\\\.\\base\\absolute"), + "absolute1 + absolute2 = joined result with backslash (device namespace)" + ); + assert_eq!( + p("\\\\?\\base").join(bs_absolute), + p("\\\\?\\base\\absolute"), + "absolute1 + absolute2 = joined result" + ); + assert_eq!( + p("\\\\.\\base").join(bs_absolute), + p("\\\\.\\base\\absolute"), + "absolute1 + absolute2 = joined result (device namespace)" + ); + + assert_eq!(p("/").join("C:"), p("C:"), "unix-absolute + win-drive = win-drive"); + assert_eq!( + p("d:/").join("C:"), + p("C:"), + "d-drive + c-drive = c-drive - interesting, as C: is supposed to be relative" + ); + assert_eq!( + p("d:\\").join("C:\\"), + p("C:\\"), + "d-drive-with-bs + c-drive-with-bs = c-drive-with-bs - nothing special happens with backslashes" + ); + assert_eq!( + p("c:\\").join("\\\\.\\"), + p("\\\\.\\"), + "c-drive-with-bs + device-namespace-unc = device-namespace-unc" + ); + assert_eq!( + p("/").join("C:/"), + p("C:\\"), + "unix-absolute + win-drive = win-drive, strangely enough it changed the trailing slash to backslash, so better not have trailing slashes" + ); + assert_eq!(p("/").join("C:\\"), p("C:\\"), "unix-absolute + win-drive = win-drive"); + assert_eq!( + p("\\\\.").join("C:"), + p("C:"), + "device-namespace-unc + win-drive-relative = win-drive-relative - c: was supposed to be relative, but it's not acting like it." + ); + assert_eq!(p("relative").join("C:"), p("C:"), "relative + win-drive = win-drive"); + + assert_eq!( + p("/").join("\\\\localhost"), + p("\\localhost"), + "unix-absolute + win-absolute-unc = win-absolute-unc" + ); + assert_eq!( + p("relative").join("\\\\localhost"), + p("\\\\localhost"), + "relative + win-absolute-unc = win-absolute-unc" + ); +} + +/// Just to learn the specialities of `Path::join()`, which boils down to `Path::push(component)`. +#[test] +#[cfg(not(windows))] +fn path_join_handling() { + assert_eq!( + p("relative").join("/absolute"), + p("/absolute"), + "relative + absolute = absolute" + ); + + assert_eq!( + p("/").join("relative"), + p("/relative"), + "absolute + relative = joined result" + ); + + assert_eq!( + p("/").join("/absolute"), + p("/absolute"), + "absolute1 + absolute2 = absolute2" + ); + + assert_eq!(p("/").join("C:"), p("/C:"), "absolute + win-drive = joined result"); + assert_eq!(p("/").join("C:/"), p("/C:/"), "absolute + win-absolute = joined result"); + assert_eq!( + p("/").join("C:\\"), + p("/C:\\"), + "absolute + win-absolute = joined result" + ); + assert_eq!( + p("relative").join("C:"), + p("relative/C:"), + "relative + win-drive = joined result" + ); + + assert_eq!( + p("/").join("\\localhost"), + p("/\\localhost"), + "absolute + win-absolute-unc = joined result" + ); + assert_eq!( + p("relative").join("\\localhost"), + p("relative/\\localhost"), + "relative + win-absolute-unc = joined result" + ); +} + +#[test] +fn empty_paths_are_noop_if_no_path_was_pushed_before() { + let root = PathBuf::from("."); + let mut s = Stack::new(root.clone()); + + let mut r = Record::default(); + s.make_relative_path_current("".as_ref(), &mut r).unwrap(); + assert_eq!( + s.current_relative().to_string_lossy(), + "", + "it's fine to push an empty path to get a value for the stack root, once" + ); +} + +#[test] +fn relative_components_are_invalid() { + let root = PathBuf::from("."); + let mut s = Stack::new(root.clone()); + + let mut r = Record::default(); + let err = s.make_relative_path_current("a/..".as_ref(), &mut r).unwrap_err(); + assert_eq!( + err.to_string(), + format!( + "Input path {input:?} contains relative or absolute components", + input = "a/.." + ) + ); + + s.make_relative_path_current("a/./b".as_ref(), &mut r) + .expect("dot is ignored"); + assert_eq!( + r, + Record { + push_dir: 2, + dirs: vec![".".into(), "./a".into()], + push: 2, + }, + "The `a` directory is pushed, and the leaf, for a total of 2 pushes" + ); + assert_eq!( + s.current().to_string_lossy(), + if cfg!(windows) { ".\\a\\b" } else { "./a/b" }, + "dot is silently ignored" + ); + s.make_relative_path_current("a//b/".as_ref(), &mut r) + .expect("multiple-slashes are ignored"); + assert_eq!( + r, + Record { + push_dir: 2, + dirs: vec![".".into(), "./a".into()], + push: 2, + }, + "nothing changed" + ); +} + +#[test] +fn absolute_paths_are_invalid() -> crate::Result { + let root = PathBuf::from("."); + let mut s = Stack::new(root.clone()); + + let mut r = Record::default(); + let err = s.make_relative_path_current("/".as_ref(), &mut r).unwrap_err(); + assert_eq!( + err.to_string(), + "Input path \"/\" contains relative or absolute components", + "a leading slash is always considered absolute" + ); + s.make_relative_path_current("a/".as_ref(), &mut r)?; + assert_eq!( + s.current(), + p("./a/"), + "trailing slashes aren't a problem at this stage, as they cannot cause a 'breakout'" + ); + s.make_relative_path_current("b\\".as_ref(), &mut r)?; + assert_eq!( + s.current(), + p("./b\\"), + "trailing backslashes are fine both on Windows and Unix - on Unix it's part fo the filename" + ); + + #[cfg(windows)] + { + let err = s.make_relative_path_current("\\".as_ref(), &mut r).unwrap_err(); + assert_eq!( + err.to_string(), + "Input path \"\\\" contains relative or absolute components", + "on Windows, backslashes are considered absolute and replace the base if it is relative, \ + hence they are forbidden." + ); + + let err = s.make_relative_path_current("c:".as_ref(), &mut r).unwrap_err(); + assert_eq!( + err.to_string(), + "Input path \"c:\" contains relative or absolute components", + "on Windows, drive-letters without trailing backslash or slash are also absolute (even though they ought to be relative)" + ); + let err = s.make_relative_path_current("c:\\".as_ref(), &mut r).unwrap_err(); + assert_eq!( + err.to_string(), + "Input path \"c:\\\" contains relative or absolute components", + "on Windows, drive-letters are absolute, which is expected" + ); + + s.make_relative_path_current("֍:".as_ref(), &mut r)?; + assert_eq!( + s.current().to_string_lossy(), + ".\\֍:", + "on Windows, almost any unicode character will do as virtual drive-letter actually with `subst`, \ + but we just turn it into a presumably invalid path which is fine, i.e. we get a joined path" + ); + let err = s + .make_relative_path_current(r#"\\localhost\hello"#.as_ref(), &mut r) + .unwrap_err(); + assert_eq!( + err.to_string(), + r#"Input path "\\localhost\hello" contains relative or absolute components"#, + "there is UNC paths as well" + ); + + let err = s.make_relative_path_current(r#"\\?\C:"#.as_ref(), &mut r).unwrap_err(); + assert_eq!( + err.to_string(), + r#"Input path "\\?\C:" contains relative or absolute components"#, + "there is UNC paths as well, sometimes they look different" + ); + } + Ok(()) +} + #[test] fn delegate_calls_are_consistent() -> crate::Result { let root = PathBuf::from("."); @@ -43,7 +325,8 @@ fn delegate_calls_are_consistent() -> crate::Result { push_dir: 2, dirs: dirs.clone(), push: 2, - } + }, + "it pushes the root-directory first, then the intermediate one" ); s.make_relative_path_current("a/b2".as_ref(), &mut r)?; @@ -53,7 +336,8 @@ fn delegate_calls_are_consistent() -> crate::Result { push_dir: 2, dirs: dirs.clone(), push: 3, - } + }, + "dirs remain the same as b2 is a leaf/file, hence the new `push`" ); s.make_relative_path_current("c/d/e".as_ref(), &mut r)?; @@ -65,7 +349,8 @@ fn delegate_calls_are_consistent() -> crate::Result { push_dir: 4, dirs: dirs.clone(), push: 6, - } + }, + "each directory is pushed individually, after popping 'a' which isn't included anymore" ); dirs.push(root.join("c").join("d").join("x")); @@ -76,10 +361,11 @@ fn delegate_calls_are_consistent() -> crate::Result { push_dir: 5, dirs: dirs.clone(), push: 8, - } + }, + "a new path component is added, hence `push_dir + 1`, but two components are added in total" ); - dirs.drain(dirs.len() - 3..).count(); + dirs.drain(1..).count(); s.make_relative_path_current("f".as_ref(), &mut r)?; assert_eq!(s.current_relative(), Path::new("f")); assert_eq!( @@ -88,7 +374,8 @@ fn delegate_calls_are_consistent() -> crate::Result { push_dir: 5, dirs: dirs.clone(), push: 9, - } + }, + "Now we only keep the root, as `f` is a leaf, hence `push + 1`" ); dirs.push(root.join("x")); @@ -99,7 +386,8 @@ fn delegate_calls_are_consistent() -> crate::Result { push_dir: 6, dirs: dirs.clone(), push: 11, - } + }, + "a new directory is pushed, or two new components total, hence `push + 2`" ); dirs.push(root.join("x").join("z")); @@ -110,7 +398,8 @@ fn delegate_calls_are_consistent() -> crate::Result { push_dir: 7, dirs: dirs.clone(), push: 12, - } + }, + "and another sub-directory is added" ); dirs.push(root.join("x").join("z").join("a")); @@ -122,10 +411,11 @@ fn delegate_calls_are_consistent() -> crate::Result { push_dir: 9, dirs: dirs.clone(), push: 14, - } + }, + "and more subdirectories, two at once this time." ); - dirs.drain(dirs.len() - 2..).count(); + dirs.drain(1 /*root*/ + 1 /*x*/ + 1 /*x/z*/ ..).count(); s.make_relative_path_current("x/z".as_ref(), &mut r)?; assert_eq!( r, @@ -133,7 +423,8 @@ fn delegate_calls_are_consistent() -> crate::Result { push_dir: 9, dirs: dirs.clone(), push: 14, - } + }, + "this only pops components, and as x/z/a/ was previously a directory, x/z is still a directory" ); assert_eq!( dirs.last(), @@ -141,16 +432,104 @@ fn delegate_calls_are_consistent() -> crate::Result { "the stack is state so keeps thinking it's a directory which is consistent. Git does it differently though." ); - s.make_relative_path_current("".as_ref(), &mut r)?; + let err = s.make_relative_path_current("".as_ref(), &mut r).unwrap_err(); + assert_eq!( + err.to_string(), + "empty inputs are not allowed", + "this is to protect us from double-counting the root path next time a component is pushed, \ + and besides that really shouldn't happen" + ); + + s.make_relative_path_current("leaf".as_ref(), &mut r)?; + dirs.drain(1..).count(); assert_eq!( r, Record { push_dir: 9, - dirs: vec![".".into()], - push: 14, + dirs: dirs.clone(), + push: 15, }, - "empty-paths reset the tree effectively" + "reset as much as possible, with just a leaf-component and the root directory" ); + s.make_relative_path_current("a//b".as_ref(), &mut r)?; + dirs.push(root.join("a")); + assert_eq!( + r, + Record { + push_dir: 10, + dirs: dirs.clone(), + push: 17, + }, + "double-slashes are automatically cleaned, even though they shouldn't happen, it's not forbidden" + ); + + #[cfg(not(windows))] + { + s.make_relative_path_current("\\/b".as_ref(), &mut r)?; + dirs.pop(); + dirs.push(root.join("\\")); + assert_eq!( + r, + Record { + push_dir: 11, + dirs: dirs.clone(), + push: 19, + }, + "a backslash is a normal character outside of Windows, so it's fine to have it as component" + ); + + s.make_relative_path_current("\\".as_ref(), &mut r)?; + assert_eq!( + r, + Record { + push_dir: 11, + dirs: dirs.clone(), + push: 19, + }, + ); + assert_eq!( + s.current().to_string_lossy(), + "./\\", + "a backslash can also be a valid leaf component - here we only popped the 'b', leaving the \\ 'directory'" + ); + + s.make_relative_path_current("\\\\".as_ref(), &mut r)?; + dirs.pop(); + assert_eq!( + r, + Record { + push_dir: 11, + dirs: dirs.clone(), + push: 20, + }, + ); + assert_eq!( + s.current().to_string_lossy(), + "./\\\\", + "the backslash can also be an ordinary leaf, without the need for it to be a directory" + ); + } + + #[cfg(windows)] + { + s.make_relative_path_current("c\\/d".as_ref(), &mut r)?; + dirs.pop(); + dirs.push(root.join("c")); + assert_eq!( + r, + Record { + push_dir: 11, + dirs: dirs.clone(), + push: 19, + }, + ); + assert_eq!( + s.current().to_string_lossy(), + ".\\c\\d", + "the backslash is a path-separator, and so is the `/`, which is turned into backslash" + ); + } + Ok(()) } diff --git a/gix-fsck/CHANGELOG.md b/gix-fsck/CHANGELOG.md index e99254fba5e..5e2afeb111c 100644 --- a/gix-fsck/CHANGELOG.md +++ b/gix-fsck/CHANGELOG.md @@ -5,24 +5,52 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## 0.4.0 (2024-04-13) + +A maintenance release without user-facing changes. + +### Commit Statistics + + + + - 6 commits contributed to the release over the course of 29 calendar days. + - 47 days passed between releases. + - 0 commits were understood as [conventional](https://www.conventionalcommits.org). + - 0 issues like '(#ID)' were seen in commit messages + +### Commit Details + + + +
view details + + * **Uncategorized** + - Prepare changelogs prior to release ([`5755271`](https://github.com/Byron/gitoxide/commit/57552717f46f96c35ba4ddc0a64434354ef845e9)) + - Merge pull request #1332 from cesfahani/fix_fsck_tree_traversal ([`4bb8ef5`](https://github.com/Byron/gitoxide/commit/4bb8ef5fd05133b87eb1e79a0ac6e45eb66940a2)) + - Fix git-fsck to handle non-root trees ([`5fdb440`](https://github.com/Byron/gitoxide/commit/5fdb440de2837f00a313b2747fc1b4773b70c045)) + - Release gix-actor v0.31.1, gix-object v0.42.1, gix-index v0.31.1, gix-pathspec v0.7.2, gix-dir v0.3.0, gix-status v0.8.0, gix v0.61.0, safety bump 2 crates ([`155cc45`](https://github.com/Byron/gitoxide/commit/155cc45730b259e662d7c4be42a469a3af3750e1)) + - Release gix-date v0.8.5, gix-hash v0.14.2, gix-trace v0.1.8, gix-utils v0.1.11, gix-features v0.38.1, gix-actor v0.31.0, gix-validate v0.8.4, gix-object v0.42.0, gix-path v0.10.7, gix-glob v0.16.2, gix-quote v0.4.12, gix-attributes v0.22.2, gix-command v0.3.6, gix-filter v0.11.0, gix-fs v0.10.1, gix-chunk v0.4.8, gix-commitgraph v0.24.2, gix-hashtable v0.5.2, gix-revwalk v0.13.0, gix-traverse v0.38.0, gix-worktree-stream v0.11.0, gix-archive v0.11.0, gix-config-value v0.14.6, gix-tempfile v13.1.1, gix-lock v13.1.1, gix-ref v0.43.0, gix-sec v0.10.6, gix-config v0.36.0, gix-prompt v0.8.4, gix-url v0.27.2, gix-credentials v0.24.2, gix-ignore v0.11.2, gix-bitmap v0.2.11, gix-index v0.31.0, gix-worktree v0.32.0, gix-diff v0.42.0, gix-discover v0.31.0, gix-pathspec v0.7.1, gix-dir v0.2.0, gix-macros v0.1.4, gix-mailmap v0.23.0, gix-negotiate v0.13.0, gix-pack v0.49.0, gix-odb v0.59.0, gix-packetline v0.17.4, gix-transport v0.41.2, gix-protocol v0.44.2, gix-revision v0.27.0, gix-refspec v0.23.0, gix-status v0.7.0, gix-submodule v0.10.0, gix-worktree-state v0.9.0, gix v0.60.0, safety bump 26 crates ([`b050327`](https://github.com/Byron/gitoxide/commit/b050327e76f234b19be921b78b7b28e034319fdb)) + - Prepare changelogs prior to release ([`52c3bbd`](https://github.com/Byron/gitoxide/commit/52c3bbd36b9e94a0f3a78b4ada84d0c08eba27f6)) +
+ ## 0.3.0 (2024-02-25) ### Chore - - change `rust-version` manifest field back to 1.65. - They didn't actually need to be higher to work, and changing them - unecessarily can break downstream CI. - - Let's keep this value as low as possible, and only increase it when - more recent features are actually used. +- change `rust-version` manifest field back to 1.65. + They didn't actually need to be higher to work, and changing them + unecessarily can break downstream CI. + + Let's keep this value as low as possible, and only increase it when + more recent features are actually used. ### Commit Statistics - - 5 commits contributed to the release over the course of 57 calendar days. + - 6 commits contributed to the release over the course of 57 calendar days. - 58 days passed between releases. - 1 commit was understood as [conventional](https://www.conventionalcommits.org). - 0 issues like '(#ID)' were seen in commit messages @@ -34,6 +62,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
view details * **Uncategorized** + - Release gix-date v0.8.4, gix-utils v0.1.10, gix-actor v0.30.1, gix-object v0.41.1, gix-path v0.10.6, gix-glob v0.16.1, gix-quote v0.4.11, gix-attributes v0.22.1, gix-command v0.3.5, gix-filter v0.10.0, gix-commitgraph v0.24.1, gix-worktree-stream v0.10.0, gix-archive v0.10.0, gix-config-value v0.14.5, gix-ref v0.42.0, gix-sec v0.10.5, gix-config v0.35.0, gix-prompt v0.8.3, gix-url v0.27.1, gix-credentials v0.24.1, gix-ignore v0.11.1, gix-index v0.30.0, gix-worktree v0.31.0, gix-diff v0.41.0, gix-discover v0.30.0, gix-pathspec v0.7.0, gix-dir v0.1.0, gix-pack v0.48.0, gix-odb v0.58.0, gix-transport v0.41.1, gix-protocol v0.44.1, gix-revision v0.26.1, gix-refspec v0.22.1, gix-status v0.6.0, gix-submodule v0.9.0, gix-worktree-state v0.8.0, gix v0.59.0, gix-fsck v0.3.0, gitoxide-core v0.36.0, gitoxide v0.34.0, safety bump 10 crates ([`45b4470`](https://github.com/Byron/gitoxide/commit/45b447045bc826f252129c300c531acde2652c64)) - Prepare changelogs prior to release ([`f2e111f`](https://github.com/Byron/gitoxide/commit/f2e111f768fc1bc6182355261c20b63610cffec7)) - Release gix-utils v0.1.9, gix-features v0.38.0, gix-actor v0.30.0, gix-object v0.41.0, gix-path v0.10.4, gix-glob v0.16.0, gix-attributes v0.22.0, gix-command v0.3.3, gix-packetline-blocking v0.17.3, gix-filter v0.9.0, gix-fs v0.10.0, gix-commitgraph v0.24.0, gix-revwalk v0.12.0, gix-traverse v0.37.0, gix-worktree-stream v0.9.0, gix-archive v0.9.0, gix-config-value v0.14.4, gix-tempfile v13.0.0, gix-lock v13.0.0, gix-ref v0.41.0, gix-sec v0.10.4, gix-config v0.34.0, gix-url v0.27.0, gix-credentials v0.24.0, gix-ignore v0.11.0, gix-index v0.29.0, gix-worktree v0.30.0, gix-diff v0.40.0, gix-discover v0.29.0, gix-mailmap v0.22.0, gix-negotiate v0.12.0, gix-pack v0.47.0, gix-odb v0.57.0, gix-pathspec v0.6.0, gix-packetline v0.17.3, gix-transport v0.41.0, gix-protocol v0.44.0, gix-revision v0.26.0, gix-refspec v0.22.0, gix-status v0.5.0, gix-submodule v0.8.0, gix-worktree-state v0.7.0, gix v0.58.0, safety bump 39 crates ([`eb6aa8f`](https://github.com/Byron/gitoxide/commit/eb6aa8f502314f886fc4ea3d52ab220763968208)) - Release gix-date v0.8.3, gix-hash v0.14.1, gix-trace v0.1.6, gix-features v0.37.1, gix-actor v0.29.1, gix-validate v0.8.3, gix-object v0.40.1, gix-path v0.10.3, gix-glob v0.15.1, gix-quote v0.4.10, gix-attributes v0.21.1, gix-command v0.3.2, gix-packetline-blocking v0.17.2, gix-utils v0.1.8, gix-filter v0.8.1, gix-fs v0.9.1, gix-chunk v0.4.7, gix-commitgraph v0.23.1, gix-hashtable v0.5.1, gix-revwalk v0.11.1, gix-traverse v0.36.1, gix-worktree-stream v0.8.1, gix-archive v0.8.1, gix-config-value v0.14.3, gix-tempfile v12.0.1, gix-lock v12.0.1, gix-ref v0.40.1, gix-sec v0.10.3, gix-config v0.33.1, gix-prompt v0.8.2, gix-url v0.26.1, gix-credentials v0.23.1, gix-ignore v0.10.1, gix-bitmap v0.2.10, gix-index v0.28.1, gix-worktree v0.29.1, gix-diff v0.39.1, gix-discover v0.28.1, gix-macros v0.1.3, gix-mailmap v0.21.1, gix-negotiate v0.11.1, gix-pack v0.46.1, gix-odb v0.56.1, gix-pathspec v0.5.1, gix-packetline v0.17.2, gix-transport v0.40.1, gix-protocol v0.43.1, gix-revision v0.25.1, gix-refspec v0.21.1, gix-status v0.4.1, gix-submodule v0.7.1, gix-worktree-state v0.6.1, gix v0.57.1 ([`972241f`](https://github.com/Byron/gitoxide/commit/972241f1904944e8b6e84c6aa1649a49be7a85c3)) @@ -47,8 +76,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Chore - - upgrade MSRV to v1.70 - Our MSRV follows the one of `helix`, which in turn follows Firefox. +- upgrade MSRV to v1.70 + Our MSRV follows the one of `helix`, which in turn follows Firefox. ### Commit Statistics diff --git a/gix-fsck/Cargo.toml b/gix-fsck/Cargo.toml index 93d827b8f42..8725bea95a4 100644 --- a/gix-fsck/Cargo.toml +++ b/gix-fsck/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "gix-fsck" -version = "0.3.0" +version = "0.4.0" repository = "https://github.com/Byron/gitoxide" authors = ["Cameron Esfahani ", "Sebastian Thiel "] license = "MIT OR Apache-2.0" @@ -13,9 +13,9 @@ rust-version = "1.65" doctest = false [dependencies] -gix-hash = { version = "^0.14.1", path = "../gix-hash" } -gix-hashtable = { version = "^0.5.1", path = "../gix-hashtable" } -gix-object = { version = "^0.41.1", path = "../gix-object" } +gix-hash = { version = "^0.14.2", path = "../gix-hash" } +gix-hashtable = { version = "^0.5.2", path = "../gix-hashtable" } +gix-object = { version = "^0.42.1", path = "../gix-object" } [dev-dependencies] gix-odb = { path = "../gix-odb" } diff --git a/gix-fsck/src/lib.rs b/gix-fsck/src/lib.rs index 99bb8986c06..6429b2307a0 100644 --- a/gix-fsck/src/lib.rs +++ b/gix-fsck/src/lib.rs @@ -80,9 +80,7 @@ where match entry_ref.mode.kind() { EntryKind::Tree => { let tree_id = entry_ref.oid.to_owned(); - if self.seen.insert(tree_id) { - tree_ids.push_back(tree_id); - } + tree_ids.push_back(tree_id); } EntryKind::Blob | EntryKind::BlobExecutable | EntryKind::Link => { let blob_id = entry_ref.oid.to_owned(); @@ -91,7 +89,7 @@ where } } EntryKind::Commit => { - // Skip submodules as it's not in this repository! + // Skip submodules as they wouldn't be in this repository! } } } diff --git a/gix-fsck/tests/connectivity/mod.rs b/gix-fsck/tests/connectivity/mod.rs index 120ae699f6b..3468f0bc290 100644 --- a/gix-fsck/tests/connectivity/mod.rs +++ b/gix-fsck/tests/connectivity/mod.rs @@ -42,8 +42,8 @@ fn hex_to_objects<'a>(hex_ids: impl IntoIterator, kind: Kind) -> fn all_commits() -> &'static [ObjectId] { static ALL_COMMITS: Lazy> = Lazy::new(|| { hex_to_ids([ - "5d18db2e2aabadf7b914435ef34f2faf8b4546dd", - "3a3dfaa55a515f3fb3a25751107bbb523af6a1b0", + "ebed23648b19484cb1f340c4ee04dda08479188a", + "8ff6d0f8891c3cb22827be142cc64606121d47b3", "734c926856a328d1168ffd7088532e0d1ad19bbe", ]) }); @@ -59,7 +59,13 @@ fn no_missing() { #[test] fn missing_blobs() { // The "blobless" repo is cloned with `--filter=blob:none`, and is missing one blob - let expected = hex_to_objects(["c18147dc648481eeb65dc5e66628429a64843327"], Kind::Blob); + let expected = hex_to_objects( + [ + "4cdeaab5b01f9a9fbbb2fb6c08404cf12b7bdab1", + "c18147dc648481eeb65dc5e66628429a64843327", + ], + Kind::Blob, + ); assert_eq!(check_missing("blobless", all_commits()), expected); } @@ -69,7 +75,7 @@ fn missing_trees() { // NOTE: This repo is also missing a blob, but we have no way of knowing that, as the tree referencing it is missing let expected = hex_to_objects( [ - "9561cfbae43c5e2accdfcd423378588dd10d827f", + "20317ffa7614f49b2702a057bf2833918ea9fd24", "fc264b3b6875a46e9031483aeb9994a1b897ffd3", ], Kind::Tree, diff --git a/gix-fsck/tests/fixtures/make_test_repos.sh b/gix-fsck/tests/fixtures/make_test_repos.sh index 19491a0c551..7b37084d01b 100755 --- a/gix-fsck/tests/fixtures/make_test_repos.sh +++ b/gix-fsck/tests/fixtures/make_test_repos.sh @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash set -x set -euo pipefail @@ -18,9 +18,11 @@ git init base git add -A git commit -m "commit 1" echo "blob-2" > blob-2 + mkdir tree-1 + echo "blob-3" > tree-1/blob-3 git add -A git commit -m "commit 2" - git rm blob-1 + git rm blob-1 tree-1/blob-3 git add -A git commit -m "commit 3" ) diff --git a/gix-glob/CHANGELOG.md b/gix-glob/CHANGELOG.md index 7f35e3116a5..21a09e1dc1a 100644 --- a/gix-glob/CHANGELOG.md +++ b/gix-glob/CHANGELOG.md @@ -5,6 +5,59 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## 0.16.3 (2024-05-28) + +### Bug Fixes + + - ignore `.gitignore` files that are directories + This happens in real-life, and Git itself doesn't budge. + +### Commit Statistics + + + + - 2 commits contributed to the release. + - 74 days passed between releases. + - 1 commit was understood as [conventional](https://www.conventionalcommits.org). + - 0 issues like '(#ID)' were seen in commit messages + +### Commit Details + + + +
view details + + * **Uncategorized** + - Merge branch 'dir-as-ignore' ([`31d2f0a`](https://github.com/Byron/gitoxide/commit/31d2f0a742257d7031df114f0c92197a0781921e)) + - Ignore `.gitignore` files that are directories ([`31ade4a`](https://github.com/Byron/gitoxide/commit/31ade4a66141a4ce465ea7edfb9ec56636e77da4)) +
+ +## 0.16.2 (2024-03-14) + +A maintenance release without user-facing changes. + +### Commit Statistics + + + + - 4 commits contributed to the release over the course of 4 calendar days. + - 18 days passed between releases. + - 0 commits were understood as [conventional](https://www.conventionalcommits.org). + - 0 issues like '(#ID)' were seen in commit messages + +### Commit Details + + + +
view details + + * **Uncategorized** + - Release gix-date v0.8.5, gix-hash v0.14.2, gix-trace v0.1.8, gix-utils v0.1.11, gix-features v0.38.1, gix-actor v0.31.0, gix-validate v0.8.4, gix-object v0.42.0, gix-path v0.10.7, gix-glob v0.16.2, gix-quote v0.4.12, gix-attributes v0.22.2, gix-command v0.3.6, gix-filter v0.11.0, gix-fs v0.10.1, gix-chunk v0.4.8, gix-commitgraph v0.24.2, gix-hashtable v0.5.2, gix-revwalk v0.13.0, gix-traverse v0.38.0, gix-worktree-stream v0.11.0, gix-archive v0.11.0, gix-config-value v0.14.6, gix-tempfile v13.1.1, gix-lock v13.1.1, gix-ref v0.43.0, gix-sec v0.10.6, gix-config v0.36.0, gix-prompt v0.8.4, gix-url v0.27.2, gix-credentials v0.24.2, gix-ignore v0.11.2, gix-bitmap v0.2.11, gix-index v0.31.0, gix-worktree v0.32.0, gix-diff v0.42.0, gix-discover v0.31.0, gix-pathspec v0.7.1, gix-dir v0.2.0, gix-macros v0.1.4, gix-mailmap v0.23.0, gix-negotiate v0.13.0, gix-pack v0.49.0, gix-odb v0.59.0, gix-packetline v0.17.4, gix-transport v0.41.2, gix-protocol v0.44.2, gix-revision v0.27.0, gix-refspec v0.23.0, gix-status v0.7.0, gix-submodule v0.10.0, gix-worktree-state v0.9.0, gix v0.60.0, safety bump 26 crates ([`b050327`](https://github.com/Byron/gitoxide/commit/b050327e76f234b19be921b78b7b28e034319fdb)) + - Prepare changelogs prior to release ([`52c3bbd`](https://github.com/Byron/gitoxide/commit/52c3bbd36b9e94a0f3a78b4ada84d0c08eba27f6)) + - Merge branch 'status' ([`3e5c974`](https://github.com/Byron/gitoxide/commit/3e5c974dd62ac134711c6c2f5a5490187a6ea55e)) + - Fix lints for nightly, and clippy ([`f8ce3d0`](https://github.com/Byron/gitoxide/commit/f8ce3d0721b6a53713a9392f2451874f520bc44c)) +
+ ## 0.16.1 (2024-02-25) A maintenance release without user-facing changes. @@ -13,7 +66,7 @@ A maintenance release without user-facing changes. - - 3 commits contributed to the release over the course of 30 calendar days. + - 4 commits contributed to the release over the course of 30 calendar days. - 36 days passed between releases. - 0 commits were understood as [conventional](https://www.conventionalcommits.org). - 0 issues like '(#ID)' were seen in commit messages @@ -31,6 +84,7 @@ A maintenance release without user-facing changes.
view details * **Uncategorized** + - Release gix-date v0.8.4, gix-utils v0.1.10, gix-actor v0.30.1, gix-object v0.41.1, gix-path v0.10.6, gix-glob v0.16.1, gix-quote v0.4.11, gix-attributes v0.22.1, gix-command v0.3.5, gix-filter v0.10.0, gix-commitgraph v0.24.1, gix-worktree-stream v0.10.0, gix-archive v0.10.0, gix-config-value v0.14.5, gix-ref v0.42.0, gix-sec v0.10.5, gix-config v0.35.0, gix-prompt v0.8.3, gix-url v0.27.1, gix-credentials v0.24.1, gix-ignore v0.11.1, gix-index v0.30.0, gix-worktree v0.31.0, gix-diff v0.41.0, gix-discover v0.30.0, gix-pathspec v0.7.0, gix-dir v0.1.0, gix-pack v0.48.0, gix-odb v0.58.0, gix-transport v0.41.1, gix-protocol v0.44.1, gix-revision v0.26.1, gix-refspec v0.22.1, gix-status v0.6.0, gix-submodule v0.9.0, gix-worktree-state v0.8.0, gix v0.59.0, gix-fsck v0.3.0, gitoxide-core v0.36.0, gitoxide v0.34.0, safety bump 10 crates ([`45b4470`](https://github.com/Byron/gitoxide/commit/45b447045bc826f252129c300c531acde2652c64)) - Prepare changelogs prior to release ([`f2e111f`](https://github.com/Byron/gitoxide/commit/f2e111f768fc1bc6182355261c20b63610cffec7)) - Thanks clippy ([`13d5602`](https://github.com/Byron/gitoxide/commit/13d5602faa58aa6f520ebc6003ed54bc9c844f2b)) - Release gix-path v0.10.5 ([`b8cba96`](https://github.com/Byron/gitoxide/commit/b8cba96ce57f8b6b0067d6a8cf3e37eaf280a238)) diff --git a/gix-glob/Cargo.toml b/gix-glob/Cargo.toml index c963ca8af6d..05f3921aeac 100644 --- a/gix-glob/Cargo.toml +++ b/gix-glob/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "gix-glob" -version = "0.16.1" +version = "0.16.3" repository = "https://github.com/Byron/gitoxide" license = "MIT OR Apache-2.0" description = "A crate of the gitoxide project dealing with pattern matching" @@ -17,8 +17,8 @@ doctest = false serde= ["dep:serde", "bstr/serde", "bitflags/serde"] [dependencies] -gix-path = { version = "^0.10.6", path = "../gix-path" } -gix-features = { version = "^0.38.0", path = "../gix-features" } +gix-path = { version = "^0.10.7", path = "../gix-path" } +gix-features = { version = "^0.38.1", path = "../gix-features" } bstr = { version = "1.3.0", default-features = false, features = ["std"]} bitflags = "2" serde = { version = "1.0.114", optional = true, default-features = false, features = ["derive"]} diff --git a/gix-glob/src/lib.rs b/gix-glob/src/lib.rs index ee8599fc8ec..4ddf98699de 100644 --- a/gix-glob/src/lib.rs +++ b/gix-glob/src/lib.rs @@ -25,11 +25,13 @@ pub struct Pattern { } /// +#[allow(clippy::empty_docs)] pub mod pattern; pub mod search; /// +#[allow(clippy::empty_docs)] pub mod wildmatch; pub use wildmatch::function::wildmatch; diff --git a/gix-glob/src/search/mod.rs b/gix-glob/src/search/mod.rs index b6fb2a49025..da58e1a1fcb 100644 --- a/gix-glob/src/search/mod.rs +++ b/gix-glob/src/search/mod.rs @@ -5,6 +5,7 @@ use std::path::{Path, PathBuf}; /// +#[allow(clippy::empty_docs)] pub mod pattern; /// A trait to convert bytes into patterns and their associated value. diff --git a/gix-glob/src/search/pattern.rs b/gix-glob/src/search/pattern.rs index 828e59df3f7..54981651ecb 100644 --- a/gix-glob/src/search/pattern.rs +++ b/gix-glob/src/search/pattern.rs @@ -48,16 +48,29 @@ fn read_in_full_ignore_missing(path: &Path, follow_symlinks: bool, buf: &mut Vec }; Ok(match file { Ok(mut file) => { - file.read_to_end(buf)?; - true + if let Err(err) = file.read_to_end(buf) { + if io_err_is_dir(&err) { + false + } else { + return Err(err); + } + } else { + true + } } - Err(err) if err.kind() == std::io::ErrorKind::NotFound || - // TODO: use the enum variant NotADirectory for this once stabilized - err.raw_os_error() == Some(20) /* Not a directory */ => false, + Err(err) if err.kind() == std::io::ErrorKind::NotFound || io_err_is_dir(&err) => false, Err(err) => return Err(err), }) } +fn io_err_is_dir(err: &std::io::Error) -> bool { + // TODO: use the enum variant NotADirectory for this once stabilized + let raw = err.raw_os_error(); + raw == Some(if cfg!(windows) { 5 } else { 21 }) /* Not a directory */ + /* Also that, but under different circumstances */ + || raw == Some(20) +} + /// Instantiation impl List where diff --git a/gix-glob/tests/fixtures/generated-archives/make_baseline.tar.xz b/gix-glob/tests/fixtures/generated-archives/make_baseline.tar.xz index 5fc9dfdf1d5..f7c19284c7e 100644 Binary files a/gix-glob/tests/fixtures/generated-archives/make_baseline.tar.xz and b/gix-glob/tests/fixtures/generated-archives/make_baseline.tar.xz differ diff --git a/gix-glob/tests/fixtures/make_baseline.sh b/gix-glob/tests/fixtures/make_baseline.sh index 5787ff64ce1..02727edbf60 100755 --- a/gix-glob/tests/fixtures/make_baseline.sh +++ b/gix-glob/tests/fixtures/make_baseline.sh @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash set -eu -o pipefail git init -q diff --git a/gix-glob/tests/search/pattern.rs b/gix-glob/tests/search/pattern.rs index 6b62ee3d286..cfdb410a67f 100644 --- a/gix-glob/tests/search/pattern.rs +++ b/gix-glob/tests/search/pattern.rs @@ -85,7 +85,7 @@ mod list { } #[test] - fn from_file() { + fn from_file_that_does_not_exist() { let mut buf = Vec::new(); for path in [ Path::new(".").join("non-existing-dir").join("pattern-file"), @@ -95,4 +95,16 @@ mod list { assert!(list.is_none(), "the file does not exist"); } } + + #[test] + fn from_file_that_is_a_directory() -> gix_testtools::Result<()> { + let tmp = gix_testtools::tempfile::TempDir::new()?; + let dir_path = tmp.path().join(".gitignore"); + std::fs::create_dir(&dir_path)?; + let mut buf = Vec::new(); + let list = List::::from_file(dir_path, None, false, &mut buf).expect("no io error"); + assert!(list.is_none(), "directories are ignored just like Git does it"); + + Ok(()) + } } diff --git a/gix-hash/CHANGELOG.md b/gix-hash/CHANGELOG.md index 4ef66fd73ed..cedce514408 100644 --- a/gix-hash/CHANGELOG.md +++ b/gix-hash/CHANGELOG.md @@ -5,22 +5,49 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## 0.14.2 (2024-03-14) + +A maintenance release without user-facing changes. + +### Commit Statistics + + + + - 3 commits contributed to the release over the course of 4 calendar days. + - 75 days passed between releases. + - 0 commits were understood as [conventional](https://www.conventionalcommits.org). + - 0 issues like '(#ID)' were seen in commit messages + +### Commit Details + + + +
view details + + * **Uncategorized** + - Prepare changelogs prior to release ([`52c3bbd`](https://github.com/Byron/gitoxide/commit/52c3bbd36b9e94a0f3a78b4ada84d0c08eba27f6)) + - Merge branch 'status' ([`3e5c974`](https://github.com/Byron/gitoxide/commit/3e5c974dd62ac134711c6c2f5a5490187a6ea55e)) + - Fix lints for nightly, and clippy ([`f8ce3d0`](https://github.com/Byron/gitoxide/commit/f8ce3d0721b6a53713a9392f2451874f520bc44c)) +
+ ## 0.14.1 (2023-12-30) + + ### Chore - - change `rust-version` manifest field back to 1.65. - They didn't actually need to be higher to work, and changing them - unecessarily can break downstream CI. - - Let's keep this value as low as possible, and only increase it when - more recent features are actually used. +- change `rust-version` manifest field back to 1.65. + They didn't actually need to be higher to work, and changing them + unecessarily can break downstream CI. + + Let's keep this value as low as possible, and only increase it when + more recent features are actually used. ### Commit Statistics - - 2 commits contributed to the release. + - 3 commits contributed to the release. - 1 commit was understood as [conventional](https://www.conventionalcommits.org). - 0 issues like '(#ID)' were seen in commit messages @@ -31,6 +58,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
view details * **Uncategorized** + - Release gix-date v0.8.3, gix-hash v0.14.1, gix-trace v0.1.6, gix-features v0.37.1, gix-actor v0.29.1, gix-validate v0.8.3, gix-object v0.40.1, gix-path v0.10.3, gix-glob v0.15.1, gix-quote v0.4.10, gix-attributes v0.21.1, gix-command v0.3.2, gix-packetline-blocking v0.17.2, gix-utils v0.1.8, gix-filter v0.8.1, gix-fs v0.9.1, gix-chunk v0.4.7, gix-commitgraph v0.23.1, gix-hashtable v0.5.1, gix-revwalk v0.11.1, gix-traverse v0.36.1, gix-worktree-stream v0.8.1, gix-archive v0.8.1, gix-config-value v0.14.3, gix-tempfile v12.0.1, gix-lock v12.0.1, gix-ref v0.40.1, gix-sec v0.10.3, gix-config v0.33.1, gix-prompt v0.8.2, gix-url v0.26.1, gix-credentials v0.23.1, gix-ignore v0.10.1, gix-bitmap v0.2.10, gix-index v0.28.1, gix-worktree v0.29.1, gix-diff v0.39.1, gix-discover v0.28.1, gix-macros v0.1.3, gix-mailmap v0.21.1, gix-negotiate v0.11.1, gix-pack v0.46.1, gix-odb v0.56.1, gix-pathspec v0.5.1, gix-packetline v0.17.2, gix-transport v0.40.1, gix-protocol v0.43.1, gix-revision v0.25.1, gix-refspec v0.21.1, gix-status v0.4.1, gix-submodule v0.7.1, gix-worktree-state v0.6.1, gix v0.57.1 ([`972241f`](https://github.com/Byron/gitoxide/commit/972241f1904944e8b6e84c6aa1649a49be7a85c3)) - Merge branch 'msrv' ([`8c492d7`](https://github.com/Byron/gitoxide/commit/8c492d7b7e6e5d520b1e3ffeb489eeb88266aa75)) - Change `rust-version` manifest field back to 1.65. ([`3bd09ef`](https://github.com/Byron/gitoxide/commit/3bd09ef120945a9669321ea856db4079a5dab930))
@@ -41,8 +69,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Chore - - upgrade MSRV to v1.70 - Our MSRV follows the one of `helix`, which in turn follows Firefox. +- upgrade MSRV to v1.70 + Our MSRV follows the one of `helix`, which in turn follows Firefox. ### Bug Fixes (BREAKING) @@ -450,7 +478,7 @@ A maintenance release without user-facing changes. ### Refactor (BREAKING) - - rename `oid::short_hex()` to `oid::to_hex()` +- rename `oid::short_hex()` to `oid::to_hex()` ### New Features (BREAKING) @@ -499,7 +527,7 @@ A maintenance release without user-facing changes. ### Refactor - - replace `quickerror` with `thiserror` +- replace `quickerror` with `thiserror` ### Bug Fixes @@ -538,7 +566,7 @@ A maintenance release without user-facing changes. ### Chore - - uniformize deny attributes +- uniformize deny attributes ### Documentation @@ -857,7 +885,7 @@ Maintenance release without observable changes. ### Refactor - - replace `quickerror` with `thiserror` +- replace `quickerror` with `thiserror` ## 0.9.8 (2022-08-24) @@ -865,7 +893,7 @@ Maintenance release without observable changes. ### Chore - - uniformize deny attributes +- uniformize deny attributes ### New Features @@ -1032,8 +1060,8 @@ A maintenance release due to reset the entire crate graph to new minor releases. ### BREAKING Changes - - rename `oid::short_hex()` to `oid::to_hex()` - - `oid::short_hex(len)` for truncated hex representations +- rename `oid::short_hex()` to `oid::to_hex()` +- `oid::short_hex(len)` for truncated hex representations ## v0.6.0 (2021-09-07) diff --git a/gix-hash/Cargo.toml b/gix-hash/Cargo.toml index 2ed05a72e51..6faf9187060 100644 --- a/gix-hash/Cargo.toml +++ b/gix-hash/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "gix-hash" -version = "0.14.1" +version = "0.14.2" description = "Borrowed and owned git hash digests used to identify git objects" authors = ["Sebastian Thiel "] repository = "https://github.com/Byron/gitoxide" diff --git a/gix-hash/src/kind.rs b/gix-hash/src/kind.rs index ee8600b1b64..5c7a0316b8d 100644 --- a/gix-hash/src/kind.rs +++ b/gix-hash/src/kind.rs @@ -1,4 +1,4 @@ -use std::{convert::TryFrom, str::FromStr}; +use std::str::FromStr; use crate::{oid, Kind, ObjectId}; diff --git a/gix-hash/src/lib.rs b/gix-hash/src/lib.rs index 4c5b91231b7..baa5b9ea9a4 100644 --- a/gix-hash/src/lib.rs +++ b/gix-hash/src/lib.rs @@ -17,9 +17,10 @@ mod object_id; pub use object_id::{decode, ObjectId}; /// +#[allow(clippy::empty_docs)] pub mod prefix; -/// An partial, owned hash possibly identifying an object uniquely, whose non-prefix bytes are zeroed. +/// A partial, owned hash possibly identifying an object uniquely, whose non-prefix bytes are zeroed. /// /// An example would `0000000000000000000000000000000032bd3242`, where `32bd3242` is the prefix, /// which would be able to match all hashes that *start with* `32bd3242`. diff --git a/gix-hash/src/oid.rs b/gix-hash/src/oid.rs index 4bf6648e751..6360712c5b3 100644 --- a/gix-hash/src/oid.rs +++ b/gix-hash/src/oid.rs @@ -1,4 +1,4 @@ -use std::{convert::TryInto, hash}; +use std::hash; use crate::{Kind, ObjectId, SIZE_OF_SHA1_DIGEST}; diff --git a/gix-hash/src/prefix.rs b/gix-hash/src/prefix.rs index b9d3849ab78..dc1752eba81 100644 --- a/gix-hash/src/prefix.rs +++ b/gix-hash/src/prefix.rs @@ -1,4 +1,4 @@ -use std::{cmp::Ordering, convert::TryFrom}; +use std::cmp::Ordering; use crate::{oid, ObjectId, Prefix}; @@ -16,6 +16,7 @@ pub enum Error { } /// +#[allow(clippy::empty_docs)] pub mod from_hex { /// The error returned by [`Prefix::from_hex`][super::Prefix::from_hex()]. #[derive(Debug, Eq, PartialEq, thiserror::Error)] diff --git a/gix-hash/tests/prefix/mod.rs b/gix-hash/tests/prefix/mod.rs index 14dbed67af3..357a18869a7 100644 --- a/gix-hash/tests/prefix/mod.rs +++ b/gix-hash/tests/prefix/mod.rs @@ -73,7 +73,7 @@ mod new { } mod try_from { - use std::{cmp::Ordering, convert::TryFrom}; + use std::cmp::Ordering; use gix_hash::{prefix::from_hex::Error, Prefix}; diff --git a/gix-hashtable/CHANGELOG.md b/gix-hashtable/CHANGELOG.md index 6bf13ba0209..51cb834e128 100644 --- a/gix-hashtable/CHANGELOG.md +++ b/gix-hashtable/CHANGELOG.md @@ -5,22 +5,49 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## 0.5.2 (2024-03-14) + +A maintenance release without user-facing changes. + +### Commit Statistics + + + + - 3 commits contributed to the release over the course of 4 calendar days. + - 75 days passed between releases. + - 0 commits were understood as [conventional](https://www.conventionalcommits.org). + - 0 issues like '(#ID)' were seen in commit messages + +### Commit Details + + + +
view details + + * **Uncategorized** + - Prepare changelogs prior to release ([`52c3bbd`](https://github.com/Byron/gitoxide/commit/52c3bbd36b9e94a0f3a78b4ada84d0c08eba27f6)) + - Merge branch 'status' ([`3e5c974`](https://github.com/Byron/gitoxide/commit/3e5c974dd62ac134711c6c2f5a5490187a6ea55e)) + - Fix lints for nightly, and clippy ([`f8ce3d0`](https://github.com/Byron/gitoxide/commit/f8ce3d0721b6a53713a9392f2451874f520bc44c)) +
+ ## 0.5.1 (2023-12-30) + + ### Chore - - change `rust-version` manifest field back to 1.65. - They didn't actually need to be higher to work, and changing them - unecessarily can break downstream CI. - - Let's keep this value as low as possible, and only increase it when - more recent features are actually used. +- change `rust-version` manifest field back to 1.65. + They didn't actually need to be higher to work, and changing them + unecessarily can break downstream CI. + + Let's keep this value as low as possible, and only increase it when + more recent features are actually used. ### Commit Statistics - - 2 commits contributed to the release. + - 3 commits contributed to the release. - 1 commit was understood as [conventional](https://www.conventionalcommits.org). - 0 issues like '(#ID)' were seen in commit messages @@ -31,6 +58,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
view details * **Uncategorized** + - Release gix-date v0.8.3, gix-hash v0.14.1, gix-trace v0.1.6, gix-features v0.37.1, gix-actor v0.29.1, gix-validate v0.8.3, gix-object v0.40.1, gix-path v0.10.3, gix-glob v0.15.1, gix-quote v0.4.10, gix-attributes v0.21.1, gix-command v0.3.2, gix-packetline-blocking v0.17.2, gix-utils v0.1.8, gix-filter v0.8.1, gix-fs v0.9.1, gix-chunk v0.4.7, gix-commitgraph v0.23.1, gix-hashtable v0.5.1, gix-revwalk v0.11.1, gix-traverse v0.36.1, gix-worktree-stream v0.8.1, gix-archive v0.8.1, gix-config-value v0.14.3, gix-tempfile v12.0.1, gix-lock v12.0.1, gix-ref v0.40.1, gix-sec v0.10.3, gix-config v0.33.1, gix-prompt v0.8.2, gix-url v0.26.1, gix-credentials v0.23.1, gix-ignore v0.10.1, gix-bitmap v0.2.10, gix-index v0.28.1, gix-worktree v0.29.1, gix-diff v0.39.1, gix-discover v0.28.1, gix-macros v0.1.3, gix-mailmap v0.21.1, gix-negotiate v0.11.1, gix-pack v0.46.1, gix-odb v0.56.1, gix-pathspec v0.5.1, gix-packetline v0.17.2, gix-transport v0.40.1, gix-protocol v0.43.1, gix-revision v0.25.1, gix-refspec v0.21.1, gix-status v0.4.1, gix-submodule v0.7.1, gix-worktree-state v0.6.1, gix v0.57.1 ([`972241f`](https://github.com/Byron/gitoxide/commit/972241f1904944e8b6e84c6aa1649a49be7a85c3)) - Merge branch 'msrv' ([`8c492d7`](https://github.com/Byron/gitoxide/commit/8c492d7b7e6e5d520b1e3ffeb489eeb88266aa75)) - Change `rust-version` manifest field back to 1.65. ([`3bd09ef`](https://github.com/Byron/gitoxide/commit/3bd09ef120945a9669321ea856db4079a5dab930))
@@ -41,8 +69,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Chore - - upgrade MSRV to v1.70 - Our MSRV follows the one of `helix`, which in turn follows Firefox. +- upgrade MSRV to v1.70 + Our MSRV follows the one of `helix`, which in turn follows Firefox. ### Commit Statistics diff --git a/gix-hashtable/Cargo.toml b/gix-hashtable/Cargo.toml index 901661478b9..3a057602c94 100644 --- a/gix-hashtable/Cargo.toml +++ b/gix-hashtable/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "gix-hashtable" -version = "0.5.1" +version = "0.5.2" repository = "https://github.com/Byron/gitoxide" license = "MIT OR Apache-2.0" description = "A crate that provides hashtable based data structures optimized to utilize ObjectId keys" @@ -18,5 +18,5 @@ hashbrown = { version = "0.14.0", default-features = false, features = [ "inline-more", "raw" ] } -gix-hash = { version = "^0.14.1", path = "../gix-hash" } +gix-hash = { version = "^0.14.2", path = "../gix-hash" } diff --git a/gix-hashtable/src/lib.rs b/gix-hashtable/src/lib.rs index 54935bcbca7..397b754df17 100644 --- a/gix-hashtable/src/lib.rs +++ b/gix-hashtable/src/lib.rs @@ -34,6 +34,7 @@ pub mod sync { } /// +#[allow(clippy::empty_docs)] pub mod hash { /// A Hasher for usage with `HashMap` keys that are already robust hashes (like an `ObjectId`). /// The first `8` bytes of the hash are used as the `HashMap` hash diff --git a/gix-ignore/CHANGELOG.md b/gix-ignore/CHANGELOG.md index 7fc6ba090df..3c489c90176 100644 --- a/gix-ignore/CHANGELOG.md +++ b/gix-ignore/CHANGELOG.md @@ -5,6 +5,31 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## 0.11.2 (2024-03-14) + +A maintenance release without user-facing changes. + +### Commit Statistics + + + + - 3 commits contributed to the release over the course of 4 calendar days. + - 18 days passed between releases. + - 0 commits were understood as [conventional](https://www.conventionalcommits.org). + - 0 issues like '(#ID)' were seen in commit messages + +### Commit Details + + + +
view details + + * **Uncategorized** + - Prepare changelogs prior to release ([`52c3bbd`](https://github.com/Byron/gitoxide/commit/52c3bbd36b9e94a0f3a78b4ada84d0c08eba27f6)) + - Merge branch 'status' ([`3e5c974`](https://github.com/Byron/gitoxide/commit/3e5c974dd62ac134711c6c2f5a5490187a6ea55e)) + - Fix lints for nightly, and clippy ([`f8ce3d0`](https://github.com/Byron/gitoxide/commit/f8ce3d0721b6a53713a9392f2451874f520bc44c)) +
+ ## 0.11.1 (2024-02-25) A maintenance release without user-facing changes. @@ -13,7 +38,7 @@ A maintenance release without user-facing changes. - - 2 commits contributed to the release over the course of 30 calendar days. + - 3 commits contributed to the release over the course of 30 calendar days. - 36 days passed between releases. - 0 commits were understood as [conventional](https://www.conventionalcommits.org). - 0 issues like '(#ID)' were seen in commit messages @@ -25,6 +50,7 @@ A maintenance release without user-facing changes.
view details * **Uncategorized** + - Release gix-date v0.8.4, gix-utils v0.1.10, gix-actor v0.30.1, gix-object v0.41.1, gix-path v0.10.6, gix-glob v0.16.1, gix-quote v0.4.11, gix-attributes v0.22.1, gix-command v0.3.5, gix-filter v0.10.0, gix-commitgraph v0.24.1, gix-worktree-stream v0.10.0, gix-archive v0.10.0, gix-config-value v0.14.5, gix-ref v0.42.0, gix-sec v0.10.5, gix-config v0.35.0, gix-prompt v0.8.3, gix-url v0.27.1, gix-credentials v0.24.1, gix-ignore v0.11.1, gix-index v0.30.0, gix-worktree v0.31.0, gix-diff v0.41.0, gix-discover v0.30.0, gix-pathspec v0.7.0, gix-dir v0.1.0, gix-pack v0.48.0, gix-odb v0.58.0, gix-transport v0.41.1, gix-protocol v0.44.1, gix-revision v0.26.1, gix-refspec v0.22.1, gix-status v0.6.0, gix-submodule v0.9.0, gix-worktree-state v0.8.0, gix v0.59.0, gix-fsck v0.3.0, gitoxide-core v0.36.0, gitoxide v0.34.0, safety bump 10 crates ([`45b4470`](https://github.com/Byron/gitoxide/commit/45b447045bc826f252129c300c531acde2652c64)) - Prepare changelogs prior to release ([`f2e111f`](https://github.com/Byron/gitoxide/commit/f2e111f768fc1bc6182355261c20b63610cffec7)) - Release gix-path v0.10.5 ([`b8cba96`](https://github.com/Byron/gitoxide/commit/b8cba96ce57f8b6b0067d6a8cf3e37eaf280a238))
diff --git a/gix-ignore/Cargo.toml b/gix-ignore/Cargo.toml index 2851ae41a1c..1ea127e8ea3 100644 --- a/gix-ignore/Cargo.toml +++ b/gix-ignore/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "gix-ignore" -version = "0.11.1" +version = "0.11.2" repository = "https://github.com/Byron/gitoxide" license = "MIT OR Apache-2.0" description = "A crate of the gitoxide project dealing .gitignore files" @@ -17,9 +17,9 @@ doctest = false serde = ["dep:serde", "bstr/serde", "gix-glob/serde"] [dependencies] -gix-glob = { version = "^0.16.1", path = "../gix-glob" } -gix-path = { version = "^0.10.6", path = "../gix-path" } -gix-trace = { version = "^0.1.7", path = "../gix-trace" } +gix-glob = { version = "^0.16.3", path = "../gix-glob" } +gix-path = { version = "^0.10.7", path = "../gix-path" } +gix-trace = { version = "^0.1.8", path = "../gix-trace" } bstr = { version = "1.3.0", default-features = false, features = ["std", "unicode"]} unicode-bom = "2.0.2" diff --git a/gix-ignore/src/lib.rs b/gix-ignore/src/lib.rs index a9ba2351e3c..5f7cc0c5d1c 100644 --- a/gix-ignore/src/lib.rs +++ b/gix-ignore/src/lib.rs @@ -12,6 +12,7 @@ pub use gix_glob as glob; /// +#[allow(clippy::empty_docs)] pub mod search; /// A grouping of lists of patterns while possibly keeping associated to their base path in order to find matches. /// @@ -45,6 +46,7 @@ pub enum Kind { } /// +#[allow(clippy::empty_docs)] pub mod parse; /// Parse git ignore patterns, line by line, from `bytes`. diff --git a/gix-ignore/src/search.rs b/gix-ignore/src/search.rs index a527b447221..9b5a2766b61 100644 --- a/gix-ignore/src/search.rs +++ b/gix-ignore/src/search.rs @@ -55,7 +55,7 @@ impl Search { .transpose()?, ); group.patterns.extend(pattern::List::::from_file( - &git_dir.join("info").join("exclude"), + git_dir.join("info").join("exclude"), None, follow_symlinks, buf, diff --git a/gix-ignore/tests/fixtures/make_global_and_external_and_dir_ignores.sh b/gix-ignore/tests/fixtures/make_global_and_external_and_dir_ignores.sh index f4bd0997ab4..9959668e671 100755 --- a/gix-ignore/tests/fixtures/make_global_and_external_and_dir_ignores.sh +++ b/gix-ignore/tests/fixtures/make_global_and_external_and_dir_ignores.sh @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash set -eu -o pipefail cat <user.exclude diff --git a/gix-index/CHANGELOG.md b/gix-index/CHANGELOG.md index 4ab4845ce22..210b650cfec 100644 --- a/gix-index/CHANGELOG.md +++ b/gix-index/CHANGELOG.md @@ -5,6 +5,172 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## 0.33.0 (2024-05-22) + + + +### New Features + + - add `From for gix_index::entry::Mode`. + +### Other + + - make clear that indices can contain invalid or dangerous paths. + It's probably best not to try to protect against violations of constraints + in this free-to-mutate data-structure and instead suggest to validate entry + paths before using them on disk (or use the `gix_worktree::Stack`). + +### Bug Fixes (BREAKING) + + - `State::from_tree()` now performs name validation. + Previously, malicious trees could be used to create a index with + invalid names, which is one step closer to actually abusing it. + +### Commit Statistics + + + + - 8 commits contributed to the release over the course of 33 calendar days. + - 33 days passed between releases. + - 3 commits were understood as [conventional](https://www.conventionalcommits.org). + - 0 issues like '(#ID)' were seen in commit messages + +### Commit Details + + + +
view details + + * **Uncategorized** + - Adjust changelogs prior to release ([`9511416`](https://github.com/Byron/gitoxide/commit/9511416a6cd0c571233f958c165329c8705c2498)) + - Merge branch 'various-fixes' ([`d6cd449`](https://github.com/Byron/gitoxide/commit/d6cd44930fb204b06e2b70fc6965e7705530c47a)) + - Merge pull request from GHSA-7w47-3wg8-547c ([`79dce79`](https://github.com/Byron/gitoxide/commit/79dce79c62f6072aa2653780d590dc3993dfa401)) + - `State::from_tree()` now performs name validation. ([`2ea87f0`](https://github.com/Byron/gitoxide/commit/2ea87f0060fd796961a2173569f16f362ed61617)) + - Apply suggestions from code review ([`bad9a79`](https://github.com/Byron/gitoxide/commit/bad9a797b99880ce9d1c20e11c801bd0e741db64)) + - Make clear that indices can contain invalid or dangerous paths. ([`b6a67d7`](https://github.com/Byron/gitoxide/commit/b6a67d7fe3b5645fccb785af01d72919c6761c52)) + - Add `From for gix_index::entry::Mode`. ([`9564699`](https://github.com/Byron/gitoxide/commit/956469944d14bc0e5b16e3f95d407bb8b2282903)) + - Merge branch 'cargo-fixes' ([`977346e`](https://github.com/Byron/gitoxide/commit/977346ee61de6207c66f3de003db6e8c722fb81c)) +
+ +## 0.32.1 (2024-04-18) + +A maintenance release without user-facing changes. + +### Commit Statistics + + + + - 4 commits contributed to the release over the course of 4 calendar days. + - 5 days passed between releases. + - 0 commits were understood as [conventional](https://www.conventionalcommits.org). + - 0 issues like '(#ID)' were seen in commit messages + +### Commit Details + + + +
view details + + * **Uncategorized** + - Release gix-index v0.32.1, gix-pathspec v0.7.4, gix-worktree v0.33.1, gix-dir v0.4.1 ([`54ac559`](https://github.com/Byron/gitoxide/commit/54ac55946bb04635cd74582a1ce2e4bee70f2e60)) + - Prepare changelog prior to `gix-dir` patch release ([`6ca6fa6`](https://github.com/Byron/gitoxide/commit/6ca6fa69b5c21c8d8e9e07e21558e98201504cda)) + - Merge pull request #1345 from EliahKagan/shell-scripts ([`fe24c89`](https://github.com/Byron/gitoxide/commit/fe24c89e326670deaa3aaa643276d612d866072e)) + - Add missing +x bit on scripts that are run and not sourced ([`41bf65a`](https://github.com/Byron/gitoxide/commit/41bf65adef6f7d2cdd28fede262173ec7ba10822)) +
+ +## 0.32.0 (2024-04-13) + +### New Features (BREAKING) + + - turn `entry::Stage` into an `enum`. + This way, it's much clearer what possible values are. + For performance, it also adds `Entry::stage_raw()` to avoid having + to match on a number. + +### Commit Statistics + + + + - 6 commits contributed to the release over the course of 9 calendar days. + - 26 days passed between releases. + - 1 commit was understood as [conventional](https://www.conventionalcommits.org). + - 1 unique issue was worked on: [#1330](https://github.com/Byron/gitoxide/issues/1330) + +### Commit Details + + + +
view details + + * **[#1330](https://github.com/Byron/gitoxide/issues/1330)** + - Fix unread-fields warnings on latest nightly ([`2e06a00`](https://github.com/Byron/gitoxide/commit/2e06a00800418a6f571ba1731ffe05074565af03)) + * **Uncategorized** + - Release gix-trace v0.1.9, gix-utils v0.1.12, gix-packetline-blocking v0.17.4, gix-filter v0.11.1, gix-fs v0.10.2, gix-traverse v0.39.0, gix-worktree-stream v0.12.0, gix-archive v0.12.0, gix-config v0.36.1, gix-url v0.27.3, gix-index v0.32.0, gix-worktree v0.33.0, gix-diff v0.43.0, gix-pathspec v0.7.3, gix-dir v0.4.0, gix-pack v0.50.0, gix-odb v0.60.0, gix-transport v0.42.0, gix-protocol v0.45.0, gix-status v0.9.0, gix-worktree-state v0.10.0, gix v0.62.0, gix-fsck v0.4.0, gitoxide-core v0.37.0, gitoxide v0.35.0, safety bump 14 crates ([`095c673`](https://github.com/Byron/gitoxide/commit/095c6739b2722a8b9af90776b435ef2da454c0e6)) + - Prepare changelogs prior to release ([`5755271`](https://github.com/Byron/gitoxide/commit/57552717f46f96c35ba4ddc0a64434354ef845e9)) + - Merge branch 'status' ([`45edd2e`](https://github.com/Byron/gitoxide/commit/45edd2ea66035adf526cb2f617873dcba60a2a9a)) + - Adapt to changes in `gix-index` ([`1e1fce1`](https://github.com/Byron/gitoxide/commit/1e1fce11a968ebbcede1135ccbd0b03e749a1267)) + - Turn `entry::Stage` into an `enum`. ([`a6cc781`](https://github.com/Byron/gitoxide/commit/a6cc78176c5b399c363bb6ee776cb16d6f5d3dad)) +
+ +## 0.31.1 (2024-03-18) + +A maintenance release without user-facing changes. + +### Commit Statistics + + + + - 4 commits contributed to the release. + - 3 days passed between releases. + - 0 commits were understood as [conventional](https://www.conventionalcommits.org). + - 0 issues like '(#ID)' were seen in commit messages + +### Commit Details + + + +
view details + + * **Uncategorized** + - Release gix-actor v0.31.1, gix-object v0.42.1, gix-index v0.31.1, gix-pathspec v0.7.2, gix-dir v0.3.0, gix-status v0.8.0, gix v0.61.0, safety bump 2 crates ([`155cc45`](https://github.com/Byron/gitoxide/commit/155cc45730b259e662d7c4be42a469a3af3750e1)) + - Prepare changelog prior to release ([`129ba3d`](https://github.com/Byron/gitoxide/commit/129ba3deccc9ada0dc571466458845939502763d)) + - Merge branch 'improvements-for-cargo' ([`41cd53e`](https://github.com/Byron/gitoxide/commit/41cd53e2af76e35e047aac4eca6324774df4cb50)) + - Remove a TODO as it doesn't make sense anymore. ([`b05af9f`](https://github.com/Byron/gitoxide/commit/b05af9f86119ba5c71a19d632312fd66edb30a0e)) +
+ +## 0.31.0 (2024-03-14) + +### Bug Fixes + + - assure memory maps are created with `MAP_PRIVATE` + That way, the mmap process should work under more circumstances. + +### Commit Statistics + + + + - 7 commits contributed to the release over the course of 10 calendar days. + - 18 days passed between releases. + - 1 commit was understood as [conventional](https://www.conventionalcommits.org). + - 1 unique issue was worked on: [#1312](https://github.com/Byron/gitoxide/issues/1312) + +### Commit Details + + + +
view details + + * **[#1312](https://github.com/Byron/gitoxide/issues/1312)** + - Assure memory maps are created with `MAP_PRIVATE` ([`88061a1`](https://github.com/Byron/gitoxide/commit/88061a176b2f4b5a377a4cff513979ddb1e306a1)) + * **Uncategorized** + - Release gix-date v0.8.5, gix-hash v0.14.2, gix-trace v0.1.8, gix-utils v0.1.11, gix-features v0.38.1, gix-actor v0.31.0, gix-validate v0.8.4, gix-object v0.42.0, gix-path v0.10.7, gix-glob v0.16.2, gix-quote v0.4.12, gix-attributes v0.22.2, gix-command v0.3.6, gix-filter v0.11.0, gix-fs v0.10.1, gix-chunk v0.4.8, gix-commitgraph v0.24.2, gix-hashtable v0.5.2, gix-revwalk v0.13.0, gix-traverse v0.38.0, gix-worktree-stream v0.11.0, gix-archive v0.11.0, gix-config-value v0.14.6, gix-tempfile v13.1.1, gix-lock v13.1.1, gix-ref v0.43.0, gix-sec v0.10.6, gix-config v0.36.0, gix-prompt v0.8.4, gix-url v0.27.2, gix-credentials v0.24.2, gix-ignore v0.11.2, gix-bitmap v0.2.11, gix-index v0.31.0, gix-worktree v0.32.0, gix-diff v0.42.0, gix-discover v0.31.0, gix-pathspec v0.7.1, gix-dir v0.2.0, gix-macros v0.1.4, gix-mailmap v0.23.0, gix-negotiate v0.13.0, gix-pack v0.49.0, gix-odb v0.59.0, gix-packetline v0.17.4, gix-transport v0.41.2, gix-protocol v0.44.2, gix-revision v0.27.0, gix-refspec v0.23.0, gix-status v0.7.0, gix-submodule v0.10.0, gix-worktree-state v0.9.0, gix v0.60.0, safety bump 26 crates ([`b050327`](https://github.com/Byron/gitoxide/commit/b050327e76f234b19be921b78b7b28e034319fdb)) + - Prepare changelogs prior to release ([`52c3bbd`](https://github.com/Byron/gitoxide/commit/52c3bbd36b9e94a0f3a78b4ada84d0c08eba27f6)) + - Merge branch 'status' ([`3e5c974`](https://github.com/Byron/gitoxide/commit/3e5c974dd62ac134711c6c2f5a5490187a6ea55e)) + - Fix lints for nightly, and clippy ([`f8ce3d0`](https://github.com/Byron/gitoxide/commit/f8ce3d0721b6a53713a9392f2451874f520bc44c)) + - Merge branch 'mmap-mode' ([`9e9b9fe`](https://github.com/Byron/gitoxide/commit/9e9b9fe6f63759d3bf479a8ec2f9dd1fbef87a08)) + - Fix usage of tracing span (which now is just a message) ([`259b38e`](https://github.com/Byron/gitoxide/commit/259b38e629807e887442ca6140a14cefcd672699)) +
+ ## 0.30.0 (2024-02-25) ### New Features @@ -26,7 +192,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - - 11 commits contributed to the release over the course of 19 calendar days. + - 12 commits contributed to the release over the course of 19 calendar days. - 36 days passed between releases. - 2 commits were understood as [conventional](https://www.conventionalcommits.org). - 0 issues like '(#ID)' were seen in commit messages @@ -38,6 +204,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
view details * **Uncategorized** + - Release gix-date v0.8.4, gix-utils v0.1.10, gix-actor v0.30.1, gix-object v0.41.1, gix-path v0.10.6, gix-glob v0.16.1, gix-quote v0.4.11, gix-attributes v0.22.1, gix-command v0.3.5, gix-filter v0.10.0, gix-commitgraph v0.24.1, gix-worktree-stream v0.10.0, gix-archive v0.10.0, gix-config-value v0.14.5, gix-ref v0.42.0, gix-sec v0.10.5, gix-config v0.35.0, gix-prompt v0.8.3, gix-url v0.27.1, gix-credentials v0.24.1, gix-ignore v0.11.1, gix-index v0.30.0, gix-worktree v0.31.0, gix-diff v0.41.0, gix-discover v0.30.0, gix-pathspec v0.7.0, gix-dir v0.1.0, gix-pack v0.48.0, gix-odb v0.58.0, gix-transport v0.41.1, gix-protocol v0.44.1, gix-revision v0.26.1, gix-refspec v0.22.1, gix-status v0.6.0, gix-submodule v0.9.0, gix-worktree-state v0.8.0, gix v0.59.0, gix-fsck v0.3.0, gitoxide-core v0.36.0, gitoxide v0.34.0, safety bump 10 crates ([`45b4470`](https://github.com/Byron/gitoxide/commit/45b447045bc826f252129c300c531acde2652c64)) - Prepare changelogs prior to release ([`f2e111f`](https://github.com/Byron/gitoxide/commit/f2e111f768fc1bc6182355261c20b63610cffec7)) - Merge branch 'btoi' ([`5fc379d`](https://github.com/Byron/gitoxide/commit/5fc379d1dc867d15a50cb086e30beefde2b42d86)) - Refactor ([`c5c69bd`](https://github.com/Byron/gitoxide/commit/c5c69bd355771a6fb3e4f6db0c5f49aa2bf7f42f)) diff --git a/gix-index/Cargo.toml b/gix-index/Cargo.toml index 684c93c5713..92290db0a30 100644 --- a/gix-index/Cargo.toml +++ b/gix-index/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "gix-index" -version = "0.30.0" +version = "0.33.0" repository = "https://github.com/Byron/gitoxide" license = "MIT OR Apache-2.0" description = "A work-in-progress crate of the gitoxide project dedicated implementing the git index file" @@ -20,17 +20,18 @@ test = true serde = ["dep:serde", "smallvec/serde", "gix-hash/serde"] [dependencies] -gix-features = { version = "^0.38.0", path = "../gix-features", features = [ +gix-features = { version = "^0.38.2", path = "../gix-features", features = [ "rustsha1", "progress", ] } -gix-hash = { version = "^0.14.1", path = "../gix-hash" } -gix-bitmap = { version = "^0.2.10", path = "../gix-bitmap" } -gix-object = { version = "^0.41.1", path = "../gix-object" } -gix-traverse = { version = "^0.37.0", path = "../gix-traverse" } -gix-lock = { version = "^13.0.0", path = "../gix-lock" } -gix-fs = { version = "^0.10.0", path = "../gix-fs" } -gix-utils = { version = "^0.1.10", path = "../gix-utils" } +gix-hash = { version = "^0.14.2", path = "../gix-hash" } +gix-bitmap = { version = "^0.2.11", path = "../gix-bitmap" } +gix-object = { version = "^0.42.2", path = "../gix-object" } +gix-validate = { version = "^0.8.5", path = "../gix-validate" } +gix-traverse = { version = "^0.39.1", path = "../gix-traverse" } +gix-lock = { version = "^14.0.0", path = "../gix-lock" } +gix-fs = { version = "^0.11.1", path = "../gix-fs" } +gix-utils = { version = "^0.1.12", path = "../gix-utils" } hashbrown = "0.14.3" fnv = "1.0.7" diff --git a/gix-index/src/access/mod.rs b/gix-index/src/access/mod.rs index 431a47e5805..dd2449c6d47 100644 --- a/gix-index/src/access/mod.rs +++ b/gix-index/src/access/mod.rs @@ -3,6 +3,7 @@ use std::{cmp::Ordering, ops::Range}; use bstr::{BStr, ByteSlice, ByteVec}; use filetime::FileTime; +use crate::entry::{Stage, StageRaw}; use crate::{entry, extension, AccelerateLookup, Entry, PathStorage, PathStorageRef, State, Version}; // TODO: integrate this somehow, somewhere, depending on later usage. @@ -81,7 +82,7 @@ impl State { res }) .ok()?; - self.entry_index_by_idx_and_stage(path, idx, stage, stage_cmp) + self.entry_index_by_idx_and_stage(path, idx, stage as StageRaw, stage_cmp) } /// Walk as far in `direction` as possible, with [`Ordering::Greater`] towards higher stages, and [`Ordering::Less`] @@ -112,7 +113,7 @@ impl State { &self, path: &BStr, idx: usize, - wanted_stage: entry::Stage, + wanted_stage: entry::StageRaw, stage_cmp: Ordering, ) -> Option { match stage_cmp { @@ -121,7 +122,7 @@ impl State { .enumerate() .rev() .take_while(|(_, e)| e.path(self) == path) - .find_map(|(idx, e)| (e.stage() == wanted_stage).then_some(idx)), + .find_map(|(idx, e)| (e.stage_raw() == wanted_stage).then_some(idx)), Ordering::Equal => Some(idx), Ordering::Less => self .entries @@ -129,7 +130,7 @@ impl State { .iter() .enumerate() .take_while(|(_, e)| e.path(self) == path) - .find_map(|(ofs, e)| (e.stage() == wanted_stage).then_some(idx + ofs + 1)), + .find_map(|(ofs, e)| (e.stage_raw() == wanted_stage).then_some(idx + ofs + 1)), } } @@ -173,7 +174,7 @@ impl State { } } } - gix_features::trace::detail!("stored directories", directories = out.icase_dirs.len()); + gix_features::trace::debug!(directories = out.icase_dirs.len(), "stored directories"); out } @@ -291,7 +292,7 @@ impl State { .binary_search_by(|e| { let res = e.path(self).cmp(path); if res.is_eq() { - stage_at_index = e.stage(); + stage_at_index = e.stage_raw(); } res }) @@ -299,7 +300,7 @@ impl State { let idx = if stage_at_index == 0 || stage_at_index == 2 { idx } else { - self.entry_index_by_idx_and_stage(path, idx, 2, stage_at_index.cmp(&2))? + self.entry_index_by_idx_and_stage(path, idx, Stage::Ours as StageRaw, stage_at_index.cmp(&2))? }; Some(&self.entries[idx]) } @@ -334,13 +335,13 @@ impl State { + self.entries[low..].partition_point(|e| e.path(self).get(..prefix_len).map_or(false, |p| p <= prefix)); let low_entry = &self.entries.get(low)?; - if low_entry.stage() != 0 { + if low_entry.stage_raw() != 0 { low = self .walk_entry_stages(low_entry.path(self), low, Ordering::Less) .unwrap_or(low); } if let Some(high_entry) = self.entries.get(high) { - if high_entry.stage() != 0 { + if high_entry.stage_raw() != 0 { high = self .walk_entry_stages(high_entry.path(self), high, Ordering::Less) .unwrap_or(high); @@ -374,7 +375,7 @@ impl State { .binary_search_by(|e| { let res = e.path(self).cmp(path); if res.is_eq() { - stage_at_index = e.stage(); + stage_at_index = e.stage_raw(); } res }) diff --git a/gix-index/src/decode/entries.rs b/gix-index/src/decode/entries.rs index 78f544691ae..d3bda1cb6a3 100644 --- a/gix-index/src/decode/entries.rs +++ b/gix-index/src/decode/entries.rs @@ -1,4 +1,4 @@ -use std::{convert::TryInto, ops::Range}; +use std::ops::Range; use crate::{ decode::{self, header}, diff --git a/gix-index/src/decode/mod.rs b/gix-index/src/decode/mod.rs index aea189248bd..6c77109877a 100644 --- a/gix-index/src/decode/mod.rs +++ b/gix-index/src/decode/mod.rs @@ -4,6 +4,7 @@ use crate::{entry, extension, Entry, State, Version}; mod entries; /// +#[allow(clippy::empty_docs)] pub mod header; mod error { diff --git a/gix-index/src/entry/flags.rs b/gix-index/src/entry/flags.rs index 05198bafb01..c32cb6f8d7a 100644 --- a/gix-index/src/entry/flags.rs +++ b/gix-index/src/entry/flags.rs @@ -62,6 +62,23 @@ bitflags! { impl Flags { /// Return the stage as extracted from the bits of this instance. pub fn stage(&self) -> Stage { + match self.stage_raw() { + 0 => Stage::Unconflicted, + 1 => Stage::Base, + 2 => Stage::Ours, + 3 => Stage::Theirs, + _ => unreachable!("BUG: Flags::STAGE_MASK is two bits, whose 4 possible values we have covered"), + } + } + + /// Return an entry's stage as raw number between 0 and 4. + /// Possible values are: + /// + /// * 0 = no conflict, + /// * 1 = base, + /// * 2 = ours, + /// * 3 = theirs + pub fn stage_raw(&self) -> u32 { (*self & Flags::STAGE_MASK).bits() >> 12 } diff --git a/gix-index/src/entry/mod.rs b/gix-index/src/entry/mod.rs index b7fb13f7baf..5fd2e501930 100644 --- a/gix-index/src/entry/mod.rs +++ b/gix-index/src/entry/mod.rs @@ -1,11 +1,26 @@ -/// The stage of an entry, one of… +/// The stage of an entry. +#[derive(Default, Copy, Clone, Debug, PartialEq, Eq, Ord, PartialOrd, Hash)] +pub enum Stage { + /// This is the default, and most entries are in this stage. + #[default] + Unconflicted = 0, + /// The entry is the common base between 'our' change and 'their' change, for comparison. + Base = 1, + /// The entry represents our change. + Ours = 2, + /// The entry represents their change. + Theirs = 3, +} + +// The stage of an entry, one of… /// * 0 = no conflict, /// * 1 = base, /// * 2 = ours, /// * 3 = theirs -pub type Stage = u32; +pub type StageRaw = u32; /// +#[allow(clippy::empty_docs)] pub mod mode; mod flags; @@ -13,14 +28,12 @@ pub(crate) use flags::at_rest; pub use flags::Flags; /// +#[allow(clippy::empty_docs)] pub mod stat; mod write; use bitflags::bitflags; -// TODO: we essentially treat this as an enum with the only exception being -// that `FILE_EXECUTABLE.contains(FILE)` works might want to turn this into an -// enum proper bitflags! { /// The kind of file of an entry. #[derive(Copy, Clone, Debug, PartialEq, Eq)] @@ -79,6 +92,17 @@ mod access { pub fn stage(&self) -> entry::Stage { self.flags.stage() } + + /// Return an entry's stage as raw number between 0 and 4. + /// Possible values are: + /// + /// * 0 = no conflict, + /// * 1 = base, + /// * 2 = ours, + /// * 3 = theirs + pub fn stage_raw(&self) -> u32 { + self.flags.stage_raw() + } } } diff --git a/gix-index/src/entry/mode.rs b/gix-index/src/entry/mode.rs index fca861d2b18..dc3a9a6de94 100644 --- a/gix-index/src/entry/mode.rs +++ b/gix-index/src/entry/mode.rs @@ -67,6 +67,12 @@ impl Mode { } } +impl From for Mode { + fn from(value: gix_object::tree::EntryMode) -> Self { + Self::from_bits_truncate(value.0 as u32) + } +} + /// A change of a [`Mode`]. pub enum Change { /// The type of mode changed, like symlink => file. diff --git a/gix-index/src/entry/write.rs b/gix-index/src/entry/write.rs index 2a6ad55884c..66ef24d9a9e 100644 --- a/gix-index/src/entry/write.rs +++ b/gix-index/src/entry/write.rs @@ -1,5 +1,3 @@ -use std::convert::TryInto; - use crate::{entry, Entry, State}; impl Entry { diff --git a/gix-index/src/extension/decode.rs b/gix-index/src/extension/decode.rs index fa0a624104b..4114f8c8f20 100644 --- a/gix-index/src/extension/decode.rs +++ b/gix-index/src/extension/decode.rs @@ -1,5 +1,3 @@ -use std::convert::TryInto; - use crate::{extension, extension::Signature, util::from_be_u32}; pub(crate) fn header(data: &[u8]) -> (Signature, u32, &[u8]) { diff --git a/gix-index/src/extension/fs_monitor.rs b/gix-index/src/extension/fs_monitor.rs index 4bb5016ffe5..79d609dba19 100644 --- a/gix-index/src/extension/fs_monitor.rs +++ b/gix-index/src/extension/fs_monitor.rs @@ -6,6 +6,7 @@ use crate::{ }; #[derive(Clone)] +#[allow(dead_code)] pub enum Token { V1 { nanos_since_1970: u64 }, V2 { token: BString }, diff --git a/gix-index/src/extension/iter.rs b/gix-index/src/extension/iter.rs index f791b064eb4..ed0a457343d 100644 --- a/gix-index/src/extension/iter.rs +++ b/gix-index/src/extension/iter.rs @@ -1,5 +1,3 @@ -use std::convert::TryInto; - use crate::{extension, extension::Iter, util::from_be_u32}; impl<'a> Iter<'a> { diff --git a/gix-index/src/extension/link.rs b/gix-index/src/extension/link.rs index 722cbd17971..f2d52ca3d43 100644 --- a/gix-index/src/extension/link.rs +++ b/gix-index/src/extension/link.rs @@ -16,6 +16,7 @@ pub struct Bitmaps { } /// +#[allow(clippy::empty_docs)] pub mod decode { /// The error returned when decoding link extensions. diff --git a/gix-index/src/extension/mod.rs b/gix-index/src/extension/mod.rs index 33b91c5517b..3b51de6b20d 100644 --- a/gix-index/src/extension/mod.rs +++ b/gix-index/src/extension/mod.rs @@ -74,23 +74,29 @@ mod iter; pub(crate) mod fs_monitor; /// +#[allow(clippy::empty_docs)] pub mod decode; /// +#[allow(clippy::empty_docs)] pub mod tree; /// +#[allow(clippy::empty_docs)] pub mod end_of_index_entry; pub(crate) mod index_entry_offset_table; /// +#[allow(clippy::empty_docs)] pub mod link; pub(crate) mod resolve_undo; /// +#[allow(clippy::empty_docs)] pub mod untracked_cache; /// +#[allow(clippy::empty_docs)] pub mod sparse; diff --git a/gix-index/src/extension/tree/decode.rs b/gix-index/src/extension/tree/decode.rs index a55d12269a1..71e881ac38a 100644 --- a/gix-index/src/extension/tree/decode.rs +++ b/gix-index/src/extension/tree/decode.rs @@ -1,5 +1,3 @@ -use std::convert::TryInto; - use gix_hash::ObjectId; use crate::{ diff --git a/gix-index/src/extension/tree/mod.rs b/gix-index/src/extension/tree/mod.rs index f2075939978..8831530e590 100644 --- a/gix-index/src/extension/tree/mod.rs +++ b/gix-index/src/extension/tree/mod.rs @@ -4,6 +4,7 @@ use crate::extension::Signature; pub const SIGNATURE: Signature = *b"TREE"; /// +#[allow(clippy::empty_docs)] pub mod verify; mod decode; diff --git a/gix-index/src/extension/tree/write.rs b/gix-index/src/extension/tree/write.rs index 2b1e3d94950..354ff0d393e 100644 --- a/gix-index/src/extension/tree/write.rs +++ b/gix-index/src/extension/tree/write.rs @@ -1,5 +1,3 @@ -use std::convert::TryFrom; - use crate::extension::{tree, Tree}; impl Tree { diff --git a/gix-index/src/extension/untracked_cache.rs b/gix-index/src/extension/untracked_cache.rs index f3b2d8afdcb..c47d6883745 100644 --- a/gix-index/src/extension/untracked_cache.rs +++ b/gix-index/src/extension/untracked_cache.rs @@ -1,5 +1,3 @@ -use std::convert::TryInto; - use bstr::BString; use gix_hash::ObjectId; diff --git a/gix-index/src/file/init.rs b/gix-index/src/file/init.rs index 88dd86af9ac..10c01015fcb 100644 --- a/gix-index/src/file/init.rs +++ b/gix-index/src/file/init.rs @@ -2,8 +2,6 @@ use std::path::{Path, PathBuf}; -use memmap2::Mmap; - use crate::{decode, extension, File, State}; mod error { @@ -64,7 +62,7 @@ impl File { let mut file = std::fs::File::open(&path)?; // SAFETY: we have to take the risk of somebody changing the file underneath. Git never writes into the same file. #[allow(unsafe_code)] - let data = unsafe { Mmap::map(&file)? }; + let data = unsafe { memmap2::MmapOptions::new().map_copy_read_only(&file)? }; if !skip_hash { // Note that even though it's trivial to offload this into a thread, which is worth it for all but the smallest diff --git a/gix-index/src/file/mod.rs b/gix-index/src/file/mod.rs index 40332abbd0e..68f6e6268c7 100644 --- a/gix-index/src/file/mod.rs +++ b/gix-index/src/file/mod.rs @@ -83,8 +83,11 @@ mod mutation { } /// +#[allow(clippy::empty_docs)] pub mod init; /// +#[allow(clippy::empty_docs)] pub mod verify; /// +#[allow(clippy::empty_docs)] pub mod write; diff --git a/gix-index/src/fs.rs b/gix-index/src/fs.rs index a9acb155de2..5fb369f9eef 100644 --- a/gix-index/src/fs.rs +++ b/gix-index/src/fs.rs @@ -54,16 +54,16 @@ impl Metadata { pub fn modified(&self) -> Option { #[cfg(not(windows))] { - #[cfg(not(target_os = "aix"))] + #[cfg(not(any(target_os = "aix", target_os = "hurd")))] let seconds = self.0.st_mtime; - #[cfg(target_os = "aix")] + #[cfg(any(target_os = "aix", target_os = "hurd"))] let seconds = self.0.st_mtim.tv_sec; - #[cfg(not(any(target_os = "netbsd", target_os = "aix")))] + #[cfg(not(any(target_os = "netbsd", target_os = "aix", target_os = "hurd")))] let nanoseconds = self.0.st_mtime_nsec; #[cfg(target_os = "netbsd")] let nanoseconds = self.0.st_mtimensec; - #[cfg(target_os = "aix")] + #[cfg(any(target_os = "aix", target_os = "hurd"))] let nanoseconds = self.0.st_mtim.tv_nsec; // All operating systems treat the seconds as offset from unix epoch, hence it must @@ -83,16 +83,16 @@ impl Metadata { pub fn created(&self) -> Option { #[cfg(not(windows))] { - #[cfg(not(target_os = "aix"))] + #[cfg(not(any(target_os = "aix", target_os = "hurd")))] let seconds = self.0.st_ctime; - #[cfg(target_os = "aix")] + #[cfg(any(target_os = "aix", target_os = "hurd"))] let seconds = self.0.st_ctim.tv_sec; - #[cfg(not(any(target_os = "netbsd", target_os = "aix")))] + #[cfg(not(any(target_os = "netbsd", target_os = "aix", target_os = "hurd")))] let nanoseconds = self.0.st_ctime_nsec; #[cfg(target_os = "netbsd")] let nanoseconds = self.0.st_ctimensec; - #[cfg(target_os = "aix")] + #[cfg(any(target_os = "aix", target_os = "hurd"))] let nanoseconds = self.0.st_ctim.tv_nsec; // All operating systems treat the seconds as offset from unix epoch, hence it must diff --git a/gix-index/src/init.rs b/gix-index/src/init.rs index ecb1f0b13a8..1301df77e9e 100644 --- a/gix-index/src/init.rs +++ b/gix-index/src/init.rs @@ -1,4 +1,6 @@ -mod from_tree { +#[allow(clippy::empty_docs)] +/// +pub mod from_tree { use std::collections::VecDeque; use bstr::{BStr, BString, ByteSlice, ByteVec}; @@ -10,6 +12,19 @@ mod from_tree { Entry, PathStorage, State, Version, }; + /// The error returned by [State::from_tree()]. + #[derive(Debug, thiserror::Error)] + #[allow(missing_docs)] + pub enum Error { + #[error("The path \"{path}\" is invalid")] + InvalidComponent { + path: BString, + source: gix_validate::path::component::Error, + }, + #[error(transparent)] + Traversal(#[from] gix_traverse::tree::breadthfirst::Error), + } + /// Initialization impl State { /// Return a new and empty in-memory index assuming the given `object_hash`. @@ -32,23 +47,42 @@ mod from_tree { } /// Create an index [`State`] by traversing `tree` recursively, accessing sub-trees /// with `objects`. + /// `validate` is used to determine which validations to perform on every path component we see. /// /// **No extension data is currently produced**. - pub fn from_tree(tree: &gix_hash::oid, objects: Find) -> Result + pub fn from_tree( + tree: &gix_hash::oid, + objects: Find, + validate: gix_validate::path::component::Options, + ) -> Result where Find: gix_object::Find, { let _span = gix_features::trace::coarse!("gix_index::State::from_tree()"); let mut buf = Vec::new(); - let root = objects.find_tree_iter(tree, &mut buf)?; - let mut delegate = CollectEntries::new(); - breadthfirst(root, breadthfirst::State::default(), &objects, &mut delegate)?; + let root = objects + .find_tree_iter(tree, &mut buf) + .map_err(breadthfirst::Error::from)?; + let mut delegate = CollectEntries::new(validate); + match breadthfirst(root, breadthfirst::State::default(), &objects, &mut delegate) { + Ok(()) => {} + Err(gix_traverse::tree::breadthfirst::Error::Cancelled) => { + let (path, err) = delegate + .invalid_path + .take() + .expect("cancellation only happens on validation error"); + return Err(Error::InvalidComponent { path, source: err }); + } + Err(err) => return Err(err.into()), + } let CollectEntries { mut entries, path_backing, path: _, path_deque: _, + validate: _, + invalid_path: _, } = delegate; entries.sort_by(|a, b| Entry::cmp_filepaths(a.path_in(&path_backing), b.path_in(&path_backing))); @@ -76,15 +110,19 @@ mod from_tree { path_backing: PathStorage, path: BString, path_deque: VecDeque, + validate: gix_validate::path::component::Options, + invalid_path: Option<(BString, gix_validate::path::component::Error)>, } impl CollectEntries { - pub fn new() -> CollectEntries { + pub fn new(validate: gix_validate::path::component::Options) -> CollectEntries { CollectEntries { entries: Vec::new(), path_backing: Vec::new(), path: BString::default(), path_deque: VecDeque::new(), + validate, + invalid_path: None, } } @@ -93,6 +131,11 @@ mod from_tree { self.path.push(b'/'); } self.path.push_str(name); + if self.invalid_path.is_none() { + if let Err(err) = gix_validate::path::component(name, None, self.validate) { + self.invalid_path = Some((self.path.clone(), err)) + } + } } pub fn add_entry(&mut self, entry: &tree::EntryRef<'_>) { @@ -103,6 +146,18 @@ mod from_tree { EntryKind::Link => Mode::SYMLINK, EntryKind::Commit => Mode::COMMIT, }; + // There are leaf-names that require special validation, specific to their mode. + // Double-validate just for this case, as the previous validation didn't know the mode yet. + if self.invalid_path.is_none() { + let start = self.path.rfind_byte(b'/').map(|pos| pos + 1).unwrap_or_default(); + if let Err(err) = gix_validate::path::component( + self.path[start..].as_ref(), + (entry.mode.kind() == EntryKind::Link).then_some(gix_validate::path::component::Mode::Symlink), + self.validate, + ) { + self.invalid_path = Some((self.path.clone(), err)); + } + } let path_start = self.path_backing.len(); self.path_backing.extend_from_slice(&self.path); @@ -117,6 +172,14 @@ mod from_tree { self.entries.push(new_entry); } + + fn determine_action(&self) -> Action { + if self.invalid_path.is_none() { + Action::Continue + } else { + Action::Cancel + } + } } impl Visit for CollectEntries { @@ -127,12 +190,12 @@ mod from_tree { .expect("every call is matched with push_tracked_path_component"); } - fn push_back_tracked_path_component(&mut self, component: &bstr::BStr) { + fn push_back_tracked_path_component(&mut self, component: &BStr) { self.push_element(component); self.path_deque.push_back(self.path.clone()); } - fn push_path_component(&mut self, component: &bstr::BStr) { + fn push_path_component(&mut self, component: &BStr) { self.push_element(component); } @@ -144,13 +207,13 @@ mod from_tree { } } - fn visit_tree(&mut self, _entry: &gix_object::tree::EntryRef<'_>) -> gix_traverse::tree::visit::Action { - Action::Continue + fn visit_tree(&mut self, _entry: &gix_object::tree::EntryRef<'_>) -> Action { + self.determine_action() } - fn visit_nontree(&mut self, entry: &gix_object::tree::EntryRef<'_>) -> gix_traverse::tree::visit::Action { + fn visit_nontree(&mut self, entry: &gix_object::tree::EntryRef<'_>) -> Action { self.add_entry(entry); - Action::Continue + self.determine_action() } } } diff --git a/gix-index/src/lib.rs b/gix-index/src/lib.rs index 96a218c6355..1e157bdc0d5 100644 --- a/gix-index/src/lib.rs +++ b/gix-index/src/lib.rs @@ -13,25 +13,33 @@ use filetime::FileTime; pub use gix_hash as hash; /// +#[allow(clippy::empty_docs)] pub mod file; /// +#[allow(clippy::empty_docs)] pub mod extension; /// +#[allow(clippy::empty_docs)] pub mod entry; mod access; -mod init; +/// +#[allow(clippy::empty_docs)] +pub mod init; /// +#[allow(clippy::empty_docs)] pub mod decode; /// +#[allow(clippy::empty_docs)] pub mod verify; /// +#[allow(clippy::empty_docs)] pub mod write; pub mod fs; @@ -107,6 +115,21 @@ pub struct AccelerateLookup<'a> { /// /// As opposed to a snapshot, it's meant to be altered and eventually be written back to disk or converted into a tree. /// We treat index and its state synonymous. +/// +/// # A note on safety +/// +/// An index (i.e. [`State`]) created by hand is not guaranteed to have valid entry paths as they are entirely controlled +/// by the caller, without applying any level of validation. +/// +/// This means that before using these paths to recreate files on disk, *they must be validated*. +/// +/// It's notable that it's possible to manufacture tree objects which contain names like `.git/hooks/pre-commit` +/// which then will look like `.git/hooks/pre-commit` in the index, which doesn't care that the name came from a single +/// tree instead of from trees named `.git`, `hooks` and a blob named `pre-commit`. The effect is still the same - an invalid +/// path is presented in the index and its consumer must validate each path component before usage. +/// +/// It's recommended to do that using `gix_worktree::Stack` which has it built-in if it's created `for_checkout()`. Alternatively +/// one can validate component names with `gix_validate::path::component()`. #[derive(Clone)] pub struct State { /// The kind of object hash used when storing the underlying file. @@ -138,6 +161,7 @@ pub struct State { } mod impls { + use crate::entry::Stage; use std::fmt::{Debug, Formatter}; use crate::State; @@ -149,11 +173,10 @@ mod impls { f, "{} {}{:?} {} {}", match entry.flags.stage() { - 0 => " ", - 1 => "BASE ", - 2 => "OURS ", - 3 => "THEIRS ", - _ => "UNKNOWN", + Stage::Unconflicted => " ", + Stage::Base => "BASE ", + Stage::Ours => "OURS ", + Stage::Theirs => "THEIRS ", }, if entry.flags.is_empty() { "".to_string() @@ -171,8 +194,6 @@ mod impls { } pub(crate) mod util { - use std::convert::TryInto; - #[inline] pub fn var_int(data: &[u8]) -> Option<(u64, &[u8])> { let (num, consumed) = gix_features::decode::leb64_from_read(data).ok()?; diff --git a/gix-index/src/verify.rs b/gix-index/src/verify.rs index 776f13b3c5f..e01a26fe306 100644 --- a/gix-index/src/verify.rs +++ b/gix-index/src/verify.rs @@ -3,6 +3,7 @@ use std::cmp::Ordering; use crate::State; /// +#[allow(clippy::empty_docs)] pub mod entries { use bstr::BString; @@ -22,6 +23,7 @@ pub mod entries { } /// +#[allow(clippy::empty_docs)] pub mod extensions { use crate::extension; diff --git a/gix-index/src/write.rs b/gix-index/src/write.rs index 3e90d52e5be..d52ba640ee0 100644 --- a/gix-index/src/write.rs +++ b/gix-index/src/write.rs @@ -1,4 +1,4 @@ -use std::{convert::TryInto, io::Write}; +use std::io::Write; use crate::{entry, extension, write::util::CountBytes, State, Version}; @@ -183,8 +183,6 @@ fn entries(out: &mut CountBytes, state: &State, header_siz } mod util { - use std::convert::TryFrom; - pub struct CountBytes { pub count: u32, pub inner: T, diff --git a/gix-index/tests/Cargo.toml b/gix-index/tests/Cargo.toml index 4d15e5aed4f..8e5700826f5 100644 --- a/gix-index/tests/Cargo.toml +++ b/gix-index/tests/Cargo.toml @@ -19,8 +19,9 @@ gix-features-parallel = ["gix-features/parallel"] [dev-dependencies] gix-index = { path = ".." } gix-features = { path = "../../gix-features", features = ["rustsha1", "progress"] } -gix-testtools = { path = "../../tests/tools"} -gix = { path = "../../gix", default-features = false, features = ["index"] } -gix-hash = { path = "../../gix-hash"} +gix-testtools = { path = "../../tests/tools" } +gix-odb = { path = "../../gix-odb" } +gix-object = { path = "../../gix-object" } +gix-hash = { path = "../../gix-hash" } filetime = "0.2.15" bstr = { version = "1.3.0", default-features = false } diff --git a/gix-index/tests/fixtures/file_metadata.sh b/gix-index/tests/fixtures/file_metadata.sh old mode 100644 new mode 100755 index 81ca3b8a3fa..94b45fcc6ae --- a/gix-index/tests/fixtures/file_metadata.sh +++ b/gix-index/tests/fixtures/file_metadata.sh @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash set -eu -o pipefail # The largest-possible date for Ext4, nanos are special there, but ont usually on other filesystems diff --git a/gix-index/tests/fixtures/generated-archives/V2_empty.tar.xz b/gix-index/tests/fixtures/generated-archives/V2_empty.tar.xz index c3336ba9a2e..70ddca8c48b 100644 Binary files a/gix-index/tests/fixtures/generated-archives/V2_empty.tar.xz and b/gix-index/tests/fixtures/generated-archives/V2_empty.tar.xz differ diff --git a/gix-index/tests/fixtures/generated-archives/make_traverse_literal_separators.tar.xz b/gix-index/tests/fixtures/generated-archives/make_traverse_literal_separators.tar.xz new file mode 100644 index 00000000000..f7b13684d75 Binary files /dev/null and b/gix-index/tests/fixtures/generated-archives/make_traverse_literal_separators.tar.xz differ diff --git a/gix-index/tests/fixtures/generated-archives/v2.tar.xz b/gix-index/tests/fixtures/generated-archives/v2.tar.xz index 42e49dc2c72..143f40f7ddc 100644 Binary files a/gix-index/tests/fixtures/generated-archives/v2.tar.xz and b/gix-index/tests/fixtures/generated-archives/v2.tar.xz differ diff --git a/gix-index/tests/fixtures/generated-archives/v2_all_file_kinds.tar.xz b/gix-index/tests/fixtures/generated-archives/v2_all_file_kinds.tar.xz index 169e964ba09..a83ecec0af5 100644 Binary files a/gix-index/tests/fixtures/generated-archives/v2_all_file_kinds.tar.xz and b/gix-index/tests/fixtures/generated-archives/v2_all_file_kinds.tar.xz differ diff --git a/gix-index/tests/fixtures/generated-archives/v2_icase_name_clashes.tar.xz b/gix-index/tests/fixtures/generated-archives/v2_icase_name_clashes.tar.xz index 7f7a4a42aba..17845f85aec 100644 Binary files a/gix-index/tests/fixtures/generated-archives/v2_icase_name_clashes.tar.xz and b/gix-index/tests/fixtures/generated-archives/v2_icase_name_clashes.tar.xz differ diff --git a/gix-index/tests/fixtures/generated-archives/v2_more_files.tar.xz b/gix-index/tests/fixtures/generated-archives/v2_more_files.tar.xz index 4f257c10db9..68d730041aa 100644 Binary files a/gix-index/tests/fixtures/generated-archives/v2_more_files.tar.xz and b/gix-index/tests/fixtures/generated-archives/v2_more_files.tar.xz differ diff --git a/gix-index/tests/fixtures/generated-archives/v2_sparse_index_no_dirs.tar.xz b/gix-index/tests/fixtures/generated-archives/v2_sparse_index_no_dirs.tar.xz index dac194d03cf..556b8fa5a83 100644 Binary files a/gix-index/tests/fixtures/generated-archives/v2_sparse_index_no_dirs.tar.xz and b/gix-index/tests/fixtures/generated-archives/v2_sparse_index_no_dirs.tar.xz differ diff --git a/gix-index/tests/fixtures/generated-archives/v2_split_index.tar.xz b/gix-index/tests/fixtures/generated-archives/v2_split_index.tar.xz index c45f3fe336e..d666760516a 100644 Binary files a/gix-index/tests/fixtures/generated-archives/v2_split_index.tar.xz and b/gix-index/tests/fixtures/generated-archives/v2_split_index.tar.xz differ diff --git a/gix-index/tests/fixtures/generated-archives/v2_split_index_recursive.tar.xz b/gix-index/tests/fixtures/generated-archives/v2_split_index_recursive.tar.xz index 03c891025ec..971200d51b9 100644 Binary files a/gix-index/tests/fixtures/generated-archives/v2_split_index_recursive.tar.xz and b/gix-index/tests/fixtures/generated-archives/v2_split_index_recursive.tar.xz differ diff --git a/gix-index/tests/fixtures/generated-archives/v2_split_vs_regular_index.tar.xz b/gix-index/tests/fixtures/generated-archives/v2_split_vs_regular_index.tar.xz index 9d751efb419..4ba20b40755 100644 Binary files a/gix-index/tests/fixtures/generated-archives/v2_split_vs_regular_index.tar.xz and b/gix-index/tests/fixtures/generated-archives/v2_split_vs_regular_index.tar.xz differ diff --git a/gix-index/tests/fixtures/generated-archives/v3_added_files.tar.xz b/gix-index/tests/fixtures/generated-archives/v3_added_files.tar.xz index d9806bdfece..9859e878710 100644 Binary files a/gix-index/tests/fixtures/generated-archives/v3_added_files.tar.xz and b/gix-index/tests/fixtures/generated-archives/v3_added_files.tar.xz differ diff --git a/gix-index/tests/fixtures/generated-archives/v3_skip_worktree.tar.xz b/gix-index/tests/fixtures/generated-archives/v3_skip_worktree.tar.xz index aaeee1fa751..2aef44263a2 100644 Binary files a/gix-index/tests/fixtures/generated-archives/v3_skip_worktree.tar.xz and b/gix-index/tests/fixtures/generated-archives/v3_skip_worktree.tar.xz differ diff --git a/gix-index/tests/fixtures/generated-archives/v3_sparse_index.tar.xz b/gix-index/tests/fixtures/generated-archives/v3_sparse_index.tar.xz index 0ed0fa93108..58a29bb4174 100644 Binary files a/gix-index/tests/fixtures/generated-archives/v3_sparse_index.tar.xz and b/gix-index/tests/fixtures/generated-archives/v3_sparse_index.tar.xz differ diff --git a/gix-index/tests/fixtures/generated-archives/v3_sparse_index_non_cone.tar.xz b/gix-index/tests/fixtures/generated-archives/v3_sparse_index_non_cone.tar.xz index ee53ba29c48..1209c847522 100644 Binary files a/gix-index/tests/fixtures/generated-archives/v3_sparse_index_non_cone.tar.xz and b/gix-index/tests/fixtures/generated-archives/v3_sparse_index_non_cone.tar.xz differ diff --git a/gix-index/tests/fixtures/generated-archives/v4_more_files_IEOT.tar.xz b/gix-index/tests/fixtures/generated-archives/v4_more_files_IEOT.tar.xz index eb191c2d5fd..72c6cd5c119 100644 Binary files a/gix-index/tests/fixtures/generated-archives/v4_more_files_IEOT.tar.xz and b/gix-index/tests/fixtures/generated-archives/v4_more_files_IEOT.tar.xz differ diff --git a/gix-index/tests/fixtures/make_index/V2_empty.sh b/gix-index/tests/fixtures/make_index/V2_empty.sh index 1eb48b72b8c..d36f819498f 100755 --- a/gix-index/tests/fixtures/make_index/V2_empty.sh +++ b/gix-index/tests/fixtures/make_index/V2_empty.sh @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash set -eu -o pipefail git init -q diff --git a/gix-index/tests/fixtures/make_index/v2.sh b/gix-index/tests/fixtures/make_index/v2.sh index a61c9527d01..a39d25bea19 100755 --- a/gix-index/tests/fixtures/make_index/v2.sh +++ b/gix-index/tests/fixtures/make_index/v2.sh @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash set -eu -o pipefail export GIT_INDEX_VERSION=2 @@ -8,3 +8,5 @@ git config index.threads 2 touch a git add a git commit -m "empty" + +git rev-parse @^{tree} > head.tree diff --git a/gix-index/tests/fixtures/make_index/v2_all_file_kinds.sh b/gix-index/tests/fixtures/make_index/v2_all_file_kinds.sh index b7a4610da2f..0dae4a4027c 100755 --- a/gix-index/tests/fixtures/make_index/v2_all_file_kinds.sh +++ b/gix-index/tests/fixtures/make_index/v2_all_file_kinds.sh @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash set -eu -o pipefail export GIT_INDEX_VERSION=2; @@ -22,3 +22,5 @@ mkdir d git add . git commit -m "init" + +git rev-parse @^{tree} > head.tree diff --git a/gix-index/tests/fixtures/make_index/v2_deeper_tree.sh b/gix-index/tests/fixtures/make_index/v2_deeper_tree.sh old mode 100644 new mode 100755 index fe2f6fa1f57..09ba2a53464 --- a/gix-index/tests/fixtures/make_index/v2_deeper_tree.sh +++ b/gix-index/tests/fixtures/make_index/v2_deeper_tree.sh @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash set -eu -o pipefail export GIT_INDEX_VERSION=2; diff --git a/gix-index/tests/fixtures/make_index/v2_icase_name_clashes.sh b/gix-index/tests/fixtures/make_index/v2_icase_name_clashes.sh old mode 100644 new mode 100755 index 78ae8c4331f..426e53c14f7 --- a/gix-index/tests/fixtures/make_index/v2_icase_name_clashes.sh +++ b/gix-index/tests/fixtures/make_index/v2_icase_name_clashes.sh @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash set -eu -o pipefail git init -q diff --git a/gix-index/tests/fixtures/make_index/v2_more_files.sh b/gix-index/tests/fixtures/make_index/v2_more_files.sh index d4cafddc097..f4b90c95aa3 100755 --- a/gix-index/tests/fixtures/make_index/v2_more_files.sh +++ b/gix-index/tests/fixtures/make_index/v2_more_files.sh @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash set -eu -o pipefail export GIT_INDEX_VERSION=2; @@ -11,3 +11,5 @@ mkdir d git add . git commit -m "empty" + +git rev-parse @^{tree} > head.tree diff --git a/gix-index/tests/fixtures/make_index/v2_sparse_index_no_dirs.sh b/gix-index/tests/fixtures/make_index/v2_sparse_index_no_dirs.sh index 890483b4276..9a35bbfe03b 100755 --- a/gix-index/tests/fixtures/make_index/v2_sparse_index_no_dirs.sh +++ b/gix-index/tests/fixtures/make_index/v2_sparse_index_no_dirs.sh @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash set -eu -o pipefail git init -q @@ -17,4 +17,4 @@ git config --worktree index.sparse true echo "/*" > .git/info/sparse-checkout && echo "!/*/" >> .git/info/sparse-checkout -git checkout main \ No newline at end of file +git checkout main diff --git a/gix-index/tests/fixtures/make_index/v2_split_index.sh b/gix-index/tests/fixtures/make_index/v2_split_index.sh index 0b5c98e5ed8..8fe5b58ce9c 100755 --- a/gix-index/tests/fixtures/make_index/v2_split_index.sh +++ b/gix-index/tests/fixtures/make_index/v2_split_index.sh @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash set -eu -o pipefail export GIT_INDEX_VERSION=2 diff --git a/gix-index/tests/fixtures/make_index/v2_split_index_recursive.sh b/gix-index/tests/fixtures/make_index/v2_split_index_recursive.sh index 0763fbe5a66..02003737a92 100755 --- a/gix-index/tests/fixtures/make_index/v2_split_index_recursive.sh +++ b/gix-index/tests/fixtures/make_index/v2_split_index_recursive.sh @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash set -eu -o pipefail export GIT_INDEX_VERSION=2 diff --git a/gix-index/tests/fixtures/make_index/v2_split_vs_regular_index.sh b/gix-index/tests/fixtures/make_index/v2_split_vs_regular_index.sh index 162ff6a3ca5..fd985a3235f 100755 --- a/gix-index/tests/fixtures/make_index/v2_split_vs_regular_index.sh +++ b/gix-index/tests/fixtures/make_index/v2_split_vs_regular_index.sh @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash # Creates a 2 identical repositories, one using a split index, the other # using a regular index diff --git a/gix-index/tests/fixtures/make_index/v3_added_files.sh b/gix-index/tests/fixtures/make_index/v3_added_files.sh index 8d2e5179fd2..d85db90ac4a 100755 --- a/gix-index/tests/fixtures/make_index/v3_added_files.sh +++ b/gix-index/tests/fixtures/make_index/v3_added_files.sh @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash set -eu -o pipefail git init -q diff --git a/gix-index/tests/fixtures/make_index/v3_skip_worktree.sh b/gix-index/tests/fixtures/make_index/v3_skip_worktree.sh index 3ebe78e04fb..9c74bdace5b 100755 --- a/gix-index/tests/fixtures/make_index/v3_skip_worktree.sh +++ b/gix-index/tests/fixtures/make_index/v3_skip_worktree.sh @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash set -eu -o pipefail git init -q diff --git a/gix-index/tests/fixtures/make_index/v3_sparse_index.sh b/gix-index/tests/fixtures/make_index/v3_sparse_index.sh index 9e88fb03178..a4410f6073b 100755 --- a/gix-index/tests/fixtures/make_index/v3_sparse_index.sh +++ b/gix-index/tests/fixtures/make_index/v3_sparse_index.sh @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash set -eu -o pipefail git init -q diff --git a/gix-index/tests/fixtures/make_index/v3_sparse_index_non_cone.sh b/gix-index/tests/fixtures/make_index/v3_sparse_index_non_cone.sh index 542d9c8ba2d..670cb73cff5 100755 --- a/gix-index/tests/fixtures/make_index/v3_sparse_index_non_cone.sh +++ b/gix-index/tests/fixtures/make_index/v3_sparse_index_non_cone.sh @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash set -eu -o pipefail git init -q @@ -13,4 +13,4 @@ mkdir d git add . git commit -m "init" -git sparse-checkout set c1/c2 --no-cone \ No newline at end of file +git sparse-checkout set c1/c2 --no-cone diff --git a/gix-index/tests/fixtures/make_index/v4_more_files_IEOT.sh b/gix-index/tests/fixtures/make_index/v4_more_files_IEOT.sh index 3b22aaa6692..3bff00b94a7 100755 --- a/gix-index/tests/fixtures/make_index/v4_more_files_IEOT.sh +++ b/gix-index/tests/fixtures/make_index/v4_more_files_IEOT.sh @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash set -eu -o pipefail export GIT_INDEX_VERSION=4 @@ -12,3 +12,5 @@ touch x git add . git commit -m "empty" + +git rev-parse @^{tree} > head.tree diff --git a/gix-index/tests/fixtures/make_traverse_literal_separators.sh b/gix-index/tests/fixtures/make_traverse_literal_separators.sh new file mode 100644 index 00000000000..a310e9e1c91 --- /dev/null +++ b/gix-index/tests/fixtures/make_traverse_literal_separators.sh @@ -0,0 +1,44 @@ +#!/usr/bin/env bash +set -eu -o pipefail + +# Makes a repo carrying a literally named file, which may even contain "/". +# File content is from stdin. Arguments are repo name, file name, and file mode. +function make_repo() ( + local repo="$1" file="$2" mode="$3" + local blob_hash_escaped tree_hash commit_hash branch + + git init -- "$repo" + cd -- "$repo" # Temporary, as the function body is a ( ) subshell. + + blob_hash_escaped="$(git hash-object -w --stdin | sed 's/../\\x&/g')" + + tree_hash="$( + printf "%s %s\\0$blob_hash_escaped" "$mode" "$file" | + git hash-object -t tree -w --stdin --literally + )" + + commit_hash="$(git commit-tree -m 'Initial commit' "$tree_hash")" + + branch="$(git symbolic-ref --short HEAD)" + git branch -f -- "$branch" "$commit_hash" + test -z "${DEBUG_FIXTURE-}" || git show # TODO: How should verbosity be controlled? + git rev-parse @^{tree} > head.tree +) + +make_repo traverse_dotdot_slashes ../outside 100644 \ + <<<'A file outside the working tree, somehow.' + +make_repo traverse_dotgit_slashes .git/hooks/pre-commit 100755 <<'EOF' +#!/bin/sh +printf 'Vulnerable!\n' +date >vulnerable +EOF + +make_repo traverse_dotdot_backslashes '..\outside' 100644 \ + <<<'A file outside the working tree, somehow.' + +make_repo traverse_dotgit_backslashes '.git\hooks\pre-commit' 100755 <<'EOF' +#!/bin/sh +printf 'Vulnerable!\n' +date >vulnerable +EOF diff --git a/gix-index/tests/index/access.rs b/gix-index/tests/index/access.rs index af76a45a295..d3e720486a4 100644 --- a/gix-index/tests/index/access.rs +++ b/gix-index/tests/index/access.rs @@ -1,5 +1,6 @@ use crate::index::Fixture; use bstr::{BString, ByteSlice}; +use gix_index::entry::Stage; fn icase_fixture() -> gix_index::File { Fixture::Generated("v2_icase_name_clashes").open() @@ -11,7 +12,7 @@ fn entry_by_path() { for entry in file.entries() { let path = entry.path(&file); assert_eq!(file.entry_by_path(path), Some(entry)); - assert_eq!(file.entry_by_path_and_stage(path, 0), Some(entry)); + assert_eq!(file.entry_by_path_and_stage(path, Stage::Unconflicted), Some(entry)); } } @@ -140,27 +141,27 @@ fn entry_by_path_and_stage() { for entry in file.entries() { let path = entry.path(&file); assert_eq!( - file.entry_index_by_path_and_stage(path, 0) + file.entry_index_by_path_and_stage(path, Stage::Unconflicted) .map(|idx| &file.entries()[idx]), Some(entry) ); - assert_eq!(file.entry_by_path_and_stage(path, 0), Some(entry)); + assert_eq!(file.entry_by_path_and_stage(path, Stage::Unconflicted), Some(entry)); } } #[test] fn entry_by_path_with_conflicting_file() { let file = Fixture::Loose("conflicting-file").open(); - for expected_stage in [1 /* common ancestor */, 2 /* ours */, 3 /* theirs */] { + for expected_stage in [Stage::Base, Stage::Ours, Stage::Theirs] { assert!( file.entry_by_path_and_stage("file".into(), expected_stage).is_some(), - "we have no stage 0 during a conflict, but all other ones. Missed {expected_stage}" + "we have no stage 0 during a conflict, but all other ones. Missed {expected_stage:?}" ); } assert_eq!( file.entry_by_path("file".into()).expect("found").stage(), - 2, + Stage::Ours, "we always find our stage while in a merge" ); } @@ -226,13 +227,13 @@ fn sort_entries() { for (idx, entry) in file.entries()[..valid_entries].iter().enumerate() { assert_eq!( - file.entry_index_by_path_and_stage_bounded(entry.path(&file), 0, valid_entries), + file.entry_index_by_path_and_stage_bounded(entry.path(&file), Stage::Unconflicted, valid_entries), Some(idx), "we can still find entries in the correctly sorted region" ); } assert_eq!( - file.entry_by_path_and_stage(new_entry_path, 0), + file.entry_by_path_and_stage(new_entry_path, Stage::Unconflicted), None, "new entry can't be found due to incorrect order" ); @@ -241,7 +242,7 @@ fn sort_entries() { assert!(file.verify_entries().is_ok(), "sorting of entries restores invariants"); assert_eq!( - file.entry_by_path_and_stage(new_entry_path, 0) + file.entry_by_path_and_stage(new_entry_path, Stage::Unconflicted) .expect("can be found") .path(&file), new_entry_path, diff --git a/gix-index/tests/index/file/read.rs b/gix-index/tests/index/file/read.rs index 53387266f4c..bf1804384be 100644 --- a/gix-index/tests/index/file/read.rs +++ b/gix-index/tests/index/file/read.rs @@ -11,7 +11,7 @@ use crate::{hex_to_id, index::Fixture, loose_file_path}; fn verify(index: gix_index::File) -> gix_index::File { index.verify_integrity().unwrap(); index.verify_entries().unwrap(); - index.verify_extensions(false, gix::objs::find::Never).unwrap(); + index.verify_extensions(false, gix_object::find::Never).unwrap(); index } diff --git a/gix-index/tests/index/file/write.rs b/gix-index/tests/index/file/write.rs index a01963c8e31..10d924b4e21 100644 --- a/gix-index/tests/index/file/write.rs +++ b/gix-index/tests/index/file/write.rs @@ -228,7 +228,7 @@ fn compare_states_against_baseline( fn compare_states(actual: &State, actual_version: Version, expected: &State, options: Options, fixture: &str) { actual.verify_entries().expect("valid"); - actual.verify_extensions(false, gix::objs::find::Never).expect("valid"); + actual.verify_extensions(false, gix_object::find::Never).expect("valid"); assert_eq!( actual.version(), diff --git a/gix-index/tests/index/init.rs b/gix-index/tests/index/init.rs index 22595526605..cc3b0494119 100644 --- a/gix-index/tests/index/init.rs +++ b/gix-index/tests/index/init.rs @@ -1,5 +1,7 @@ use gix_index::State; use gix_testtools::scripted_fixture_read_only_standalone; +use std::error::Error; +use std::path::Path; #[test] fn from_tree() -> crate::Result { @@ -11,19 +13,45 @@ fn from_tree() -> crate::Result { ]; for fixture in fixtures { - let repo_dir = scripted_fixture_read_only_standalone(fixture)?; - let repo = gix::open(&repo_dir)?; + let worktree_dir = scripted_fixture_read_only_standalone(fixture)?; - let tree_id = repo.head_commit()?.tree_id()?; + let tree_id = tree_id(&worktree_dir); - let expected_state = repo.index()?; - let actual_state = State::from_tree(&tree_id, &repo.objects)?; + let git_dir = worktree_dir.join(".git"); + let expected_state = + gix_index::File::at(git_dir.join("index"), gix_hash::Kind::Sha1, false, Default::default())?; + let odb = gix_odb::at(git_dir.join("objects"))?; + let actual_state = State::from_tree(&tree_id, &odb, Default::default())?; compare_states(&actual_state, &expected_state, fixture) } Ok(()) } +#[test] +fn from_tree_validation() -> crate::Result { + let root = scripted_fixture_read_only_standalone("make_traverse_literal_separators.sh")?; + for repo_name in [ + "traverse_dotdot_slashes", + "traverse_dotgit_slashes", + "traverse_dotgit_backslashes", + "traverse_dotdot_backslashes", + ] { + let worktree_dir = root.join(repo_name); + let tree_id = tree_id(&worktree_dir); + let git_dir = worktree_dir.join(".git"); + let odb = gix_odb::at(git_dir.join("objects"))?; + + let err = State::from_tree(&tree_id, &odb, Default::default()).unwrap_err(); + assert_eq!( + err.source().expect("inner").to_string(), + "Path separators like / or \\ are not allowed", + "Note that this effectively tests what would happen on Windows, where \\ also isn't allowed" + ); + } + Ok(()) +} + #[test] fn new() { let state = State::new(gix_hash::Kind::Sha1); @@ -34,7 +62,7 @@ fn new() { fn compare_states(actual: &State, expected: &State, fixture: &str) { actual.verify_entries().expect("valid"); - actual.verify_extensions(false, gix::objs::find::Never).expect("valid"); + actual.verify_extensions(false, gix_object::find::Never).expect("valid"); assert_eq!( actual.entries().len(), @@ -49,3 +77,9 @@ fn compare_states(actual: &State, expected: &State, fixture: &str) { assert_eq!(a.path(actual), e.path(expected), "entry path mismatch in {fixture:?}"); } } + +fn tree_id(root: &Path) -> gix_hash::ObjectId { + let hex_hash = + std::fs::read_to_string(root.join("head.tree")).expect("head.tree was created by git rev-parse @^{tree}"); + hex_hash.trim().parse().expect("valid hash") +} diff --git a/gix-lock/CHANGELOG.md b/gix-lock/CHANGELOG.md index b4366a01354..0d211364a87 100644 --- a/gix-lock/CHANGELOG.md +++ b/gix-lock/CHANGELOG.md @@ -5,6 +5,56 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## 14.0.0 (2024-05-22) + +A maintenance release without user-facing changes. + +### Commit Statistics + + + + - 1 commit contributed to the release. + - 68 days passed between releases. + - 0 commits were understood as [conventional](https://www.conventionalcommits.org). + - 0 issues like '(#ID)' were seen in commit messages + +### Commit Details + + + +
view details + + * **Uncategorized** + - Adjust changelogs prior to release ([`9511416`](https://github.com/Byron/gitoxide/commit/9511416a6cd0c571233f958c165329c8705c2498)) +
+ +## 13.1.1 (2024-03-14) + +A maintenance release without user-facing changes. + +### Commit Statistics + + + + - 5 commits contributed to the release over the course of 37 calendar days. + - 37 days passed between releases. + - 0 commits were understood as [conventional](https://www.conventionalcommits.org). + - 0 issues like '(#ID)' were seen in commit messages + +### Commit Details + + + +
view details + + * **Uncategorized** + - Release gix-date v0.8.5, gix-hash v0.14.2, gix-trace v0.1.8, gix-utils v0.1.11, gix-features v0.38.1, gix-actor v0.31.0, gix-validate v0.8.4, gix-object v0.42.0, gix-path v0.10.7, gix-glob v0.16.2, gix-quote v0.4.12, gix-attributes v0.22.2, gix-command v0.3.6, gix-filter v0.11.0, gix-fs v0.10.1, gix-chunk v0.4.8, gix-commitgraph v0.24.2, gix-hashtable v0.5.2, gix-revwalk v0.13.0, gix-traverse v0.38.0, gix-worktree-stream v0.11.0, gix-archive v0.11.0, gix-config-value v0.14.6, gix-tempfile v13.1.1, gix-lock v13.1.1, gix-ref v0.43.0, gix-sec v0.10.6, gix-config v0.36.0, gix-prompt v0.8.4, gix-url v0.27.2, gix-credentials v0.24.2, gix-ignore v0.11.2, gix-bitmap v0.2.11, gix-index v0.31.0, gix-worktree v0.32.0, gix-diff v0.42.0, gix-discover v0.31.0, gix-pathspec v0.7.1, gix-dir v0.2.0, gix-macros v0.1.4, gix-mailmap v0.23.0, gix-negotiate v0.13.0, gix-pack v0.49.0, gix-odb v0.59.0, gix-packetline v0.17.4, gix-transport v0.41.2, gix-protocol v0.44.2, gix-revision v0.27.0, gix-refspec v0.23.0, gix-status v0.7.0, gix-submodule v0.10.0, gix-worktree-state v0.9.0, gix v0.60.0, safety bump 26 crates ([`b050327`](https://github.com/Byron/gitoxide/commit/b050327e76f234b19be921b78b7b28e034319fdb)) + - Prepare changelogs prior to release ([`52c3bbd`](https://github.com/Byron/gitoxide/commit/52c3bbd36b9e94a0f3a78b4ada84d0c08eba27f6)) + - Merge branch 'status' ([`3e5c974`](https://github.com/Byron/gitoxide/commit/3e5c974dd62ac134711c6c2f5a5490187a6ea55e)) + - Fix lints for nightly, and clippy ([`f8ce3d0`](https://github.com/Byron/gitoxide/commit/f8ce3d0721b6a53713a9392f2451874f520bc44c)) + - Merge branch 'tempfile-permissions' ([`7b44c7f`](https://github.com/Byron/gitoxide/commit/7b44c7ff1dc0b8875214d2673c7f52948cf04ff0)) +
+ ## 13.1.0 (2024-02-06) ### New Features @@ -22,7 +72,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - - 2 commits contributed to the release. + - 3 commits contributed to the release. - 17 days passed between releases. - 2 commits were understood as [conventional](https://www.conventionalcommits.org). - 0 issues like '(#ID)' were seen in commit messages @@ -34,6 +84,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
view details * **Uncategorized** + - Release gix-tempfile v13.1.0, gix-lock v13.1.0, safety bump 12 crates ([`8430442`](https://github.com/Byron/gitoxide/commit/84304427dfe4d170c7732161b126961719f70059)) - Add `*_with_permissions(..., permissions)` methods. ([`719fc81`](https://github.com/Byron/gitoxide/commit/719fc81fa72756faa13fc3f8d4759df5d40cebd0)) - When obtaining locks, use mode 666 on Linux by default. ([`d83e45d`](https://github.com/Byron/gitoxide/commit/d83e45de625587b7b11e376a4d608854027a723e))
@@ -68,12 +119,12 @@ A maintenance release without user-facing changes. ### Chore - - change `rust-version` manifest field back to 1.65. - They didn't actually need to be higher to work, and changing them - unecessarily can break downstream CI. - - Let's keep this value as low as possible, and only increase it when - more recent features are actually used. +- change `rust-version` manifest field back to 1.65. + They didn't actually need to be higher to work, and changing them + unecessarily can break downstream CI. + + Let's keep this value as low as possible, and only increase it when + more recent features are actually used. ### Commit Statistics @@ -101,8 +152,8 @@ A maintenance release without user-facing changes. ### Chore - - upgrade MSRV to v1.70 - Our MSRV follows the one of `helix`, which in turn follows Firefox. +- upgrade MSRV to v1.70 + Our MSRV follows the one of `helix`, which in turn follows Firefox. ### Commit Statistics @@ -189,7 +240,7 @@ A maintenance release without user-facing changes. ### Other - - make clear that locks can leak and what that means on acquiring a lock +- make clear that locks can leak and what that means on acquiring a lock ### Commit Statistics @@ -337,7 +388,7 @@ A maintenance release without user-facing changes. ### Chore - - Add `clippy::redundant-closure-for-method-calls` lint +- Add `clippy::redundant-closure-for-method-calls` lint ### Commit Statistics @@ -454,10 +505,10 @@ A maintenance release without user-facing changes. ### Chore - - replace `quick-error` with `thiserror` - This increases the compile time of the crate alone if there is no proc-macro - in the dependency tree, but will ever so slightly improve compile times for `gix` - as a whole. +- replace `quick-error` with `thiserror` + This increases the compile time of the crate alone if there is no proc-macro + in the dependency tree, but will ever so slightly improve compile times for `gix` + as a whole. ### Commit Statistics @@ -529,8 +580,8 @@ A maintenance release without user-facing changes. ### Chore - - uniformize deny attributes - - remove default link to cargo doc everywhere +- uniformize deny attributes +- remove default link to cargo doc everywhere ### Documentation @@ -748,8 +799,8 @@ A maintenance release without user-facing changes. ### Chore - - uniformize deny attributes - - remove default link to cargo doc everywhere +- uniformize deny attributes +- remove default link to cargo doc everywhere ## 2.1.0 (2022-05-18) diff --git a/gix-lock/Cargo.toml b/gix-lock/Cargo.toml index 238f0792e93..ad769a9e102 100644 --- a/gix-lock/Cargo.toml +++ b/gix-lock/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "gix-lock" -version = "13.1.0" +version = "14.0.0" repository = "https://github.com/Byron/gitoxide" license = "MIT OR Apache-2.0" description = "A git-style lock-file implementation" @@ -14,8 +14,8 @@ doctest = false test = true [dependencies] -gix-utils = { version = "^0.1.9", default-features = false, path = "../gix-utils" } -gix-tempfile = { version = "^13.0.0", default-features = false, path = "../gix-tempfile" } +gix-utils = { version = "^0.1.11", default-features = false, path = "../gix-utils" } +gix-tempfile = { version = "^14.0.0", default-features = false, path = "../gix-tempfile" } thiserror = "1.0.38" [dev-dependencies] diff --git a/gix-lock/src/lib.rs b/gix-lock/src/lib.rs index f07d48350cc..04cf1766fdb 100644 --- a/gix-lock/src/lib.rs +++ b/gix-lock/src/lib.rs @@ -24,10 +24,12 @@ use gix_tempfile::handle::{Closed, Writable}; const DOT_LOCK_SUFFIX: &str = ".lock"; /// +#[allow(clippy::empty_docs)] pub mod acquire; pub use gix_utils::backoff; /// +#[allow(clippy::empty_docs)] pub mod commit; /// Locks a resource to eventually be overwritten with the content of this file. @@ -53,4 +55,5 @@ pub struct Marker { } /// +#[allow(clippy::empty_docs)] pub mod file; diff --git a/gix-macros/CHANGELOG.md b/gix-macros/CHANGELOG.md index 8f4635b29ce..d6e15af9499 100644 --- a/gix-macros/CHANGELOG.md +++ b/gix-macros/CHANGELOG.md @@ -5,22 +5,75 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## 0.1.5 (2024-05-22) + +A maintenance release without user-facing changes. + +### Commit Statistics + + + + - 3 commits contributed to the release over the course of 8 calendar days. + - 68 days passed between releases. + - 0 commits were understood as [conventional](https://www.conventionalcommits.org). + - 0 issues like '(#ID)' were seen in commit messages + +### Commit Details + + + +
view details + + * **Uncategorized** + - Adjust changelogs prior to release ([`9511416`](https://github.com/Byron/gitoxide/commit/9511416a6cd0c571233f958c165329c8705c2498)) + - Merge branch 'status' ([`04ef31e`](https://github.com/Byron/gitoxide/commit/04ef31e9d6f5332d49037a5a4c248ebbb5aaf92b)) + - Adapt to changes in `gix-pack` ([`bad5b48`](https://github.com/Byron/gitoxide/commit/bad5b48e4f0d865b0b0937f136d9a0041aa88046)) +
+ +## 0.1.4 (2024-03-14) + +A maintenance release without user-facing changes. + +### Commit Statistics + + + + - 4 commits contributed to the release over the course of 4 calendar days. + - 75 days passed between releases. + - 0 commits were understood as [conventional](https://www.conventionalcommits.org). + - 0 issues like '(#ID)' were seen in commit messages + +### Commit Details + + + +
view details + + * **Uncategorized** + - Release gix-date v0.8.5, gix-hash v0.14.2, gix-trace v0.1.8, gix-utils v0.1.11, gix-features v0.38.1, gix-actor v0.31.0, gix-validate v0.8.4, gix-object v0.42.0, gix-path v0.10.7, gix-glob v0.16.2, gix-quote v0.4.12, gix-attributes v0.22.2, gix-command v0.3.6, gix-filter v0.11.0, gix-fs v0.10.1, gix-chunk v0.4.8, gix-commitgraph v0.24.2, gix-hashtable v0.5.2, gix-revwalk v0.13.0, gix-traverse v0.38.0, gix-worktree-stream v0.11.0, gix-archive v0.11.0, gix-config-value v0.14.6, gix-tempfile v13.1.1, gix-lock v13.1.1, gix-ref v0.43.0, gix-sec v0.10.6, gix-config v0.36.0, gix-prompt v0.8.4, gix-url v0.27.2, gix-credentials v0.24.2, gix-ignore v0.11.2, gix-bitmap v0.2.11, gix-index v0.31.0, gix-worktree v0.32.0, gix-diff v0.42.0, gix-discover v0.31.0, gix-pathspec v0.7.1, gix-dir v0.2.0, gix-macros v0.1.4, gix-mailmap v0.23.0, gix-negotiate v0.13.0, gix-pack v0.49.0, gix-odb v0.59.0, gix-packetline v0.17.4, gix-transport v0.41.2, gix-protocol v0.44.2, gix-revision v0.27.0, gix-refspec v0.23.0, gix-status v0.7.0, gix-submodule v0.10.0, gix-worktree-state v0.9.0, gix v0.60.0, safety bump 26 crates ([`b050327`](https://github.com/Byron/gitoxide/commit/b050327e76f234b19be921b78b7b28e034319fdb)) + - Prepare changelogs prior to release ([`52c3bbd`](https://github.com/Byron/gitoxide/commit/52c3bbd36b9e94a0f3a78b4ada84d0c08eba27f6)) + - Merge branch 'status' ([`3e5c974`](https://github.com/Byron/gitoxide/commit/3e5c974dd62ac134711c6c2f5a5490187a6ea55e)) + - Fix lints for nightly, and clippy ([`f8ce3d0`](https://github.com/Byron/gitoxide/commit/f8ce3d0721b6a53713a9392f2451874f520bc44c)) +
+ ## 0.1.3 (2023-12-30) + + ### Chore - - change `rust-version` manifest field back to 1.65. - They didn't actually need to be higher to work, and changing them - unecessarily can break downstream CI. - - Let's keep this value as low as possible, and only increase it when - more recent features are actually used. +- change `rust-version` manifest field back to 1.65. + They didn't actually need to be higher to work, and changing them + unecessarily can break downstream CI. + + Let's keep this value as low as possible, and only increase it when + more recent features are actually used. ### Commit Statistics - - 2 commits contributed to the release. + - 3 commits contributed to the release. - 1 commit was understood as [conventional](https://www.conventionalcommits.org). - 0 issues like '(#ID)' were seen in commit messages @@ -31,6 +84,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
view details * **Uncategorized** + - Release gix-date v0.8.3, gix-hash v0.14.1, gix-trace v0.1.6, gix-features v0.37.1, gix-actor v0.29.1, gix-validate v0.8.3, gix-object v0.40.1, gix-path v0.10.3, gix-glob v0.15.1, gix-quote v0.4.10, gix-attributes v0.21.1, gix-command v0.3.2, gix-packetline-blocking v0.17.2, gix-utils v0.1.8, gix-filter v0.8.1, gix-fs v0.9.1, gix-chunk v0.4.7, gix-commitgraph v0.23.1, gix-hashtable v0.5.1, gix-revwalk v0.11.1, gix-traverse v0.36.1, gix-worktree-stream v0.8.1, gix-archive v0.8.1, gix-config-value v0.14.3, gix-tempfile v12.0.1, gix-lock v12.0.1, gix-ref v0.40.1, gix-sec v0.10.3, gix-config v0.33.1, gix-prompt v0.8.2, gix-url v0.26.1, gix-credentials v0.23.1, gix-ignore v0.10.1, gix-bitmap v0.2.10, gix-index v0.28.1, gix-worktree v0.29.1, gix-diff v0.39.1, gix-discover v0.28.1, gix-macros v0.1.3, gix-mailmap v0.21.1, gix-negotiate v0.11.1, gix-pack v0.46.1, gix-odb v0.56.1, gix-pathspec v0.5.1, gix-packetline v0.17.2, gix-transport v0.40.1, gix-protocol v0.43.1, gix-revision v0.25.1, gix-refspec v0.21.1, gix-status v0.4.1, gix-submodule v0.7.1, gix-worktree-state v0.6.1, gix v0.57.1 ([`972241f`](https://github.com/Byron/gitoxide/commit/972241f1904944e8b6e84c6aa1649a49be7a85c3)) - Merge branch 'msrv' ([`8c492d7`](https://github.com/Byron/gitoxide/commit/8c492d7b7e6e5d520b1e3ffeb489eeb88266aa75)) - Change `rust-version` manifest field back to 1.65. ([`3bd09ef`](https://github.com/Byron/gitoxide/commit/3bd09ef120945a9669321ea856db4079a5dab930))
@@ -41,8 +95,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Chore - - upgrade MSRV to v1.70 - Our MSRV follows the one of `helix`, which in turn follows Firefox. +- upgrade MSRV to v1.70 + Our MSRV follows the one of `helix`, which in turn follows Firefox. ### Commit Statistics diff --git a/gix-macros/Cargo.toml b/gix-macros/Cargo.toml index 6efc37ac6e5..78849181bc1 100644 --- a/gix-macros/Cargo.toml +++ b/gix-macros/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "gix-macros" -version = "0.1.3" +version = "0.1.5" edition = "2021" description = "Proc-macro utilities for gix" authors = [ @@ -14,7 +14,7 @@ include = ["src/**/*", "LICENSE-*"] rust-version = "1.65" [lib] -proc_macro = true +proc-macro = true [dependencies] syn = { version = "2.0", features = ["full", "fold"] } diff --git a/gix-macros/src/momo.rs b/gix-macros/src/momo.rs index 802f4b5b024..74b1636d30c 100644 --- a/gix-macros/src/momo.rs +++ b/gix-macros/src/momo.rs @@ -98,18 +98,18 @@ pub(crate) fn inner(code: proc_macro2::TokenStream) -> proc_macro2::TokenStream #[derive(Copy, Clone)] // All conversions we support. Check references to this type for an idea how to add more. -enum Conversion<'a> { - Into(&'a Type), - AsRef(&'a Type), - AsMut(&'a Type), +enum Conversion { + Into, + AsRef, + AsMut, } -impl<'a> Conversion<'a> { +impl Conversion { fn conversion_expr(&self, i: &Ident) -> Expr { match *self { - Conversion::Into(_) => parse_quote!(#i.into()), - Conversion::AsRef(_) => parse_quote!(#i.as_ref()), - Conversion::AsMut(_) => parse_quote!(#i.as_mut()), + Conversion::Into => parse_quote!(#i.into()), + Conversion::AsRef => parse_quote!(#i.as_ref()), + Conversion::AsMut => parse_quote!(#i.as_mut()), } } } @@ -128,13 +128,13 @@ fn parse_bounds(bounds: &Punctuated) -> Option) -> Option HashMap> { +fn parse_generics(decl: &Signature) -> HashMap { let mut ty_conversions = HashMap::new(); for gp in decl.generics.params.iter() { if let GenericParam::Type(ref tp) = gp { @@ -167,9 +167,9 @@ fn parse_generics(decl: &Signature) -> HashMap> { ty_conversions } -fn convert<'a>( - inputs: &'a Punctuated, - ty_conversions: &HashMap>, +fn convert( + inputs: &Punctuated, + ty_conversions: &HashMap, ) -> (bool, Punctuated, Punctuated, bool) { let mut has_conversion_in_effect = false; let mut argtypes = Punctuated::new(); @@ -212,7 +212,7 @@ fn convert<'a>( if let Some(conv) = parse_bounds(bounds) { has_conversion_in_effect = true; argexprs.push(conv.conversion_expr(ident)); - if let Conversion::AsMut(_) = conv { + if let Conversion::AsMut = conv { pat_ident.mutability = Some(Default::default()); } } else { @@ -223,7 +223,7 @@ fn convert<'a>( if let Some(conv) = parse_bounded_type(&pat_type.ty).and_then(|ident| ty_conversions.get(&ident)) { has_conversion_in_effect = true; argexprs.push(conv.conversion_expr(ident)); - if let Conversion::AsMut(_) = conv { + if let Conversion::AsMut = conv { pat_ident.mutability = Some(Default::default()); } } else { diff --git a/gix-mailmap/CHANGELOG.md b/gix-mailmap/CHANGELOG.md index 2df4946d4d1..13043046d0c 100644 --- a/gix-mailmap/CHANGELOG.md +++ b/gix-mailmap/CHANGELOG.md @@ -5,6 +5,56 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## 0.23.1 (2024-05-22) + +A maintenance release without user-facing changes. + +### Commit Statistics + + + + - 2 commits contributed to the release over the course of 8 calendar days. + - 68 days passed between releases. + - 0 commits were understood as [conventional](https://www.conventionalcommits.org). + - 0 issues like '(#ID)' were seen in commit messages + +### Commit Details + + + +
view details + + * **Uncategorized** + - Adjust changelogs prior to release ([`9511416`](https://github.com/Byron/gitoxide/commit/9511416a6cd0c571233f958c165329c8705c2498)) + - Release gix-date v0.8.6 ([`d3588ca`](https://github.com/Byron/gitoxide/commit/d3588ca4fe0364c88e42cdac24ceae548355d99d)) +
+ +## 0.23.0 (2024-03-14) + +A maintenance release without user-facing changes. + +### Commit Statistics + + + + - 4 commits contributed to the release over the course of 4 calendar days. + - 54 days passed between releases. + - 0 commits were understood as [conventional](https://www.conventionalcommits.org). + - 0 issues like '(#ID)' were seen in commit messages + +### Commit Details + + + +
view details + + * **Uncategorized** + - Release gix-date v0.8.5, gix-hash v0.14.2, gix-trace v0.1.8, gix-utils v0.1.11, gix-features v0.38.1, gix-actor v0.31.0, gix-validate v0.8.4, gix-object v0.42.0, gix-path v0.10.7, gix-glob v0.16.2, gix-quote v0.4.12, gix-attributes v0.22.2, gix-command v0.3.6, gix-filter v0.11.0, gix-fs v0.10.1, gix-chunk v0.4.8, gix-commitgraph v0.24.2, gix-hashtable v0.5.2, gix-revwalk v0.13.0, gix-traverse v0.38.0, gix-worktree-stream v0.11.0, gix-archive v0.11.0, gix-config-value v0.14.6, gix-tempfile v13.1.1, gix-lock v13.1.1, gix-ref v0.43.0, gix-sec v0.10.6, gix-config v0.36.0, gix-prompt v0.8.4, gix-url v0.27.2, gix-credentials v0.24.2, gix-ignore v0.11.2, gix-bitmap v0.2.11, gix-index v0.31.0, gix-worktree v0.32.0, gix-diff v0.42.0, gix-discover v0.31.0, gix-pathspec v0.7.1, gix-dir v0.2.0, gix-macros v0.1.4, gix-mailmap v0.23.0, gix-negotiate v0.13.0, gix-pack v0.49.0, gix-odb v0.59.0, gix-packetline v0.17.4, gix-transport v0.41.2, gix-protocol v0.44.2, gix-revision v0.27.0, gix-refspec v0.23.0, gix-status v0.7.0, gix-submodule v0.10.0, gix-worktree-state v0.9.0, gix v0.60.0, safety bump 26 crates ([`b050327`](https://github.com/Byron/gitoxide/commit/b050327e76f234b19be921b78b7b28e034319fdb)) + - Prepare changelogs prior to release ([`52c3bbd`](https://github.com/Byron/gitoxide/commit/52c3bbd36b9e94a0f3a78b4ada84d0c08eba27f6)) + - Merge branch 'status' ([`3e5c974`](https://github.com/Byron/gitoxide/commit/3e5c974dd62ac134711c6c2f5a5490187a6ea55e)) + - Fix lints for nightly, and clippy ([`f8ce3d0`](https://github.com/Byron/gitoxide/commit/f8ce3d0721b6a53713a9392f2451874f520bc44c)) +
+ ## 0.22.0 (2024-01-20) A maintenance release without user-facing changes. @@ -13,7 +63,7 @@ A maintenance release without user-facing changes. - - 1 commit contributed to the release. + - 2 commits contributed to the release. - 20 days passed between releases. - 0 commits were understood as [conventional](https://www.conventionalcommits.org). - 0 issues like '(#ID)' were seen in commit messages @@ -25,6 +75,7 @@ A maintenance release without user-facing changes.
view details * **Uncategorized** + - Release gix-utils v0.1.9, gix-features v0.38.0, gix-actor v0.30.0, gix-object v0.41.0, gix-path v0.10.4, gix-glob v0.16.0, gix-attributes v0.22.0, gix-command v0.3.3, gix-packetline-blocking v0.17.3, gix-filter v0.9.0, gix-fs v0.10.0, gix-commitgraph v0.24.0, gix-revwalk v0.12.0, gix-traverse v0.37.0, gix-worktree-stream v0.9.0, gix-archive v0.9.0, gix-config-value v0.14.4, gix-tempfile v13.0.0, gix-lock v13.0.0, gix-ref v0.41.0, gix-sec v0.10.4, gix-config v0.34.0, gix-url v0.27.0, gix-credentials v0.24.0, gix-ignore v0.11.0, gix-index v0.29.0, gix-worktree v0.30.0, gix-diff v0.40.0, gix-discover v0.29.0, gix-mailmap v0.22.0, gix-negotiate v0.12.0, gix-pack v0.47.0, gix-odb v0.57.0, gix-pathspec v0.6.0, gix-packetline v0.17.3, gix-transport v0.41.0, gix-protocol v0.44.0, gix-revision v0.26.0, gix-refspec v0.22.0, gix-status v0.5.0, gix-submodule v0.8.0, gix-worktree-state v0.7.0, gix v0.58.0, safety bump 39 crates ([`eb6aa8f`](https://github.com/Byron/gitoxide/commit/eb6aa8f502314f886fc4ea3d52ab220763968208)) - Prepare changelogs prior to release ([`6a2e0be`](https://github.com/Byron/gitoxide/commit/6a2e0bebfdf012dc2ed0ff2604086081f2a0f96d))
@@ -34,12 +85,12 @@ A maintenance release without user-facing changes. ### Chore - - change `rust-version` manifest field back to 1.65. - They didn't actually need to be higher to work, and changing them - unecessarily can break downstream CI. - - Let's keep this value as low as possible, and only increase it when - more recent features are actually used. +- change `rust-version` manifest field back to 1.65. + They didn't actually need to be higher to work, and changing them + unecessarily can break downstream CI. + + Let's keep this value as low as possible, and only increase it when + more recent features are actually used. ### Commit Statistics @@ -67,8 +118,8 @@ A maintenance release without user-facing changes. ### Chore - - upgrade MSRV to v1.70 - Our MSRV follows the one of `helix`, which in turn follows Firefox. +- upgrade MSRV to v1.70 + Our MSRV follows the one of `helix`, which in turn follows Firefox. ### Commit Statistics @@ -211,7 +262,7 @@ A maintenance release without user-facing changes. ### Chore - - don't call crate 'WIP' in manifest anymore. +- don't call crate 'WIP' in manifest anymore. ### Commit Statistics @@ -445,10 +496,10 @@ A maintenance release without user-facing changes. ### Chore - - replace `quick-error` with `thiserror` - This increases the compile time of the crate alone if there is no proc-macro - in the dependency tree, but will ever so slightly improve compile times for `gix` - as a whole. +- replace `quick-error` with `thiserror` + This increases the compile time of the crate alone if there is no proc-macro + in the dependency tree, but will ever so slightly improve compile times for `gix` + as a whole. ### Commit Statistics @@ -544,8 +595,8 @@ A maintenance release without user-facing changes. ### Chore - - uniformize deny attributes - - remove default link to cargo doc everywhere +- uniformize deny attributes +- remove default link to cargo doc everywhere ### Documentation @@ -767,8 +818,8 @@ Maintenance release without user-facing changes. ### Chore - - uniformize deny attributes - - remove default link to cargo doc everywhere +- uniformize deny attributes +- remove default link to cargo doc everywhere ### New Features diff --git a/gix-mailmap/Cargo.toml b/gix-mailmap/Cargo.toml index f468795eabd..0d3c6ac6907 100644 --- a/gix-mailmap/Cargo.toml +++ b/gix-mailmap/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "gix-mailmap" -version = "0.22.0" +version = "0.23.1" repository = "https://github.com/Byron/gitoxide" license = "MIT OR Apache-2.0" description = "A crate of the gitoxide project for parsing mailmap files" @@ -17,8 +17,8 @@ doctest = false serde= ["dep:serde", "bstr/serde", "gix-actor/serde"] [dependencies] -gix-actor = { version = "^0.30.0", path = "../gix-actor" } -gix-date = { version = "^0.8.3", path = "../gix-date" } +gix-actor = { version = "^0.31.2", path = "../gix-actor" } +gix-date = { version = "^0.8.6", path = "../gix-date" } bstr = { version = "1.3.0", default-features = false, features = ["std", "unicode"]} thiserror = "1.0.38" serde = { version = "1.0.114", optional = true, default-features = false, features = ["derive"]} diff --git a/gix-mailmap/src/lib.rs b/gix-mailmap/src/lib.rs index f6d9821eeba..64844666448 100644 --- a/gix-mailmap/src/lib.rs +++ b/gix-mailmap/src/lib.rs @@ -12,6 +12,7 @@ use bstr::BStr; /// +#[allow(clippy::empty_docs)] pub mod parse; /// Parse the given `buf` of bytes line by line into mapping [Entries][Entry]. @@ -30,6 +31,7 @@ pub fn parse_ignore_errors(buf: &[u8]) -> impl Iterator> { mod entry; /// +#[allow(clippy::empty_docs)] pub mod snapshot; /// A data-structure to efficiently store a list of entries for optimal, case-insensitive lookup by email and diff --git a/gix-negotiate/CHANGELOG.md b/gix-negotiate/CHANGELOG.md index 24164986fdb..bd82c1cc02d 100644 --- a/gix-negotiate/CHANGELOG.md +++ b/gix-negotiate/CHANGELOG.md @@ -5,6 +5,57 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## 0.13.1 (2024-05-22) + +A maintenance release without user-facing changes. + +### Commit Statistics + + + + - 5 commits contributed to the release over the course of 8 calendar days. + - 68 days passed between releases. + - 0 commits were understood as [conventional](https://www.conventionalcommits.org). + - 0 issues like '(#ID)' were seen in commit messages + +### Commit Details + + + +
view details + + * **Uncategorized** + - Adjust changelogs prior to release ([`9511416`](https://github.com/Byron/gitoxide/commit/9511416a6cd0c571233f958c165329c8705c2498)) + - Merge branch 'various-fixes' ([`d6cd449`](https://github.com/Byron/gitoxide/commit/d6cd44930fb204b06e2b70fc6965e7705530c47a)) + - Merge pull request from GHSA-7w47-3wg8-547c ([`79dce79`](https://github.com/Byron/gitoxide/commit/79dce79c62f6072aa2653780d590dc3993dfa401)) + - Adapt to changes in `gix-ref` ([`d2ae9d5`](https://github.com/Byron/gitoxide/commit/d2ae9d5f11be9f2561f6799d88804d0d8eae33ef)) + - Release gix-date v0.8.6 ([`d3588ca`](https://github.com/Byron/gitoxide/commit/d3588ca4fe0364c88e42cdac24ceae548355d99d)) +
+ +## 0.13.0 (2024-03-14) + +A maintenance release without user-facing changes. + +### Commit Statistics + + + + - 2 commits contributed to the release. + - 54 days passed between releases. + - 0 commits were understood as [conventional](https://www.conventionalcommits.org). + - 0 issues like '(#ID)' were seen in commit messages + +### Commit Details + + + +
view details + + * **Uncategorized** + - Release gix-date v0.8.5, gix-hash v0.14.2, gix-trace v0.1.8, gix-utils v0.1.11, gix-features v0.38.1, gix-actor v0.31.0, gix-validate v0.8.4, gix-object v0.42.0, gix-path v0.10.7, gix-glob v0.16.2, gix-quote v0.4.12, gix-attributes v0.22.2, gix-command v0.3.6, gix-filter v0.11.0, gix-fs v0.10.1, gix-chunk v0.4.8, gix-commitgraph v0.24.2, gix-hashtable v0.5.2, gix-revwalk v0.13.0, gix-traverse v0.38.0, gix-worktree-stream v0.11.0, gix-archive v0.11.0, gix-config-value v0.14.6, gix-tempfile v13.1.1, gix-lock v13.1.1, gix-ref v0.43.0, gix-sec v0.10.6, gix-config v0.36.0, gix-prompt v0.8.4, gix-url v0.27.2, gix-credentials v0.24.2, gix-ignore v0.11.2, gix-bitmap v0.2.11, gix-index v0.31.0, gix-worktree v0.32.0, gix-diff v0.42.0, gix-discover v0.31.0, gix-pathspec v0.7.1, gix-dir v0.2.0, gix-macros v0.1.4, gix-mailmap v0.23.0, gix-negotiate v0.13.0, gix-pack v0.49.0, gix-odb v0.59.0, gix-packetline v0.17.4, gix-transport v0.41.2, gix-protocol v0.44.2, gix-revision v0.27.0, gix-refspec v0.23.0, gix-status v0.7.0, gix-submodule v0.10.0, gix-worktree-state v0.9.0, gix v0.60.0, safety bump 26 crates ([`b050327`](https://github.com/Byron/gitoxide/commit/b050327e76f234b19be921b78b7b28e034319fdb)) + - Prepare changelogs prior to release ([`52c3bbd`](https://github.com/Byron/gitoxide/commit/52c3bbd36b9e94a0f3a78b4ada84d0c08eba27f6)) +
+ ## 0.12.0 (2024-01-20) A maintenance release without user-facing changes. @@ -13,7 +64,7 @@ A maintenance release without user-facing changes. - - 4 commits contributed to the release over the course of 4 calendar days. + - 5 commits contributed to the release over the course of 4 calendar days. - 20 days passed between releases. - 0 commits were understood as [conventional](https://www.conventionalcommits.org). - 0 issues like '(#ID)' were seen in commit messages @@ -25,6 +76,7 @@ A maintenance release without user-facing changes.
view details * **Uncategorized** + - Release gix-utils v0.1.9, gix-features v0.38.0, gix-actor v0.30.0, gix-object v0.41.0, gix-path v0.10.4, gix-glob v0.16.0, gix-attributes v0.22.0, gix-command v0.3.3, gix-packetline-blocking v0.17.3, gix-filter v0.9.0, gix-fs v0.10.0, gix-commitgraph v0.24.0, gix-revwalk v0.12.0, gix-traverse v0.37.0, gix-worktree-stream v0.9.0, gix-archive v0.9.0, gix-config-value v0.14.4, gix-tempfile v13.0.0, gix-lock v13.0.0, gix-ref v0.41.0, gix-sec v0.10.4, gix-config v0.34.0, gix-url v0.27.0, gix-credentials v0.24.0, gix-ignore v0.11.0, gix-index v0.29.0, gix-worktree v0.30.0, gix-diff v0.40.0, gix-discover v0.29.0, gix-mailmap v0.22.0, gix-negotiate v0.12.0, gix-pack v0.47.0, gix-odb v0.57.0, gix-pathspec v0.6.0, gix-packetline v0.17.3, gix-transport v0.41.0, gix-protocol v0.44.0, gix-revision v0.26.0, gix-refspec v0.22.0, gix-status v0.5.0, gix-submodule v0.8.0, gix-worktree-state v0.7.0, gix v0.58.0, safety bump 39 crates ([`eb6aa8f`](https://github.com/Byron/gitoxide/commit/eb6aa8f502314f886fc4ea3d52ab220763968208)) - Prepare changelogs prior to release ([`6a2e0be`](https://github.com/Byron/gitoxide/commit/6a2e0bebfdf012dc2ed0ff2604086081f2a0f96d)) - Merge branch 'dirwalk' ([`5d176fc`](https://github.com/Byron/gitoxide/commit/5d176fc5ab82bfc7c194b4d929e73da9659ae8b8)) - Adapt to changes in `gix-features` ([`eacb5a4`](https://github.com/Byron/gitoxide/commit/eacb5a4ae2fd94b095005cfbc0a8b2aa67539e52)) @@ -37,12 +89,12 @@ A maintenance release without user-facing changes. ### Chore - - change `rust-version` manifest field back to 1.65. - They didn't actually need to be higher to work, and changing them - unecessarily can break downstream CI. - - Let's keep this value as low as possible, and only increase it when - more recent features are actually used. +- change `rust-version` manifest field back to 1.65. + They didn't actually need to be higher to work, and changing them + unecessarily can break downstream CI. + + Let's keep this value as low as possible, and only increase it when + more recent features are actually used. ### Commit Statistics @@ -70,8 +122,8 @@ A maintenance release without user-facing changes. ### Chore - - upgrade MSRV to v1.70 - Our MSRV follows the one of `helix`, which in turn follows Firefox. +- upgrade MSRV to v1.70 + Our MSRV follows the one of `helix`, which in turn follows Firefox. ### Commit Statistics @@ -331,7 +383,7 @@ A maintenance release without user-facing changes. ### Chore - - Add `clippy::redundant-closure-for-method-calls` lint +- Add `clippy::redundant-closure-for-method-calls` lint ### New Features @@ -417,12 +469,12 @@ A maintenance release without user-facing changes. ### Other - - try to change test-suite from --negotiate-only to the more realistic fetch with --dry-run. - This means we will have to reproduce what git does naturally, to fill in common refs - and also provide tips. - - Unfortunately this doesn't work as it's apparently not really dry-running, but modifying - the repository underneath. This means it's not idempotent when running it multiple times. +- try to change test-suite from --negotiate-only to the more realistic fetch with --dry-run. + This means we will have to reproduce what git does naturally, to fill in common refs + and also provide tips. + + Unfortunately this doesn't work as it's apparently not really dry-running, but modifying + the repository underneath. This means it's not idempotent when running it multiple times. ### Commit Statistics diff --git a/gix-negotiate/Cargo.toml b/gix-negotiate/Cargo.toml index 3989e568246..6c1c41732e9 100644 --- a/gix-negotiate/Cargo.toml +++ b/gix-negotiate/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "gix-negotiate" -version = "0.12.0" +version = "0.13.1" repository = "https://github.com/Byron/gitoxide" license = "MIT OR Apache-2.0" description = "A crate of the gitoxide project implementing negotiation algorithms" @@ -14,11 +14,11 @@ doctest = false test = false [dependencies] -gix-hash = { version = "^0.14.1", path = "../gix-hash" } -gix-object = { version = "^0.41.0", path = "../gix-object" } -gix-date = { version = "^0.8.3", path = "../gix-date" } -gix-commitgraph = { version = "^0.24.0", path = "../gix-commitgraph" } -gix-revwalk = { version = "^0.12.0", path = "../gix-revwalk" } +gix-hash = { version = "^0.14.2", path = "../gix-hash" } +gix-object = { version = "^0.42.2", path = "../gix-object" } +gix-date = { version = "^0.8.6", path = "../gix-date" } +gix-commitgraph = { version = "^0.24.2", path = "../gix-commitgraph" } +gix-revwalk = { version = "^0.13.1", path = "../gix-revwalk" } thiserror = "1.0.40" smallvec = "1.10.0" bitflags = "2" diff --git a/gix-negotiate/tests/baseline/mod.rs b/gix-negotiate/tests/baseline/mod.rs index 158d70a4be3..f416097f428 100644 --- a/gix-negotiate/tests/baseline/mod.rs +++ b/gix-negotiate/tests/baseline/mod.rs @@ -26,9 +26,10 @@ fn run() -> crate::Result { let store = gix_odb::at(base.join("client").join(".git/objects"))?; let refs = gix_ref::file::Store::at( base.join("client").join(".git"), - WriteReflog::Disable, - gix_hash::Kind::Sha1, - false, + gix_ref::store::init::Options { + write_reflog: WriteReflog::Disable, + ..Default::default() + }, ); let lookup_names = |names: &[&str]| -> Vec { names diff --git a/gix-negotiate/tests/fixtures/generated-archives/make_repos.tar.xz b/gix-negotiate/tests/fixtures/generated-archives/make_repos.tar.xz index 93e83693131..2a0799c3ea3 100644 Binary files a/gix-negotiate/tests/fixtures/generated-archives/make_repos.tar.xz and b/gix-negotiate/tests/fixtures/generated-archives/make_repos.tar.xz differ diff --git a/gix-negotiate/tests/fixtures/make_repos.sh b/gix-negotiate/tests/fixtures/make_repos.sh index b1b96527d89..1b6997c220b 100755 --- a/gix-negotiate/tests/fixtures/make_repos.sh +++ b/gix-negotiate/tests/fixtures/make_repos.sh @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash set -eu -o pipefail function tick () { diff --git a/gix-object/CHANGELOG.md b/gix-object/CHANGELOG.md index ff1db7a9c2e..0a0b8fa6688 100644 --- a/gix-object/CHANGELOG.md +++ b/gix-object/CHANGELOG.md @@ -5,6 +5,86 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## 0.42.2 (2024-05-22) + +A maintenance release without user-facing changes. + +### Commit Statistics + + + + - 2 commits contributed to the release over the course of 8 calendar days. + - 65 days passed between releases. + - 0 commits were understood as [conventional](https://www.conventionalcommits.org). + - 0 issues like '(#ID)' were seen in commit messages + +### Commit Details + + + +
view details + + * **Uncategorized** + - Adjust changelogs prior to release ([`9511416`](https://github.com/Byron/gitoxide/commit/9511416a6cd0c571233f958c165329c8705c2498)) + - Release gix-date v0.8.6 ([`d3588ca`](https://github.com/Byron/gitoxide/commit/d3588ca4fe0364c88e42cdac24ceae548355d99d)) +
+ +## 0.42.1 (2024-03-18) + +### Bug Fixes + + - allow parsing of commits with broken timestamp. + Instead, default to UTC just like Git. + +### Commit Statistics + + + + - 4 commits contributed to the release. + - 3 days passed between releases. + - 1 commit was understood as [conventional](https://www.conventionalcommits.org). + - 1 unique issue was worked on: [#1322](https://github.com/Byron/gitoxide/issues/1322) + +### Commit Details + + + +
view details + + * **[#1322](https://github.com/Byron/gitoxide/issues/1322)** + - Allow parsing of commits with broken timestamp. ([`ab21083`](https://github.com/Byron/gitoxide/commit/ab210836ac4558c88cc5f19fdddfb5c8eeb29754)) + * **Uncategorized** + - Release gix-actor v0.31.1, gix-object v0.42.1, gix-index v0.31.1, gix-pathspec v0.7.2, gix-dir v0.3.0, gix-status v0.8.0, gix v0.61.0, safety bump 2 crates ([`155cc45`](https://github.com/Byron/gitoxide/commit/155cc45730b259e662d7c4be42a469a3af3750e1)) + - Prepare changelog prior to release ([`129ba3d`](https://github.com/Byron/gitoxide/commit/129ba3deccc9ada0dc571466458845939502763d)) + - Merge branch 'improvements-for-cargo' ([`41cd53e`](https://github.com/Byron/gitoxide/commit/41cd53e2af76e35e047aac4eca6324774df4cb50)) +
+ +## 0.42.0 (2024-03-14) + +A maintenance release without user-facing changes. + +### Commit Statistics + + + + - 4 commits contributed to the release over the course of 4 calendar days. + - 18 days passed between releases. + - 0 commits were understood as [conventional](https://www.conventionalcommits.org). + - 0 issues like '(#ID)' were seen in commit messages + +### Commit Details + + + +
view details + + * **Uncategorized** + - Release gix-date v0.8.5, gix-hash v0.14.2, gix-trace v0.1.8, gix-utils v0.1.11, gix-features v0.38.1, gix-actor v0.31.0, gix-validate v0.8.4, gix-object v0.42.0, gix-path v0.10.7, gix-glob v0.16.2, gix-quote v0.4.12, gix-attributes v0.22.2, gix-command v0.3.6, gix-filter v0.11.0, gix-fs v0.10.1, gix-chunk v0.4.8, gix-commitgraph v0.24.2, gix-hashtable v0.5.2, gix-revwalk v0.13.0, gix-traverse v0.38.0, gix-worktree-stream v0.11.0, gix-archive v0.11.0, gix-config-value v0.14.6, gix-tempfile v13.1.1, gix-lock v13.1.1, gix-ref v0.43.0, gix-sec v0.10.6, gix-config v0.36.0, gix-prompt v0.8.4, gix-url v0.27.2, gix-credentials v0.24.2, gix-ignore v0.11.2, gix-bitmap v0.2.11, gix-index v0.31.0, gix-worktree v0.32.0, gix-diff v0.42.0, gix-discover v0.31.0, gix-pathspec v0.7.1, gix-dir v0.2.0, gix-macros v0.1.4, gix-mailmap v0.23.0, gix-negotiate v0.13.0, gix-pack v0.49.0, gix-odb v0.59.0, gix-packetline v0.17.4, gix-transport v0.41.2, gix-protocol v0.44.2, gix-revision v0.27.0, gix-refspec v0.23.0, gix-status v0.7.0, gix-submodule v0.10.0, gix-worktree-state v0.9.0, gix v0.60.0, safety bump 26 crates ([`b050327`](https://github.com/Byron/gitoxide/commit/b050327e76f234b19be921b78b7b28e034319fdb)) + - Prepare changelogs prior to release ([`52c3bbd`](https://github.com/Byron/gitoxide/commit/52c3bbd36b9e94a0f3a78b4ada84d0c08eba27f6)) + - Merge branch 'status' ([`3e5c974`](https://github.com/Byron/gitoxide/commit/3e5c974dd62ac134711c6c2f5a5490187a6ea55e)) + - Fix lints for nightly, and clippy ([`f8ce3d0`](https://github.com/Byron/gitoxide/commit/f8ce3d0721b6a53713a9392f2451874f520bc44c)) +
+ ## 0.41.1 (2024-02-25) ### New Features @@ -24,7 +104,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - - 15 commits contributed to the release over the course of 34 calendar days. + - 16 commits contributed to the release over the course of 34 calendar days. - 36 days passed between releases. - 2 commits were understood as [conventional](https://www.conventionalcommits.org). - 1 unique issue was worked on: [#1259](https://github.com/Byron/gitoxide/issues/1259) @@ -39,6 +119,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Allow the creation of any `EntryMode`. ([`82e85fd`](https://github.com/Byron/gitoxide/commit/82e85fd192f59bc62bb27de13360a543971e8167)) - `EntryMode` canonicalization ([`67833df`](https://github.com/Byron/gitoxide/commit/67833dfd18c49ed8073e7a3c0461579c0a5c2017)) * **Uncategorized** + - Release gix-date v0.8.4, gix-utils v0.1.10, gix-actor v0.30.1, gix-object v0.41.1, gix-path v0.10.6, gix-glob v0.16.1, gix-quote v0.4.11, gix-attributes v0.22.1, gix-command v0.3.5, gix-filter v0.10.0, gix-commitgraph v0.24.1, gix-worktree-stream v0.10.0, gix-archive v0.10.0, gix-config-value v0.14.5, gix-ref v0.42.0, gix-sec v0.10.5, gix-config v0.35.0, gix-prompt v0.8.3, gix-url v0.27.1, gix-credentials v0.24.1, gix-ignore v0.11.1, gix-index v0.30.0, gix-worktree v0.31.0, gix-diff v0.41.0, gix-discover v0.30.0, gix-pathspec v0.7.0, gix-dir v0.1.0, gix-pack v0.48.0, gix-odb v0.58.0, gix-transport v0.41.1, gix-protocol v0.44.1, gix-revision v0.26.1, gix-refspec v0.22.1, gix-status v0.6.0, gix-submodule v0.9.0, gix-worktree-state v0.8.0, gix v0.59.0, gix-fsck v0.3.0, gitoxide-core v0.36.0, gitoxide v0.34.0, safety bump 10 crates ([`45b4470`](https://github.com/Byron/gitoxide/commit/45b447045bc826f252129c300c531acde2652c64)) - Prepare changelogs prior to release ([`f2e111f`](https://github.com/Byron/gitoxide/commit/f2e111f768fc1bc6182355261c20b63610cffec7)) - Merge branch 'btoi' ([`5fc379d`](https://github.com/Byron/gitoxide/commit/5fc379d1dc867d15a50cb086e30beefde2b42d86)) - Refactor ([`c5c69bd`](https://github.com/Byron/gitoxide/commit/c5c69bd355771a6fb3e4f6db0c5f49aa2bf7f42f)) @@ -84,12 +165,12 @@ A maintenance release without user-facing changes. ### Chore - - change `rust-version` manifest field back to 1.65. - They didn't actually need to be higher to work, and changing them - unecessarily can break downstream CI. - - Let's keep this value as low as possible, and only increase it when - more recent features are actually used. +- change `rust-version` manifest field back to 1.65. + They didn't actually need to be higher to work, and changing them + unecessarily can break downstream CI. + + Let's keep this value as low as possible, and only increase it when + more recent features are actually used. ### Commit Statistics @@ -118,12 +199,12 @@ A maintenance release without user-facing changes. ### Chore - - upgrade MSRV to v1.70 - Our MSRV follows the one of `helix`, which in turn follows Firefox. +- upgrade MSRV to v1.70 + Our MSRV follows the one of `helix`, which in turn follows Firefox. ### Other - - Add gix-object fuzzers +- Add gix-object fuzzers ### Commit Statistics @@ -333,11 +414,11 @@ A maintenance release without user-facing changes. ### Chore - - switch `nom` to `winnow` in remaining uses in `gix-object`, `gix-ref`, and `gix-actor` for ~20% more performance. - It's likely that over time, these parsers will get even faster due to improvements to `winnow`. - Thanks, Ed Page, for single-handedly performing this transition. - - add benchmarks to avoid parsing performance regressions - - remove unused dependency: 'hex' +- switch `nom` to `winnow` in remaining uses in `gix-object`, `gix-ref`, and `gix-actor` for ~20% more performance. + It's likely that over time, these parsers will get even faster due to improvements to `winnow`. + Thanks, Ed Page, for single-handedly performing this transition. +- add benchmarks to avoid parsing performance regressions +- remove unused dependency: 'hex' ### Commit Statistics @@ -417,9 +498,9 @@ A maintenance release without user-facing changes. ### Other - - fix test for struct size for 32-bit architectures - The size of gix_object::Data is 24 bytes only on 64-bit architectures, - on 32-bit architectures it's exactly half that (12 bytes). +- fix test for struct size for 32-bit architectures + The size of gix_object::Data is 24 bytes only on 64-bit architectures, + on 32-bit architectures it's exactly half that (12 bytes). ### Commit Statistics @@ -545,7 +626,7 @@ A maintenance release without user-facing changes. ### Chore - - Add `clippy::redundant-closure-for-method-calls` lint +- Add `clippy::redundant-closure-for-method-calls` lint ### Commit Statistics @@ -865,10 +946,10 @@ A maintenance release without user-facing changes. ### Refactor (BREAKING) - - move git_pack::data::Object to git_object::Data, massively alter git_odb::Find trait - This will break a lot, but has to happen to prepare these traits for the - next generation of object databases. - - move loose header manipulation from git-pack to git-object +- move git_pack::data::Object to git_object::Data, massively alter git_odb::Find trait + This will break a lot, but has to happen to prepare these traits for the + next generation of object databases. +- move loose header manipulation from git-pack to git-object ### New Features (BREAKING) @@ -885,8 +966,8 @@ A maintenance release without user-facing changes. ### Chore (BREAKING) - - remove quick-error in favor of thiserror - Some errors change shape which makes this a breaking change. +- remove quick-error in favor of thiserror + Some errors change shape which makes this a breaking change. ### Changed (BREAKING) @@ -896,9 +977,9 @@ A maintenance release without user-facing changes. ### Refactor - - Use borrowed::Id in trees for full type safety - - make reusing round-trip code easier - - Prefer integration level tests, but use unit-tests where appropriate +- Use borrowed::Id in trees for full type safety +- make reusing round-trip code easier +- Prefer integration level tests, but use unit-tests where appropriate ### Performance @@ -907,7 +988,7 @@ A maintenance release without user-facing changes. ### Other - - describe variants +- describe variants ### Bug Fixes @@ -949,8 +1030,8 @@ A maintenance release without user-facing changes. ### Chore - - uniformize deny attributes - - upgrade dependencies +- uniformize deny attributes +- upgrade dependencies ### Documentation @@ -1629,8 +1710,8 @@ A maintenance release without user-facing changes. ### Chore (BREAKING) - - remove quick-error in favor of thiserror - Some errors change shape which makes this a breaking change. +- remove quick-error in favor of thiserror + Some errors change shape which makes this a breaking change. ## 0.20.3 (2022-08-28) @@ -1642,7 +1723,7 @@ Maintenance release without user-facing changes. ### Chore - - uniformize deny attributes +- uniformize deny attributes ### New Features @@ -1695,7 +1776,7 @@ A automated maintenance release without impact to the public API. ### Fixes - - Corrected the tree-entry ordering implementation. +- Corrected the tree-entry ordering implementation. ## 0.17.0 (2022-01-23) @@ -1704,7 +1785,7 @@ A automated maintenance release without impact to the public API. ### Chore - - upgrade dependencies +- upgrade dependencies ### New Features @@ -1719,9 +1800,9 @@ A automated maintenance release without impact to the public API. - rename `commit::ref_iter::Token::into_id()` to `*::try_into_id()` This makes the method more consistent. - - move gix_pack::data::Object to gix_object::Data, massively alter gix_odb::Find trait - This will break a lot, but has to happen to prepare these traits for the - next generation of object databases. +- move gix_pack::data::Object to gix_object::Data, massively alter gix_odb::Find trait + This will break a lot, but has to happen to prepare these traits for the + next generation of object databases. ## 0.16.0 (2021-11-29) @@ -1746,13 +1827,13 @@ or generally trying to figure out what changed between commits. ### Other - - describe variants +- describe variants ### Performance - - Provide a new fast parser for tree objects which is used by the tree entry iterator. +- Provide a new fast parser for tree objects which is used by the tree entry iterator. - parse entry mode into number instead of comparing it to byte strings ## v0.14.0 (2021-09-08) @@ -1790,8 +1871,8 @@ or generally trying to figure out what changed between commits. ### Refactor - - Use borrowed::Id in trees for full type safety - - make reusing round-trip code easier +- Use borrowed::Id in trees for full type safety +- make reusing round-trip code easier ## v0.1.0 (2020-07-12) @@ -1799,5 +1880,5 @@ or generally trying to figure out what changed between commits. ### Refactor - - Prefer integration level tests, but use unit-tests where appropriate +- Prefer integration level tests, but use unit-tests where appropriate diff --git a/gix-object/Cargo.toml b/gix-object/Cargo.toml index 3dd080034d1..fb161f71b2a 100644 --- a/gix-object/Cargo.toml +++ b/gix-object/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "gix-object" -version = "0.41.1" +version = "0.42.2" description = "Immutable and mutable git objects with decoding and encoding support" authors = ["Sebastian Thiel "] repository = "https://github.com/Byron/gitoxide" @@ -34,15 +34,15 @@ serde = [ verbose-object-parsing-errors = ["winnow/std"] [dependencies] -gix-features = { version = "^0.38.0", path = "../gix-features", features = [ +gix-features = { version = "^0.38.2", path = "../gix-features", features = [ "rustsha1", "progress", ] } -gix-hash = { version = "^0.14.1", path = "../gix-hash" } -gix-validate = { version = "^0.8.3", path = "../gix-validate" } -gix-actor = { version = "^0.30.1", path = "../gix-actor" } -gix-date = { version = "^0.8.4", path = "../gix-date" } -gix-utils = { version = "^0.1.10", path = "../gix-utils" } +gix-hash = { version = "^0.14.2", path = "../gix-hash" } +gix-validate = { version = "^0.8.5", path = "../gix-validate" } +gix-actor = { version = "^0.31.2", path = "../gix-actor" } +gix-date = { version = "^0.8.6", path = "../gix-date" } +gix-utils = { version = "^0.1.11", path = "../gix-utils" } itoa = "1.0.1" thiserror = "1.0.34" diff --git a/gix-object/src/commit/message/mod.rs b/gix-object/src/commit/message/mod.rs index ac5e3224c0d..5bb0046ee90 100644 --- a/gix-object/src/commit/message/mod.rs +++ b/gix-object/src/commit/message/mod.rs @@ -7,6 +7,7 @@ use crate::{ }; /// +#[allow(clippy::empty_docs)] pub mod body; mod decode; diff --git a/gix-object/src/commit/mod.rs b/gix-object/src/commit/mod.rs index 17dc12bebd6..dd79b24ae84 100644 --- a/gix-object/src/commit/mod.rs +++ b/gix-object/src/commit/mod.rs @@ -7,6 +7,7 @@ use crate::{Commit, CommitRef, TagRef}; mod decode; /// +#[allow(clippy::empty_docs)] pub mod message; /// A parsed commit message that assumes a title separated from the body by two consecutive newlines. @@ -53,6 +54,7 @@ impl From> for BString { } /// +#[allow(clippy::empty_docs)] pub mod ref_iter; mod write; diff --git a/gix-object/src/find.rs b/gix-object/src/find.rs index 8471592cbc9..5a8c5e36cc2 100644 --- a/gix-object/src/find.rs +++ b/gix-object/src/find.rs @@ -1,6 +1,7 @@ /// The error type returned by the [`Find`](crate::Find) trait. pub type Error = Box; /// +#[allow(clippy::empty_docs)] pub mod existing { use gix_hash::ObjectId; @@ -16,6 +17,7 @@ pub mod existing { } /// +#[allow(clippy::empty_docs)] pub mod existing_object { use gix_hash::ObjectId; @@ -42,6 +44,7 @@ pub mod existing_object { } /// +#[allow(clippy::empty_docs)] pub mod existing_iter { use gix_hash::ObjectId; diff --git a/gix-object/src/lib.rs b/gix-object/src/lib.rs index 43cb5f5bfd0..f8af6a81c94 100644 --- a/gix-object/src/lib.rs +++ b/gix-object/src/lib.rs @@ -19,18 +19,23 @@ pub use gix_date as date; use smallvec::SmallVec; /// +#[allow(clippy::empty_docs)] pub mod commit; mod object; /// +#[allow(clippy::empty_docs)] pub mod tag; /// +#[allow(clippy::empty_docs)] pub mod tree; mod blob; /// +#[allow(clippy::empty_docs)] pub mod data; /// +#[allow(clippy::empty_docs)] pub mod find; mod traits; @@ -40,6 +45,7 @@ pub mod encode; pub(crate) mod parse; /// +#[allow(clippy::empty_docs)] pub mod kind; /// The four types of objects that git differentiates. @@ -267,6 +273,7 @@ pub struct Header { } /// +#[allow(clippy::empty_docs)] pub mod decode { #[cfg(feature = "verbose-object-parsing-errors")] mod _decode { diff --git a/gix-object/src/object/convert.rs b/gix-object/src/object/convert.rs index 5e6e634869d..87ee35b1c48 100644 --- a/gix-object/src/object/convert.rs +++ b/gix-object/src/object/convert.rs @@ -1,5 +1,3 @@ -use std::convert::TryFrom; - use crate::{tree, Blob, BlobRef, Commit, CommitRef, Object, ObjectRef, Tag, TagRef, Tree, TreeRef}; impl From> for Tag { diff --git a/gix-object/src/parse.rs b/gix-object/src/parse.rs index e241f79b45e..9401e505cea 100644 --- a/gix-object/src/parse.rs +++ b/gix-object/src/parse.rs @@ -4,7 +4,6 @@ use winnow::{ error::{AddContext, ParserError, StrContext}, prelude::*, token::{take_till, take_until, take_while}, - Parser, }; use crate::ByteSlice; diff --git a/gix-object/src/tag/mod.rs b/gix-object/src/tag/mod.rs index ecd56c9f7db..cc0d50e32ba 100644 --- a/gix-object/src/tag/mod.rs +++ b/gix-object/src/tag/mod.rs @@ -5,9 +5,11 @@ use crate::TagRef; mod decode; /// +#[allow(clippy::empty_docs)] pub mod write; /// +#[allow(clippy::empty_docs)] pub mod ref_iter; impl<'a> TagRef<'a> { diff --git a/gix-object/src/tree/mod.rs b/gix-object/src/tree/mod.rs index 1014fcfde29..0699d1a46db 100644 --- a/gix-object/src/tree/mod.rs +++ b/gix-object/src/tree/mod.rs @@ -7,6 +7,7 @@ use crate::{ mod ref_iter; /// +#[allow(clippy::empty_docs)] pub mod write; /// The mode of items storable in a tree, similar to the file mode on a unix file system. diff --git a/gix-object/src/tree/ref_iter.rs b/gix-object/src/tree/ref_iter.rs index bdec622d265..2b82a968732 100644 --- a/gix-object/src/tree/ref_iter.rs +++ b/gix-object/src/tree/ref_iter.rs @@ -1,5 +1,3 @@ -use std::convert::TryFrom; - use bstr::BStr; use winnow::{error::ParserError, prelude::*}; @@ -124,8 +122,6 @@ impl TryFrom for tree::EntryMode { } mod decode { - use std::convert::TryFrom; - use bstr::ByteSlice; use winnow::{error::ParserError, prelude::*}; diff --git a/gix-object/tests/commit/from_bytes.rs b/gix-object/tests/commit/from_bytes.rs index 84c98785561..c999ee5e945 100644 --- a/gix-object/tests/commit/from_bytes.rs +++ b/gix-object/tests/commit/from_bytes.rs @@ -8,6 +8,33 @@ use crate::{ fixture_name, linus_signature, signature, }; +#[test] +fn invalid_timestsamp() { + let actor = gix_actor::SignatureRef { + name: b"Name".as_bstr(), + email: b"name@example.com".as_bstr(), + time: Time { + seconds: 1312735823, + offset: 0, + sign: Sign::Plus, + }, + }; + assert_eq!( + CommitRef::from_bytes(&fixture_name("commit", "invalid-timestamp.txt")) + .expect("auto-correct invalid timestamp by discarding it (time is still valid UTC)"), + CommitRef { + tree: b"7989dfb2ec2f41914611a22fb30bbc2b3849df9a".as_bstr(), + parents: [b"8845ae683e2688bc619baade49510c17e978518f".as_bstr()].into(), + author: actor, + committer: actor, + encoding: None, + message: b"edit changelog to mention about x_sendfile_header default change".as_bstr(), + extra_headers: vec![] + }, + "the offset of the actor is null, leaving the UTC time" + ); +} + #[test] fn unsigned() -> crate::Result { assert_eq!( diff --git a/gix-object/tests/fixtures/commit/invalid-timestamp.txt b/gix-object/tests/fixtures/commit/invalid-timestamp.txt new file mode 100644 index 00000000000..bd30774f1ff --- /dev/null +++ b/gix-object/tests/fixtures/commit/invalid-timestamp.txt @@ -0,0 +1,6 @@ +tree 7989dfb2ec2f41914611a22fb30bbc2b3849df9a +parent 8845ae683e2688bc619baade49510c17e978518f +author Name 1312735823 +051800 +committer Name 1312735823 +051800 + +edit changelog to mention about x_sendfile_header default change \ No newline at end of file diff --git a/gix-object/tests/fixtures/generated-archives/make_trees.tar.xz b/gix-object/tests/fixtures/generated-archives/make_trees.tar.xz index 61b6a6ac062..5e42731caba 100644 Binary files a/gix-object/tests/fixtures/generated-archives/make_trees.tar.xz and b/gix-object/tests/fixtures/generated-archives/make_trees.tar.xz differ diff --git a/gix-object/tests/fixtures/make_trees.sh b/gix-object/tests/fixtures/make_trees.sh index 74b282cbb30..0b7a53ccb1a 100755 --- a/gix-object/tests/fixtures/make_trees.sh +++ b/gix-object/tests/fixtures/make_trees.sh @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash set -eu -o pipefail function baseline() { diff --git a/gix-odb/CHANGELOG.md b/gix-odb/CHANGELOG.md index d1fa77e5c45..c4730b914b5 100644 --- a/gix-odb/CHANGELOG.md +++ b/gix-odb/CHANGELOG.md @@ -5,6 +5,111 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## 0.61.0 (2024-05-22) + +### Bug Fixes + + - more robustness in the face of a trampling-herd of threads loading a single index. + The motivating example is here: https://github.com/praetorian-inc/noseyparker/issues/179 + + Previously, it was possible for a trampling herd of threads to consolidate the + disk state. Most of them would be 'needs-init' threads which could notice that + the initialization already happened, and just use that. + + But a thread might be late for the party and somehow manages to not get any + newly loaded index, and thus tries to consolidate with what's on disk again. + Then it would again determine no change, and return nothing, causing the caller + to abort and not find objects it should find because it wouldn't see the index + that it should have seen. + + The reason the thread got into this mess is that the 'is-load-ongoing' flagging + was racy itself, so it would not wait for ongoing loads and just conclude nothing + happened. An extra delay (by yielding) now assures it either seees the loading state + and waits for it, sees the newly loaded indices. + + Note that this issue can be reproduced with: + + ``` + './target/release/gix -r repo-with-one-pack -t10 --trace odb stats --extra-header-lookup' + ``` + - use `tempfile` permissions support to set the correct mode on unix. + Previousoly it would make an additional syscall to change permissions, which + is slower than necessary. + +### Commit Statistics + + + + - 6 commits contributed to the release over the course of 10 calendar days. + - 38 days passed between releases. + - 2 commits were understood as [conventional](https://www.conventionalcommits.org). + - 0 issues like '(#ID)' were seen in commit messages + +### Commit Details + + + +
view details + + * **Uncategorized** + - Adjust changelogs prior to release ([`9511416`](https://github.com/Byron/gitoxide/commit/9511416a6cd0c571233f958c165329c8705c2498)) + - Release gix-date v0.8.6 ([`d3588ca`](https://github.com/Byron/gitoxide/commit/d3588ca4fe0364c88e42cdac24ceae548355d99d)) + - Merge branch 'status' ([`04ef31e`](https://github.com/Byron/gitoxide/commit/04ef31e9d6f5332d49037a5a4c248ebbb5aaf92b)) + - Adapt to changes in `gix-pack` ([`bad5b48`](https://github.com/Byron/gitoxide/commit/bad5b48e4f0d865b0b0937f136d9a0041aa88046)) + - More robustness in the face of a trampling-herd of threads loading a single index. ([`addf446`](https://github.com/Byron/gitoxide/commit/addf446f052ff74edcdb083f2b2968b313daa940)) + - Use `tempfile` permissions support to set the correct mode on unix. ([`7b3dc92`](https://github.com/Byron/gitoxide/commit/7b3dc92d04e5240a7bdbcdaa4019ac93cec6eaa3)) +
+ +## 0.60.0 (2024-04-13) + +A maintenance release without user-facing changes. + +### Commit Statistics + + + + - 2 commits contributed to the release. + - 29 days passed between releases. + - 0 commits were understood as [conventional](https://www.conventionalcommits.org). + - 0 issues like '(#ID)' were seen in commit messages + +### Commit Details + + + +
view details + + * **Uncategorized** + - Release gix-trace v0.1.9, gix-utils v0.1.12, gix-packetline-blocking v0.17.4, gix-filter v0.11.1, gix-fs v0.10.2, gix-traverse v0.39.0, gix-worktree-stream v0.12.0, gix-archive v0.12.0, gix-config v0.36.1, gix-url v0.27.3, gix-index v0.32.0, gix-worktree v0.33.0, gix-diff v0.43.0, gix-pathspec v0.7.3, gix-dir v0.4.0, gix-pack v0.50.0, gix-odb v0.60.0, gix-transport v0.42.0, gix-protocol v0.45.0, gix-status v0.9.0, gix-worktree-state v0.10.0, gix v0.62.0, gix-fsck v0.4.0, gitoxide-core v0.37.0, gitoxide v0.35.0, safety bump 14 crates ([`095c673`](https://github.com/Byron/gitoxide/commit/095c6739b2722a8b9af90776b435ef2da454c0e6)) + - Prepare changelogs prior to release ([`5755271`](https://github.com/Byron/gitoxide/commit/57552717f46f96c35ba4ddc0a64434354ef845e9)) +
+ +## 0.59.0 (2024-03-14) + +A maintenance release without user-facing changes. + +### Commit Statistics + + + + - 4 commits contributed to the release over the course of 4 calendar days. + - 18 days passed between releases. + - 0 commits were understood as [conventional](https://www.conventionalcommits.org). + - 0 issues like '(#ID)' were seen in commit messages + +### Commit Details + + + +
view details + + * **Uncategorized** + - Release gix-date v0.8.5, gix-hash v0.14.2, gix-trace v0.1.8, gix-utils v0.1.11, gix-features v0.38.1, gix-actor v0.31.0, gix-validate v0.8.4, gix-object v0.42.0, gix-path v0.10.7, gix-glob v0.16.2, gix-quote v0.4.12, gix-attributes v0.22.2, gix-command v0.3.6, gix-filter v0.11.0, gix-fs v0.10.1, gix-chunk v0.4.8, gix-commitgraph v0.24.2, gix-hashtable v0.5.2, gix-revwalk v0.13.0, gix-traverse v0.38.0, gix-worktree-stream v0.11.0, gix-archive v0.11.0, gix-config-value v0.14.6, gix-tempfile v13.1.1, gix-lock v13.1.1, gix-ref v0.43.0, gix-sec v0.10.6, gix-config v0.36.0, gix-prompt v0.8.4, gix-url v0.27.2, gix-credentials v0.24.2, gix-ignore v0.11.2, gix-bitmap v0.2.11, gix-index v0.31.0, gix-worktree v0.32.0, gix-diff v0.42.0, gix-discover v0.31.0, gix-pathspec v0.7.1, gix-dir v0.2.0, gix-macros v0.1.4, gix-mailmap v0.23.0, gix-negotiate v0.13.0, gix-pack v0.49.0, gix-odb v0.59.0, gix-packetline v0.17.4, gix-transport v0.41.2, gix-protocol v0.44.2, gix-revision v0.27.0, gix-refspec v0.23.0, gix-status v0.7.0, gix-submodule v0.10.0, gix-worktree-state v0.9.0, gix v0.60.0, safety bump 26 crates ([`b050327`](https://github.com/Byron/gitoxide/commit/b050327e76f234b19be921b78b7b28e034319fdb)) + - Prepare changelogs prior to release ([`52c3bbd`](https://github.com/Byron/gitoxide/commit/52c3bbd36b9e94a0f3a78b4ada84d0c08eba27f6)) + - Merge branch 'status' ([`3e5c974`](https://github.com/Byron/gitoxide/commit/3e5c974dd62ac134711c6c2f5a5490187a6ea55e)) + - Fix lints for nightly, and clippy ([`f8ce3d0`](https://github.com/Byron/gitoxide/commit/f8ce3d0721b6a53713a9392f2451874f520bc44c)) +
+ ## 0.58.0 (2024-02-25) A maintenance release without user-facing changes. @@ -13,7 +118,7 @@ A maintenance release without user-facing changes. - - 4 commits contributed to the release over the course of 30 calendar days. + - 5 commits contributed to the release over the course of 30 calendar days. - 36 days passed between releases. - 0 commits were understood as [conventional](https://www.conventionalcommits.org). - 0 issues like '(#ID)' were seen in commit messages @@ -25,6 +130,7 @@ A maintenance release without user-facing changes.
view details * **Uncategorized** + - Release gix-date v0.8.4, gix-utils v0.1.10, gix-actor v0.30.1, gix-object v0.41.1, gix-path v0.10.6, gix-glob v0.16.1, gix-quote v0.4.11, gix-attributes v0.22.1, gix-command v0.3.5, gix-filter v0.10.0, gix-commitgraph v0.24.1, gix-worktree-stream v0.10.0, gix-archive v0.10.0, gix-config-value v0.14.5, gix-ref v0.42.0, gix-sec v0.10.5, gix-config v0.35.0, gix-prompt v0.8.3, gix-url v0.27.1, gix-credentials v0.24.1, gix-ignore v0.11.1, gix-index v0.30.0, gix-worktree v0.31.0, gix-diff v0.41.0, gix-discover v0.30.0, gix-pathspec v0.7.0, gix-dir v0.1.0, gix-pack v0.48.0, gix-odb v0.58.0, gix-transport v0.41.1, gix-protocol v0.44.1, gix-revision v0.26.1, gix-refspec v0.22.1, gix-status v0.6.0, gix-submodule v0.9.0, gix-worktree-state v0.8.0, gix v0.59.0, gix-fsck v0.3.0, gitoxide-core v0.36.0, gitoxide v0.34.0, safety bump 10 crates ([`45b4470`](https://github.com/Byron/gitoxide/commit/45b447045bc826f252129c300c531acde2652c64)) - Prepare changelogs prior to release ([`f2e111f`](https://github.com/Byron/gitoxide/commit/f2e111f768fc1bc6182355261c20b63610cffec7)) - Merge branch 'tempfile-permissions' ([`7b44c7f`](https://github.com/Byron/gitoxide/commit/7b44c7ff1dc0b8875214d2673c7f52948cf04ff0)) - Release gix-tempfile v13.1.0, gix-lock v13.1.0, safety bump 12 crates ([`8430442`](https://github.com/Byron/gitoxide/commit/84304427dfe4d170c7732161b126961719f70059)) diff --git a/gix-odb/Cargo.toml b/gix-odb/Cargo.toml index 041f5a67b1d..7bda4ed5de9 100644 --- a/gix-odb/Cargo.toml +++ b/gix-odb/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "gix-odb" -version = "0.58.0" +version = "0.61.0" repository = "https://github.com/Byron/gitoxide" authors = ["Sebastian Thiel "] license = "MIT OR Apache-2.0" @@ -15,33 +15,25 @@ doctest = false [features] ## Data structures implement `serde::Serialize` and `serde::Deserialize`. -serde= ["dep:serde", "gix-hash/serde", "gix-object/serde", "gix-pack/serde"] +serde = ["dep:serde", "gix-hash/serde", "gix-object/serde", "gix-pack/serde"] [dependencies] -gix-features = { version = "^0.38.0", path = "../gix-features", features = ["rustsha1", "walkdir", "zlib", "crc32" ] } -gix-hash = { version = "^0.14.1", path = "../gix-hash" } -gix-date = { version = "^0.8.4", path = "../gix-date" } -gix-path = { version = "^0.10.6", path = "../gix-path" } -gix-quote = { version = "^0.4.11", path = "../gix-quote" } -gix-object = { version = "^0.41.1", path = "../gix-object" } -gix-pack = { version = "^0.48.0", path = "../gix-pack", default-features = false } -gix-fs = { version = "^0.10.0", path = "../gix-fs" } -serde = { version = "1.0.114", optional = true, default-features = false, features = ["derive"]} +gix-features = { version = "^0.38.2", path = "../gix-features", features = ["rustsha1", "walkdir", "zlib", "crc32"] } +gix-hash = { version = "^0.14.2", path = "../gix-hash" } +gix-date = { version = "^0.8.6", path = "../gix-date" } +gix-path = { version = "^0.10.7", path = "../gix-path" } +gix-quote = { version = "^0.4.12", path = "../gix-quote" } +gix-object = { version = "^0.42.2", path = "../gix-object" } +gix-pack = { version = "^0.51.0", path = "../gix-pack", default-features = false } +gix-fs = { version = "^0.11.1", path = "../gix-fs" } +serde = { version = "1.0.114", optional = true, default-features = false, features = ["derive"] } -tempfile = "3.1.0" +tempfile = "3.10.0" thiserror = "1.0.26" parking_lot = { version = "0.12.0" } arc-swap = "1.5.0" document-features = { version = "0.2.0", optional = true } -#[dev-dependencies] -#gix-testtools = { path = "../tests/tools"} -#gix-actor = { path = "../gix-actor" } -#pretty_assertions = "1.0.0" -#filetime = "0.2.15" -#maplit = "1.0.2" -#crossbeam-channel = "0.5.6" - [package.metadata.docs.rs] features = ["document-features", "serde"] diff --git a/gix-odb/src/alternate/mod.rs b/gix-odb/src/alternate/mod.rs index c4e9fc8c040..919a3249ebd 100644 --- a/gix-odb/src/alternate/mod.rs +++ b/gix-odb/src/alternate/mod.rs @@ -21,6 +21,7 @@ use std::{fs, io, path::PathBuf}; use gix_path::realpath::MAX_SYMLINKS; /// +#[allow(clippy::empty_docs)] pub mod parse; /// Returned by [`resolve()`] diff --git a/gix-odb/src/lib.rs b/gix-odb/src/lib.rs index 40d7c2aea90..f034fdaadc9 100644 --- a/gix-odb/src/lib.rs +++ b/gix-odb/src/lib.rs @@ -47,6 +47,7 @@ pub struct Cache { } /// +#[allow(clippy::empty_docs)] pub mod cache; /// @@ -69,6 +70,7 @@ pub fn sink(object_hash: gix_hash::Kind) -> Sink { mod sink; /// +#[allow(clippy::empty_docs)] pub mod find; /// An object database equivalent to `/dev/null`, dropping all objects stored into it. @@ -77,6 +79,7 @@ mod traits; pub use traits::{Header, HeaderExt, Write}; /// +#[allow(clippy::empty_docs)] pub mod write { /// The error type returned by the [`Write`](crate::Write) trait. pub type Error = Box; diff --git a/gix-odb/src/store_impls/dynamic/find.rs b/gix-odb/src/store_impls/dynamic/find.rs index 99b58d7477f..13608b7c876 100644 --- a/gix-odb/src/store_impls/dynamic/find.rs +++ b/gix-odb/src/store_impls/dynamic/find.rs @@ -1,4 +1,4 @@ -use std::{convert::TryInto, ops::Deref}; +use std::ops::Deref; use gix_pack::cache::DecodeEntry; @@ -19,6 +19,8 @@ pub(crate) mod error { LoadIndex(#[from] crate::store::load_index::Error), #[error(transparent)] LoadPack(#[from] std::io::Error), + #[error(transparent)] + EntryType(#[from] gix_pack::data::entry::decode::Error), #[error("Reached recursion limit of {} while resolving ref delta bases for {}", .max_depth, .id)] DeltaBaseRecursionLimit { /// the maximum recursion depth we encountered. @@ -144,19 +146,21 @@ where } }, }; - let entry = pack.entry(pack_offset); + let entry = pack.entry(pack_offset)?; let header_size = entry.header_size(); - let res = match pack.decode_entry( + let res = pack.decode_entry( entry, buffer, inflate, &|id, _out| { - index_file.pack_offset_by_id(id).map(|pack_offset| { - gix_pack::data::decode::entry::ResolvedBase::InPack(pack.entry(pack_offset)) - }) + let pack_offset = index_file.pack_offset_by_id(id)?; + pack.entry(pack_offset) + .ok() + .map(gix_pack::data::decode::entry::ResolvedBase::InPack) }, pack_cache, - ) { + ); + let res = match res { Ok(r) => Ok(( gix_object::Data { kind: r.kind, @@ -230,7 +234,7 @@ where let pack = possibly_pack .as_ref() .expect("pack to still be available like just now"); - let entry = pack.entry(pack_offset); + let entry = pack.entry(pack_offset)?; let header_size = entry.header_size(); pack.decode_entry( entry, @@ -239,10 +243,10 @@ where &|id, out| { index_file .pack_offset_by_id(id) - .map(|pack_offset| { - gix_pack::data::decode::entry::ResolvedBase::InPack( - pack.entry(pack_offset), - ) + .and_then(|pack_offset| { + pack.entry(pack_offset) + .ok() + .map(gix_pack::data::decode::entry::ResolvedBase::InPack) }) .or_else(|| { (id == base_id).then(|| { @@ -398,7 +402,7 @@ where } }, }; - let entry = pack.entry(pack_offset); + let entry = pack.entry(pack_offset).ok()?; buf.resize(entry.decompressed_size.try_into().expect("representable size"), 0); assert_eq!(pack.id, pack_id.to_intrinsic_pack_id(), "both ids must always match"); diff --git a/gix-odb/src/store_impls/dynamic/handle.rs b/gix-odb/src/store_impls/dynamic/handle.rs index 655bdfa430c..883e2c1ccb8 100644 --- a/gix-odb/src/store_impls/dynamic/handle.rs +++ b/gix-odb/src/store_impls/dynamic/handle.rs @@ -1,6 +1,5 @@ use std::{ cell::RefCell, - convert::{TryFrom, TryInto}, ops::Deref, rc::Rc, sync::{atomic::Ordering, Arc}, diff --git a/gix-odb/src/store_impls/dynamic/header.rs b/gix-odb/src/store_impls/dynamic/header.rs index 1e4370a91ee..2ebcc73efb4 100644 --- a/gix-odb/src/store_impls/dynamic/header.rs +++ b/gix-odb/src/store_impls/dynamic/header.rs @@ -72,10 +72,12 @@ where } }, }; - let entry = pack.entry(pack_offset); + let entry = pack.entry(pack_offset)?; let res = match pack.decode_header(entry, inflate, &|id| { - index_file.pack_offset_by_id(id).map(|pack_offset| { - gix_pack::data::decode::header::ResolvedBase::InPack(pack.entry(pack_offset)) + index_file.pack_offset_by_id(id).and_then(|pack_offset| { + pack.entry(pack_offset) + .ok() + .map(gix_pack::data::decode::header::ResolvedBase::InPack) }) }) { Ok(header) => Ok(header.into()), @@ -129,14 +131,14 @@ where let pack = possibly_pack .as_ref() .expect("pack to still be available like just now"); - let entry = pack.entry(pack_offset); + let entry = pack.entry(pack_offset)?; pack.decode_header(entry, inflate, &|id| { index_file .pack_offset_by_id(id) - .map(|pack_offset| { - gix_pack::data::decode::header::ResolvedBase::InPack( - pack.entry(pack_offset), - ) + .and_then(|pack_offset| { + pack.entry(pack_offset) + .ok() + .map(gix_pack::data::decode::header::ResolvedBase::InPack) }) .or_else(|| { (id == base_id).then(|| { diff --git a/gix-odb/src/store_impls/dynamic/init.rs b/gix-odb/src/store_impls/dynamic/init.rs index 980bacf767b..2031e48c837 100644 --- a/gix-odb/src/store_impls/dynamic/init.rs +++ b/gix-odb/src/store_impls/dynamic/init.rs @@ -1,4 +1,4 @@ -use std::{iter::FromIterator, path::PathBuf, sync::Arc}; +use std::{path::PathBuf, sync::Arc}; use arc_swap::ArcSwap; diff --git a/gix-odb/src/store_impls/dynamic/load_index.rs b/gix-odb/src/store_impls/dynamic/load_index.rs index a255f6c4b58..18afbc86958 100644 --- a/gix-odb/src/store_impls/dynamic/load_index.rs +++ b/gix-odb/src/store_impls/dynamic/load_index.rs @@ -4,7 +4,7 @@ use std::{ ops::Deref, path::{Path, PathBuf}, sync::{ - atomic::{AtomicU16, AtomicUsize, Ordering}, + atomic::{AtomicU16, Ordering}, Arc, }, time::SystemTime, @@ -86,7 +86,7 @@ impl super::Store { Ok(Some(self.collect_snapshot())) } else { // always compare to the latest state - // Nothing changed in the mean time, try to load another index… + // Nothing changed in the meantime, try to load another index… if self.load_next_index(index) { Ok(Some(self.collect_snapshot())) } else { @@ -119,7 +119,7 @@ impl super::Store { let slot = &self.files[index.slot_indices[slot_map_index]]; let _lock = slot.write.lock(); if slot.generation.load(Ordering::SeqCst) > index.generation { - // There is a disk consolidation in progress which just overwrote a slot that cold be disposed with some other + // There is a disk consolidation in progress which just overwrote a slot that could be disposed with some other // index, one we didn't intend to load. // Continue with the next slot index in the hope there is something else we can do… continue 'retry_with_next_slot_index; @@ -128,14 +128,18 @@ impl super::Store { let bundle_mut = Arc::make_mut(&mut bundle); if let Some(files) = bundle_mut.as_mut() { // these are always expected to be set, unless somebody raced us. We handle this later by retrying. - let _loaded_count = IncOnDrop(&index.loaded_indices); - match files.load_index(self.object_hash) { + let res = { + let res = files.load_index(self.object_hash); + slot.files.store(bundle); + index.loaded_indices.fetch_add(1, Ordering::SeqCst); + res + }; + match res { Ok(_) => { - slot.files.store(bundle); break 'retry_with_next_slot_index; } - Err(_) => { - slot.files.store(bundle); + Err(_err) => { + gix_features::trace::error!(err=?_err, "Failed to load index file - some objects may seem to not exist"); continue 'retry_with_next_slot_index; } } @@ -145,9 +149,14 @@ impl super::Store { // There can be contention as many threads start working at the same time and take all the // slots to load indices for. Some threads might just be left-over and have to wait for something // to change. - let num_load_operations = index.num_indices_currently_being_loaded.deref(); // TODO: potentially hot loop - could this be a condition variable? - while num_load_operations.load(Ordering::Relaxed) != 0 { + // This is a timing-based fix for the case that the `num_indices_being_loaded` isn't yet incremented, + // and we might break out here without actually waiting for the loading operation. Then we'd fail to + // observe a change and the underlying handler would not have all the indices it needs at its disposal. + // Yielding means we will definitely loose enough time to observe the ongoing operation, + // or its effects. + std::thread::yield_now(); + while index.num_indices_currently_being_loaded.load(Ordering::SeqCst) != 0 { std::thread::yield_now() } break 'retry_with_next_slot_index; @@ -197,7 +206,7 @@ impl super::Store { // We might not be able to detect by pointer if the state changed, as this itself is racy. So we keep track of double-initialization // using a flag, which means that if `needs_init` was true we saw the index uninitialized once, but now that we are here it's - // initialized meaning that somebody was faster and we couldn't detect it by comparisons to the index. + // initialized meaning that somebody was faster, and we couldn't detect it by comparisons to the index. // If so, make sure we collect the snapshot instead of returning None in case nothing actually changed, which is likely with a // race like this. if !was_uninitialized && needs_init { @@ -397,18 +406,19 @@ impl super::Store { // generation stays the same, as it's the same value still but scheduled for eventual removal. } } else { + // set the generation before we actually change the value, otherwise readers of old generations could observe the new one. + // We rather want them to turn around here and update their index, which, by that time, might actually already be available. + // If not, they would fail unable to load a pack or index they need, but that's preferred over returning wrong objects. + // Safety: can't race as we hold the lock, have to set the generation beforehand to help avoid others to observe the value. + slot.generation.store(generation, Ordering::SeqCst); *files_mut = None; }; slot.files.store(files); - if !needs_stable_indices { - // Not racy due to lock, generation must be set after unsetting the slot value AND storing it. - slot.generation.store(generation, Ordering::SeqCst); - } } let new_index = self.index.load(); Ok(if index.state_id() == new_index.state_id() { - // there was no change, and nothing was loaded in the meantime, reflect that in the return value to not get into loops + // there was no change, and nothing was loaded in the meantime, reflect that in the return value to not get into loops. None } else { if load_new_index { @@ -619,34 +629,44 @@ impl super::Store { } pub(crate) fn collect_snapshot(&self) -> Snapshot { + // We don't observe changes-on-disk in our 'wait-for-load' loop. + // That loop is meant to help assure the marker (which includes the amount of loaded indices) matches + // the actual amount of indices we collect. let index = self.index.load(); - let indices = if index.is_initialized() { - index - .slot_indices - .iter() - .map(|idx| (*idx, &self.files[*idx])) - .filter_map(|(id, file)| { - let lookup = match (**file.files.load()).as_ref()? { - types::IndexAndPacks::Index(bundle) => handle::SingleOrMultiIndex::Single { - index: bundle.index.loaded()?.clone(), - data: bundle.data.loaded().cloned(), - }, - types::IndexAndPacks::MultiIndex(multi) => handle::SingleOrMultiIndex::Multi { - index: multi.multi_index.loaded()?.clone(), - data: multi.data.iter().map(|f| f.loaded().cloned()).collect(), - }, - }; - handle::IndexLookup { file: lookup, id }.into() - }) - .collect() - } else { - Vec::new() - }; + loop { + if index.num_indices_currently_being_loaded.deref().load(Ordering::SeqCst) != 0 { + std::thread::yield_now(); + continue; + } + let marker = index.marker(); + let indices = if index.is_initialized() { + index + .slot_indices + .iter() + .map(|idx| (*idx, &self.files[*idx])) + .filter_map(|(id, file)| { + let lookup = match (**file.files.load()).as_ref()? { + types::IndexAndPacks::Index(bundle) => handle::SingleOrMultiIndex::Single { + index: bundle.index.loaded()?.clone(), + data: bundle.data.loaded().cloned(), + }, + types::IndexAndPacks::MultiIndex(multi) => handle::SingleOrMultiIndex::Multi { + index: multi.multi_index.loaded()?.clone(), + data: multi.data.iter().map(|f| f.loaded().cloned()).collect(), + }, + }; + handle::IndexLookup { file: lookup, id }.into() + }) + .collect() + } else { + Vec::new() + }; - Snapshot { - indices, - loose_dbs: Arc::clone(&index.loose_dbs), - marker: index.marker(), + return Snapshot { + indices, + loose_dbs: Arc::clone(&index.loose_dbs), + marker, + }; } } } @@ -669,13 +689,6 @@ impl<'a> Drop for IncOnNewAndDecOnDrop<'a> { } } -struct IncOnDrop<'a>(&'a AtomicUsize); -impl<'a> Drop for IncOnDrop<'a> { - fn drop(&mut self) { - self.0.fetch_add(1, Ordering::SeqCst); - } -} - pub(crate) enum Either { IndexPath(PathBuf), MultiIndexFile(Arc), diff --git a/gix-odb/src/store_impls/dynamic/mod.rs b/gix-odb/src/store_impls/dynamic/mod.rs index 7ea462b24a1..65ea5361415 100644 --- a/gix-odb/src/store_impls/dynamic/mod.rs +++ b/gix-odb/src/store_impls/dynamic/mod.rs @@ -51,20 +51,25 @@ impl RefreshMode { } /// +#[allow(clippy::empty_docs)] pub mod find; /// +#[allow(clippy::empty_docs)] pub mod prefix; mod header; /// +#[allow(clippy::empty_docs)] pub mod iter; /// +#[allow(clippy::empty_docs)] pub mod write; /// +#[allow(clippy::empty_docs)] pub mod init; pub(crate) mod types; @@ -73,9 +78,11 @@ pub use types::Metrics; pub(crate) mod handle; /// +#[allow(clippy::empty_docs)] pub mod load_index; /// +#[allow(clippy::empty_docs)] pub mod verify; mod load_one; @@ -85,4 +92,5 @@ mod metrics; mod access; /// +#[allow(clippy::empty_docs)] pub mod structure; diff --git a/gix-odb/src/store_impls/dynamic/prefix.rs b/gix-odb/src/store_impls/dynamic/prefix.rs index 0d671400448..b6869d8c2b6 100644 --- a/gix-odb/src/store_impls/dynamic/prefix.rs +++ b/gix-odb/src/store_impls/dynamic/prefix.rs @@ -5,6 +5,7 @@ use gix_object::Exists; use crate::store::{load_index, Handle}; /// +#[allow(clippy::empty_docs)] pub mod lookup { use crate::loose; @@ -24,6 +25,7 @@ pub mod lookup { } /// +#[allow(clippy::empty_docs)] pub mod disambiguate { /// A potentially ambiguous prefix for use with `Handle::disambiguate_prefix()`. #[derive(Debug, Copy, Clone)] diff --git a/gix-odb/src/store_impls/dynamic/types.rs b/gix-odb/src/store_impls/dynamic/types.rs index 473c587bbea..35257ee7ee8 100644 --- a/gix-odb/src/store_impls/dynamic/types.rs +++ b/gix-odb/src/store_impls/dynamic/types.rs @@ -18,7 +18,7 @@ pub(crate) type AtomicGeneration = AtomicU32; /// A way to indicate which pack indices we have seen already and which of them are loaded, along with an idea /// of whether stored `PackId`s are still usable. -#[derive(Default, Copy, Clone)] +#[derive(Default, Copy, Clone, Debug)] pub struct SlotIndexMarker { /// The generation the `loaded_until_index` belongs to. Indices of different generations are completely incompatible. /// This value changes once the internal representation is compacted, something that may happen only if there is no handle @@ -262,7 +262,7 @@ impl IndexAndPacks { } } - /// If we are garbage, put ourselves into the loaded state. Otherwise put ourselves back to unloaded. + /// If we are garbage, put ourselves into the loaded state. Otherwise, put ourselves back to unloaded. pub(crate) fn put_back(&mut self) { match self { IndexAndPacks::Index(bundle) => { diff --git a/gix-odb/src/store_impls/dynamic/verify.rs b/gix-odb/src/store_impls/dynamic/verify.rs index a59ee2c5367..b9a1f605911 100644 --- a/gix-odb/src/store_impls/dynamic/verify.rs +++ b/gix-odb/src/store_impls/dynamic/verify.rs @@ -13,6 +13,7 @@ use crate::{ }; /// +#[allow(clippy::empty_docs)] pub mod integrity { use std::{marker::PhantomData, path::PathBuf}; diff --git a/gix-odb/src/store_impls/loose/mod.rs b/gix-odb/src/store_impls/loose/mod.rs index 17e4a33d65a..3dddf03660a 100644 --- a/gix-odb/src/store_impls/loose/mod.rs +++ b/gix-odb/src/store_impls/loose/mod.rs @@ -50,10 +50,13 @@ fn hash_path(id: &gix_hash::oid, mut root: PathBuf) -> PathBuf { } /// +#[allow(clippy::empty_docs)] pub mod find; /// +#[allow(clippy::empty_docs)] pub mod iter; /// +#[allow(clippy::empty_docs)] pub mod verify; /// The type for an iterator over `Result)` @@ -63,4 +66,5 @@ pub struct Iter { } /// +#[allow(clippy::empty_docs)] pub mod write; diff --git a/gix-odb/src/store_impls/loose/verify.rs b/gix-odb/src/store_impls/loose/verify.rs index ae83c1d01cb..2bacfa0cd27 100644 --- a/gix-odb/src/store_impls/loose/verify.rs +++ b/gix-odb/src/store_impls/loose/verify.rs @@ -8,6 +8,7 @@ use gix_features::progress::{Count, DynNestedProgress, Progress}; use crate::{loose::Store, Write}; /// +#[allow(clippy::empty_docs)] pub mod integrity { /// The error returned by [`verify_integrity()`][super::Store::verify_integrity()]. #[derive(Debug, thiserror::Error)] diff --git a/gix-odb/src/store_impls/loose/write.rs b/gix-odb/src/store_impls/loose/write.rs index 2cac12d1814..a70351a1574 100644 --- a/gix-odb/src/store_impls/loose/write.rs +++ b/gix-odb/src/store_impls/loose/write.rs @@ -107,8 +107,16 @@ impl Store { impl Store { fn dest(&self) -> Result, Error> { + #[cfg_attr(not(unix), allow(unused_mut))] + let mut builder = tempfile::Builder::new(); + #[cfg(unix)] + { + use std::os::unix::fs::PermissionsExt; + let perms = std::fs::Permissions::from_mode(0o444); + builder.permissions(perms); + } Ok(hash::Write::new( - deflate::Write::new(NamedTempFile::new_in(&self.path).map_err(|err| Error::Io { + deflate::Write::new(builder.tempfile_in(&self.path).map_err(|err| Error::Io { source: err, message: "create named temp file in", path: self.path.to_owned(), @@ -144,24 +152,6 @@ impl Store { return Ok(id); } } - #[cfg(unix)] - if let Ok(mut perm) = object_path.metadata().map(|m| m.permissions()) { - use std::os::unix::fs::PermissionsExt; - /// For now we assume the default with standard umask. This can be more sophisticated, - /// but we have the bare minimum. - fn comp_mode(_mode: u32) -> u32 { - 0o444 - } - let new_mode = comp_mode(perm.mode()); - if (perm.mode() ^ new_mode) & !0o170000 != 0 { - perm.set_mode(new_mode); - std::fs::set_permissions(&object_path, perm).map_err(|err| Error::Io { - source: err, - message: "Failed to set permission bits", - path: object_path.clone(), - })?; - } - } res.map_err(|err| Error::Persist { source: err, target: object_path, diff --git a/gix-odb/tests/Cargo.toml b/gix-odb/tests/Cargo.toml index 2c26eb6b801..d42d1775af6 100644 --- a/gix-odb/tests/Cargo.toml +++ b/gix-odb/tests/Cargo.toml @@ -24,9 +24,10 @@ gix-date = { path = "../../gix-date" } gix-object = { path = "../../gix-object" } gix-pack = { path = "../../gix-pack" } -gix-testtools = { path = "../../tests/tools"} +gix-testtools = { path = "../../tests/tools" } gix-actor = { path = "../../gix-actor" } pretty_assertions = "1.0.0" filetime = "0.2.15" maplit = "1.0.2" +crossbeam-channel = "0.5.13" diff --git a/gix-odb/tests/fixtures/generated-archives/make_replaced_history.tar.xz b/gix-odb/tests/fixtures/generated-archives/make_replaced_history.tar.xz index a00c1703c01..a30dfd02a15 100644 Binary files a/gix-odb/tests/fixtures/generated-archives/make_replaced_history.tar.xz and b/gix-odb/tests/fixtures/generated-archives/make_replaced_history.tar.xz differ diff --git a/gix-odb/tests/fixtures/generated-archives/make_repo_multi_index.tar.xz b/gix-odb/tests/fixtures/generated-archives/make_repo_multi_index.tar.xz index 6b0aa4c1e87..03c2e9c071f 100644 Binary files a/gix-odb/tests/fixtures/generated-archives/make_repo_multi_index.tar.xz and b/gix-odb/tests/fixtures/generated-archives/make_repo_multi_index.tar.xz differ diff --git a/gix-odb/tests/fixtures/make_alternates_odb.sh b/gix-odb/tests/fixtures/make_alternates_odb.sh index 03b9964383f..85bafc0fd82 100755 --- a/gix-odb/tests/fixtures/make_alternates_odb.sh +++ b/gix-odb/tests/fixtures/make_alternates_odb.sh @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash set -eu -o pipefail git init -q diff --git a/gix-odb/tests/fixtures/make_replaced_history.sh b/gix-odb/tests/fixtures/make_replaced_history.sh index d4d0161b4c7..416f682bf93 100755 --- a/gix-odb/tests/fixtures/make_replaced_history.sh +++ b/gix-odb/tests/fixtures/make_replaced_history.sh @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash set -eu -o pipefail git init -q diff --git a/gix-odb/tests/fixtures/make_repo_multi_index.sh b/gix-odb/tests/fixtures/make_repo_multi_index.sh index 43decae9124..abbdfb9cce1 100755 --- a/gix-odb/tests/fixtures/make_repo_multi_index.sh +++ b/gix-odb/tests/fixtures/make_repo_multi_index.sh @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash set -eu -o pipefail omit_multi_index=${1:-no} diff --git a/gix-odb/tests/fixtures/repo_with_loose_objects.sh b/gix-odb/tests/fixtures/repo_with_loose_objects.sh index cb055cb351d..dd6f8db5621 100755 --- a/gix-odb/tests/fixtures/repo_with_loose_objects.sh +++ b/gix-odb/tests/fixtures/repo_with_loose_objects.sh @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash set -eu -o pipefail git init -q diff --git a/gix-odb/tests/odb/find/mod.rs b/gix-odb/tests/odb/find/mod.rs index 3a93a03f1af..77d515e9505 100644 --- a/gix-odb/tests/odb/find/mod.rs +++ b/gix-odb/tests/odb/find/mod.rs @@ -16,7 +16,7 @@ fn can_find(db: impl gix_object::Find, hex_id: &str) { #[test] fn loose_object() { - can_find(&db(), "37d4e6c5c48ba0d245164c4e10d5f41140cab980"); + can_find(db(), "37d4e6c5c48ba0d245164c4e10d5f41140cab980"); } #[test] diff --git a/gix-odb/tests/odb/header/mod.rs b/gix-odb/tests/odb/header/mod.rs index 4094a7399c8..8e059729316 100644 --- a/gix-odb/tests/odb/header/mod.rs +++ b/gix-odb/tests/odb/header/mod.rs @@ -8,7 +8,7 @@ fn find_header(db: impl gix_odb::Header, hex_id: &str) -> gix_odb::find::Header #[test] fn loose_object() { - find_header(&db(), "37d4e6c5c48ba0d245164c4e10d5f41140cab980"); + find_header(db(), "37d4e6c5c48ba0d245164c4e10d5f41140cab980"); } #[test] diff --git a/gix-odb/tests/odb/regression/mod.rs b/gix-odb/tests/odb/regression/mod.rs index 1b8f5de0b0a..c6bffa61125 100644 --- a/gix-odb/tests/odb/regression/mod.rs +++ b/gix-odb/tests/odb/regression/mod.rs @@ -17,12 +17,13 @@ mod repo_with_small_packs { } #[test] - #[cfg(feature = "internal-testing-gix-features-parallel")] + #[cfg(feature = "gix-features-parallel")] fn multi_threaded_access_will_not_panic() -> crate::Result { for arg in ["no", "without-multi-index"] { - let base = gix_testtools::scripted_fixture_read_only_with_args("make_repo_multi_index.sh", Some(arg))? - .join(".git") - .join("objects"); + let base = + gix_testtools::scripted_fixture_read_only_with_args_standalone("make_repo_multi_index.sh", Some(arg))? + .join(".git") + .join("objects"); let store = gix_odb::at(base)?; let (tx, barrier) = crossbeam_channel::unbounded::<()>(); let handles = (0..std::thread::available_parallelism()?.get()).map(|tid| { @@ -36,7 +37,7 @@ mod repo_with_small_packs { for id in store.iter()? { let id = id?; assert!( - store.try_find(id, &mut buf).is_ok(), + store.try_find(&id, &mut buf).is_ok(), "Thread {} could not find {}", tid, id diff --git a/gix-pack/CHANGELOG.md b/gix-pack/CHANGELOG.md index 58ef8b5519c..827dcca5f28 100644 --- a/gix-pack/CHANGELOG.md +++ b/gix-pack/CHANGELOG.md @@ -5,6 +5,122 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## 0.51.0 (2024-05-22) + +### Bug Fixes (BREAKING) + + - don't panic when unknown entry types are encountered. + Related to https://github.com/helix-editor/helix/issues/10660 + which runs into object types that are unknown. + + I have looked into this and [couldn't find evidence of a new pack-entry type](https://github.com/git/git/blob/0f3415f1f8478b05e64db11eb8aaa2915e48fef6/packfile.c#L1303-L1358) + in the Git codebase. + + It also looks like that Git [will never write packs that aren't V2](https://github.com/git/git/blob/0f3415f1f8478b05e64db11eb8aaa2915e48fef6/pack-write.c#L352) + - initially I thought it might write V3 based on some other criteria. + +### Commit Statistics + + + + - 4 commits contributed to the release over the course of 9 calendar days. + - 38 days passed between releases. + - 1 commit was understood as [conventional](https://www.conventionalcommits.org). + - 0 issues like '(#ID)' were seen in commit messages + +### Commit Details + + + +
view details + + * **Uncategorized** + - Adjust changelogs prior to release ([`9511416`](https://github.com/Byron/gitoxide/commit/9511416a6cd0c571233f958c165329c8705c2498)) + - Merge branch 'status' ([`04ef31e`](https://github.com/Byron/gitoxide/commit/04ef31e9d6f5332d49037a5a4c248ebbb5aaf92b)) + - Adapt to changes in `gix-pack` ([`bad5b48`](https://github.com/Byron/gitoxide/commit/bad5b48e4f0d865b0b0937f136d9a0041aa88046)) + - Don't panic when unknown entry types are encountered. ([`b32a847`](https://github.com/Byron/gitoxide/commit/b32a847e10b742834169fac56c284645c1ed28f3)) +
+ + +For now, the thesis is that one would have to be able to mark bad objectsto be able to handle this more gracefully, but all we can do is try to fail. + +## 0.50.0 (2024-04-13) + +### Bug Fixes (BREAKING) + + - Make `topo` more similar to `Ancestors`, but also rename `Ancestors` to `Simple` + +### Commit Statistics + + + + - 7 commits contributed to the release over the course of 5 calendar days. + - 29 days passed between releases. + - 1 commit was understood as [conventional](https://www.conventionalcommits.org). + - 0 issues like '(#ID)' were seen in commit messages + +### Thanks Clippy + + + +[Clippy](https://github.com/rust-lang/rust-clippy) helped 1 time to make code idiomatic. + +### Commit Details + + + +
view details + + * **Uncategorized** + - Release gix-trace v0.1.9, gix-utils v0.1.12, gix-packetline-blocking v0.17.4, gix-filter v0.11.1, gix-fs v0.10.2, gix-traverse v0.39.0, gix-worktree-stream v0.12.0, gix-archive v0.12.0, gix-config v0.36.1, gix-url v0.27.3, gix-index v0.32.0, gix-worktree v0.33.0, gix-diff v0.43.0, gix-pathspec v0.7.3, gix-dir v0.4.0, gix-pack v0.50.0, gix-odb v0.60.0, gix-transport v0.42.0, gix-protocol v0.45.0, gix-status v0.9.0, gix-worktree-state v0.10.0, gix v0.62.0, gix-fsck v0.4.0, gitoxide-core v0.37.0, gitoxide v0.35.0, safety bump 14 crates ([`095c673`](https://github.com/Byron/gitoxide/commit/095c6739b2722a8b9af90776b435ef2da454c0e6)) + - Prepare changelogs prior to release ([`5755271`](https://github.com/Byron/gitoxide/commit/57552717f46f96c35ba4ddc0a64434354ef845e9)) + - Merge branch 'add-topo-walk' ([`b590a9d`](https://github.com/Byron/gitoxide/commit/b590a9d2b6a273f76f0320d2b9fe1f679c08f549)) + - Adapt to changes in `gix-traverse` ([`1cfeb11`](https://github.com/Byron/gitoxide/commit/1cfeb11f1fe9ad9c7b9084840ed7f5c5877f2f9a)) + - Make `topo` more similar to `Ancestors`, but also rename `Ancestors` to `Simple` ([`2a9c178`](https://github.com/Byron/gitoxide/commit/2a9c178326b7f13ba6bc1f89fc2b9d9facbecf48)) + - Adapt to changes in `gix-traverse` ([`6154bf3`](https://github.com/Byron/gitoxide/commit/6154bf3a346d69f9749271d50e4f3aacdcbad4d0)) + - Thanks clippy ([`7f6bee5`](https://github.com/Byron/gitoxide/commit/7f6bee5452ee01638f89a0cec2d4ee2a6f0d0136)) +
+ +## 0.49.0 (2024-03-14) + + + +### Chore + + - remove repetitive words + +### Bug Fixes + + - assure memory maps are created with `MAP_PRIVATE` + That way, the mmap process should work under more circumstances. + +### Commit Statistics + + + + - 8 commits contributed to the release over the course of 5 calendar days. + - 18 days passed between releases. + - 2 commits were understood as [conventional](https://www.conventionalcommits.org). + - 1 unique issue was worked on: [#1312](https://github.com/Byron/gitoxide/issues/1312) + +### Commit Details + + + +
view details + + * **[#1312](https://github.com/Byron/gitoxide/issues/1312)** + - Assure memory maps are created with `MAP_PRIVATE` ([`88061a1`](https://github.com/Byron/gitoxide/commit/88061a176b2f4b5a377a4cff513979ddb1e306a1)) + * **Uncategorized** + - Release gix-date v0.8.5, gix-hash v0.14.2, gix-trace v0.1.8, gix-utils v0.1.11, gix-features v0.38.1, gix-actor v0.31.0, gix-validate v0.8.4, gix-object v0.42.0, gix-path v0.10.7, gix-glob v0.16.2, gix-quote v0.4.12, gix-attributes v0.22.2, gix-command v0.3.6, gix-filter v0.11.0, gix-fs v0.10.1, gix-chunk v0.4.8, gix-commitgraph v0.24.2, gix-hashtable v0.5.2, gix-revwalk v0.13.0, gix-traverse v0.38.0, gix-worktree-stream v0.11.0, gix-archive v0.11.0, gix-config-value v0.14.6, gix-tempfile v13.1.1, gix-lock v13.1.1, gix-ref v0.43.0, gix-sec v0.10.6, gix-config v0.36.0, gix-prompt v0.8.4, gix-url v0.27.2, gix-credentials v0.24.2, gix-ignore v0.11.2, gix-bitmap v0.2.11, gix-index v0.31.0, gix-worktree v0.32.0, gix-diff v0.42.0, gix-discover v0.31.0, gix-pathspec v0.7.1, gix-dir v0.2.0, gix-macros v0.1.4, gix-mailmap v0.23.0, gix-negotiate v0.13.0, gix-pack v0.49.0, gix-odb v0.59.0, gix-packetline v0.17.4, gix-transport v0.41.2, gix-protocol v0.44.2, gix-revision v0.27.0, gix-refspec v0.23.0, gix-status v0.7.0, gix-submodule v0.10.0, gix-worktree-state v0.9.0, gix v0.60.0, safety bump 26 crates ([`b050327`](https://github.com/Byron/gitoxide/commit/b050327e76f234b19be921b78b7b28e034319fdb)) + - Prepare changelogs prior to release ([`52c3bbd`](https://github.com/Byron/gitoxide/commit/52c3bbd36b9e94a0f3a78b4ada84d0c08eba27f6)) + - Merge branch 'status' ([`3e5c974`](https://github.com/Byron/gitoxide/commit/3e5c974dd62ac134711c6c2f5a5490187a6ea55e)) + - Merge pull request #1314 from avoidalone/main ([`5722e3a`](https://github.com/Byron/gitoxide/commit/5722e3aeeba5dd44e38a6cdbb70717a45345307e)) + - Remove repetitive words ([`39879af`](https://github.com/Byron/gitoxide/commit/39879af6eaf2bf4fe159a5c6371c98d516c4febe)) + - Fix lints for nightly, and clippy ([`f8ce3d0`](https://github.com/Byron/gitoxide/commit/f8ce3d0721b6a53713a9392f2451874f520bc44c)) + - Merge branch 'mmap-mode' ([`9e9b9fe`](https://github.com/Byron/gitoxide/commit/9e9b9fe6f63759d3bf479a8ec2f9dd1fbef87a08)) +
+ ## 0.48.0 (2024-02-25) A maintenance release without user-facing changes. @@ -13,7 +129,7 @@ A maintenance release without user-facing changes. - - 10 commits contributed to the release over the course of 30 calendar days. + - 11 commits contributed to the release over the course of 30 calendar days. - 36 days passed between releases. - 0 commits were understood as [conventional](https://www.conventionalcommits.org). - 0 issues like '(#ID)' were seen in commit messages @@ -31,6 +147,7 @@ A maintenance release without user-facing changes.
view details * **Uncategorized** + - Release gix-date v0.8.4, gix-utils v0.1.10, gix-actor v0.30.1, gix-object v0.41.1, gix-path v0.10.6, gix-glob v0.16.1, gix-quote v0.4.11, gix-attributes v0.22.1, gix-command v0.3.5, gix-filter v0.10.0, gix-commitgraph v0.24.1, gix-worktree-stream v0.10.0, gix-archive v0.10.0, gix-config-value v0.14.5, gix-ref v0.42.0, gix-sec v0.10.5, gix-config v0.35.0, gix-prompt v0.8.3, gix-url v0.27.1, gix-credentials v0.24.1, gix-ignore v0.11.1, gix-index v0.30.0, gix-worktree v0.31.0, gix-diff v0.41.0, gix-discover v0.30.0, gix-pathspec v0.7.0, gix-dir v0.1.0, gix-pack v0.48.0, gix-odb v0.58.0, gix-transport v0.41.1, gix-protocol v0.44.1, gix-revision v0.26.1, gix-refspec v0.22.1, gix-status v0.6.0, gix-submodule v0.9.0, gix-worktree-state v0.8.0, gix v0.59.0, gix-fsck v0.3.0, gitoxide-core v0.36.0, gitoxide v0.34.0, safety bump 10 crates ([`45b4470`](https://github.com/Byron/gitoxide/commit/45b447045bc826f252129c300c531acde2652c64)) - Prepare changelogs prior to release ([`f2e111f`](https://github.com/Byron/gitoxide/commit/f2e111f768fc1bc6182355261c20b63610cffec7)) - Thanks clippy ([`13d5602`](https://github.com/Byron/gitoxide/commit/13d5602faa58aa6f520ebc6003ed54bc9c844f2b)) - Merge branch 'tempfile-permissions' ([`7b44c7f`](https://github.com/Byron/gitoxide/commit/7b44c7ff1dc0b8875214d2673c7f52948cf04ff0)) diff --git a/gix-pack/Cargo.toml b/gix-pack/Cargo.toml index 4d353d7aa90..8803ef1642c 100644 --- a/gix-pack/Cargo.toml +++ b/gix-pack/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "gix-pack" -version = "0.48.0" +version = "0.51.0" repository = "https://github.com/Byron/gitoxide" authors = ["Sebastian Thiel "] license = "MIT OR Apache-2.0" @@ -32,16 +32,16 @@ serde = ["dep:serde", "gix-object/serde"] wasm = ["gix-diff?/wasm"] [dependencies] -gix-features = { version = "^0.38.0", path = "../gix-features", features = ["crc32", "rustsha1", "progress", "zlib"] } -gix-path = { version = "^0.10.6", path = "../gix-path" } -gix-hash = { version = "^0.14.1", path = "../gix-hash" } -gix-chunk = { version = "^0.4.7", path = "../gix-chunk" } -gix-object = { version = "^0.41.1", path = "../gix-object" } -gix-hashtable = { version = "^0.5.1", path = "../gix-hashtable" } +gix-features = { version = "^0.38.2", path = "../gix-features", features = ["crc32", "rustsha1", "progress", "zlib"] } +gix-path = { version = "^0.10.7", path = "../gix-path" } +gix-hash = { version = "^0.14.2", path = "../gix-hash" } +gix-chunk = { version = "^0.4.8", path = "../gix-chunk" } +gix-object = { version = "^0.42.2", path = "../gix-object" } +gix-hashtable = { version = "^0.5.2", path = "../gix-hashtable" } # for streaming of packs (input, output) -gix-traverse = { version = "^0.37.0", path = "../gix-traverse", optional = true } -gix-diff = { version = "^0.41.0", path = "../gix-diff", default-features = false, optional = true } +gix-traverse = { version = "^0.39.1", path = "../gix-traverse", optional = true } +gix-diff = { version = "^0.44.0", path = "../gix-diff", default-features = false, optional = true } memmap2 = "0.9.0" smallvec = "1.3.0" @@ -58,7 +58,7 @@ document-features = { version = "0.2.0", optional = true } [target.'cfg(not(target_arch = "wasm32"))'.dependencies] -gix-tempfile = { version = "^13.0.0", default-features = false, path = "../gix-tempfile" } +gix-tempfile = { version = "^14.0.0", default-features = false, path = "../gix-tempfile" } [dev-dependencies] gix-testtools = { path = "../tests/tools"} diff --git a/gix-pack/src/bundle/find.rs b/gix-pack/src/bundle/find.rs index 98e28333d48..4a7fd23129d 100644 --- a/gix-pack/src/bundle/find.rs +++ b/gix-pack/src/bundle/find.rs @@ -37,7 +37,7 @@ impl crate::Bundle { cache: &mut dyn crate::cache::DecodeEntry, ) -> Result<(gix_object::Data<'a>, crate::data::entry::Location), crate::data::decode::Error> { let ofs = self.index.pack_offset_at_index(idx); - let pack_entry = self.pack.entry(ofs); + let pack_entry = self.pack.entry(ofs)?; let header_size = pack_entry.header_size(); self.pack .decode_entry( @@ -45,11 +45,11 @@ impl crate::Bundle { out, inflate, &|id, _out| { - self.index.lookup(id).map(|idx| { - crate::data::decode::entry::ResolvedBase::InPack( - self.pack.entry(self.index.pack_offset_at_index(idx)), - ) - }) + let idx = self.index.lookup(id)?; + self.pack + .entry(self.index.pack_offset_at_index(idx)) + .ok() + .map(crate::data::decode::entry::ResolvedBase::InPack) }, cache, ) diff --git a/gix-pack/src/bundle/mod.rs b/gix-pack/src/bundle/mod.rs index d8ef1107d70..473e51dbf72 100644 --- a/gix-pack/src/bundle/mod.rs +++ b/gix-pack/src/bundle/mod.rs @@ -1,4 +1,5 @@ /// +#[allow(clippy::empty_docs)] pub mod init; mod find; @@ -7,12 +8,14 @@ mod find; pub mod write; /// +#[allow(clippy::empty_docs)] pub mod verify { use std::sync::atomic::AtomicBool; use gix_features::progress::DynNestedProgress; /// + #[allow(clippy::empty_docs)] pub mod integrity { /// Returned by [`Bundle::verify_integrity()`][crate::Bundle::verify_integrity()]. pub struct Outcome { diff --git a/gix-pack/src/bundle/write/mod.rs b/gix-pack/src/bundle/write/mod.rs index 9ddd1b9d3a5..8f45090ab3e 100644 --- a/gix-pack/src/bundle/write/mod.rs +++ b/gix-pack/src/bundle/write/mod.rs @@ -51,8 +51,8 @@ impl crate::Bundle { /// * `progress` provides detailed progress information which can be discarded with [`gix_features::progress::Discard`]. /// * `should_interrupt` is checked regularly and when true, the whole operation will stop. /// * `thin_pack_base_object_lookup` If set, we expect to see a thin-pack with objects that reference their base object by object id which is - /// expected to exist in the object database the bundle is contained within. - /// `options` further configure how the task is performed. + /// expected to exist in the object database the bundle is contained within. + /// `options` further configure how the task is performed. /// /// # Note /// @@ -300,31 +300,46 @@ impl crate::Bundle { )?; drop(pack_entries_iter); - let data_path = directory.join(format!("pack-{}.pack", outcome.data_hash.to_hex())); - let index_path = data_path.with_extension("idx"); - let keep_path = data_path.with_extension("keep"); + if outcome.num_objects == 0 { + WriteOutcome { + outcome, + data_path: None, + index_path: None, + keep_path: None, + } + } else { + let data_path = directory.join(format!("pack-{}.pack", outcome.data_hash.to_hex())); + let index_path = data_path.with_extension("idx"); + let keep_path = if data_path.is_file() { + // avoid trying to overwrite existing files, we know they have the same content + // and this is likely to fail on Windows as negotiation opened the pack. + None + } else { + let keep_path = data_path.with_extension("keep"); - std::fs::write(&keep_path, b"")?; - Arc::try_unwrap(data_file) - .expect("only one handle left after pack was consumed") - .into_inner() - .into_inner() - .map_err(|err| Error::from(err.into_error()))? - .persist(&data_path)?; - index_file - .persist(&index_path) - .map_err(|err| { - progress.info(format!( - "pack file at {} is retained despite failing to move the index file into place. You can use plumbing to make it usable.", - data_path.display() - )); - err - })?; - WriteOutcome { - outcome, - data_path: Some(data_path), - index_path: Some(index_path), - keep_path: Some(keep_path), + std::fs::write(&keep_path, b"")?; + Arc::try_unwrap(data_file) + .expect("only one handle left after pack was consumed") + .into_inner() + .into_inner() + .map_err(|err| Error::from(err.into_error()))? + .persist(&data_path)?; + Some(keep_path) + }; + if !index_path.is_file() { + index_file + .persist(&index_path) + .map_err(|err| { + gix_features::trace::warn!("pack file at \"{}\" is retained despite failing to move the index file into place. You can use plumbing to make it usable.",data_path.display()); + err + })?; + } + WriteOutcome { + outcome, + data_path: Some(data_path), + index_path: Some(index_path), + keep_path, + } } } None => WriteOutcome { diff --git a/gix-pack/src/bundle/write/types.rs b/gix-pack/src/bundle/write/types.rs index 9ade3ed0a52..e188d097b72 100644 --- a/gix-pack/src/bundle/write/types.rs +++ b/gix-pack/src/bundle/write/types.rs @@ -33,18 +33,20 @@ impl Default for Options { #[derive(PartialEq, Eq, Debug, Hash, Ord, PartialOrd, Clone)] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] pub struct Outcome { - /// The successful result of the index write operation + /// The successful result of the index write operation. pub index: crate::index::write::Outcome, - /// The version of the pack + /// The version of the pack. pub pack_version: crate::data::Version, - /// The kind of hash stored within the pack and indices + /// The kind of hash stored within the pack and indices. pub object_hash: gix_hash::Kind, - /// The path to the pack index file + /// The path to the pack index file. pub index_path: Option, - /// The path to the pack data file + /// The path to the pack data file. pub data_path: Option, /// The path to the `.keep` file to prevent collection of the newly written pack until refs are pointing to it. + /// It might be `None` if the file at `data_path` already existed, indicating that we have received a pack that + /// was already present locally. /// /// The file is created right before moving the pack data and index data into place (i.e. `data_path` and `index_path`) /// and is expected to be removed by the caller when ready. diff --git a/gix-pack/src/cache/delta/from_offsets.rs b/gix-pack/src/cache/delta/from_offsets.rs index d790dcc0f71..4b05bed4e56 100644 --- a/gix-pack/src/cache/delta/from_offsets.rs +++ b/gix-pack/src/cache/delta/from_offsets.rs @@ -1,5 +1,4 @@ use std::{ - convert::TryFrom, fs, io, io::{BufRead, Read, Seek, SeekFrom}, sync::atomic::{AtomicBool, Ordering}, @@ -32,13 +31,13 @@ const PACK_HEADER_LEN: usize = 12; impl Tree { /// Create a new `Tree` from any data sorted by offset, ascending as returned by the `data_sorted_by_offsets` iterator. /// * `get_pack_offset(item: &T) -> data::Offset` is a function returning the pack offset of the given item, which can be used - /// for obtaining the objects entry within the pack. + /// for obtaining the objects entry within the pack. /// * `pack_path` is the path to the pack file itself and from which to read the entry data, which is a pack file matching the offsets - /// returned by `get_pack_offset(…)`. + /// returned by `get_pack_offset(…)`. /// * `progress` is used to track progress when creating the tree. /// * `resolve_in_pack_id(gix_hash::oid) -> Option` takes an object ID and tries to resolve it to an object within this pack if - /// possible. Failing to do so aborts the operation, and this function is not expected to be called in usual packs. It's a theoretical - /// possibility though as old packs might have referred to their objects using the 20 bytes hash, instead of their encoded offset from the base. + /// possible. Failing to do so aborts the operation, and this function is not expected to be called in usual packs. It's a theoretical + /// possibility though as old packs might have referred to their objects using the 20 bytes hash, instead of their encoded offset from the base. /// /// Note that the sort order is ascending. The given pack file path must match the provided offsets. pub fn from_offsets_in_pack( @@ -58,12 +57,14 @@ impl Tree { })?, ); - let anticipated_num_objects = if let Some(num_objects) = data_sorted_by_offsets.size_hint().1 { - progress.init(Some(num_objects), progress::count("objects")); - num_objects - } else { - 0 - }; + let anticipated_num_objects = data_sorted_by_offsets + .size_hint() + .1 + .map(|num_objects| { + progress.init(Some(num_objects), progress::count("objects")); + num_objects + }) + .unwrap_or_default(); let mut tree = Tree::with_capacity(anticipated_num_objects)?; { diff --git a/gix-pack/src/cache/delta/mod.rs b/gix-pack/src/cache/delta/mod.rs index c923b5d1078..161432e8c2b 100644 --- a/gix-pack/src/cache/delta/mod.rs +++ b/gix-pack/src/cache/delta/mod.rs @@ -12,9 +12,11 @@ pub enum Error { } /// +#[allow(clippy::empty_docs)] pub mod traverse; /// +#[allow(clippy::empty_docs)] pub mod from_offsets; /// Tree datastructure diff --git a/gix-pack/src/cache/delta/traverse/mod.rs b/gix-pack/src/cache/delta/traverse/mod.rs index ea0c9c93be9..290f389bbe4 100644 --- a/gix-pack/src/cache/delta/traverse/mod.rs +++ b/gix-pack/src/cache/delta/traverse/mod.rs @@ -26,6 +26,8 @@ pub enum Error { }, #[error("The resolver failed to obtain the pack entry bytes for the entry at {pack_offset}")] ResolveFailed { pack_offset: u64 }, + #[error(transparent)] + EntryType(#[from] crate::data::entry::decode::Error), #[error("One of the object inspectors failed")] Inspect(#[from] Box), #[error("Interrupted")] diff --git a/gix-pack/src/cache/delta/traverse/resolve.rs b/gix-pack/src/cache/delta/traverse/resolve.rs index 92bff6b03e8..56d7432e776 100644 --- a/gix-pack/src/cache/delta/traverse/resolve.rs +++ b/gix-pack/src/cache/delta/traverse/resolve.rs @@ -128,7 +128,7 @@ where let bytes = resolve(slice.clone(), resolve_data).ok_or(Error::ResolveFailed { pack_offset: slice.start, })?; - let entry = data::Entry::from_bytes(bytes, slice.start, hash_len); + let entry = data::Entry::from_bytes(bytes, slice.start, hash_len)?; let compressed = &bytes[entry.header_size()..]; let decompressed_len = entry.decompressed_size as usize; decompress_all_at_once_with(&mut inflate, compressed, decompressed_len, out)?; @@ -304,7 +304,7 @@ where let bytes = resolve(slice.clone(), resolve_data).ok_or(Error::ResolveFailed { pack_offset: slice.start, })?; - let entry = data::Entry::from_bytes(bytes, slice.start, hash_len); + let entry = data::Entry::from_bytes(bytes, slice.start, hash_len)?; let compressed = &bytes[entry.header_size()..]; let decompressed_len = entry.decompressed_size as usize; decompress_all_at_once_with(&mut inflate, compressed, decompressed_len, out)?; diff --git a/gix-pack/src/cache/lru.rs b/gix-pack/src/cache/lru.rs index 4eef90306a1..f670037261d 100644 --- a/gix-pack/src/cache/lru.rs +++ b/gix-pack/src/cache/lru.rs @@ -133,7 +133,7 @@ mod _static { if data.len() > self.mem_limit { return; } - // If we could hold it but are are at limit, all we can do is make space. + // If we could hold it but are at limit, all we can do is make space. let mem_free = self.mem_limit - self.mem_used; if data.len() > mem_free { // prefer freeing free-lists instead of clearing our cache diff --git a/gix-pack/src/cache/mod.rs b/gix-pack/src/cache/mod.rs index eabb6a3ed85..d41467c05b9 100644 --- a/gix-pack/src/cache/mod.rs +++ b/gix-pack/src/cache/mod.rs @@ -52,6 +52,7 @@ pub mod lru; pub mod object; /// +#[allow(clippy::empty_docs)] pub(crate) mod delta; /// Replaces content of the given `Vec` with the slice. The vec will have the same length diff --git a/gix-pack/src/data/entry/decode.rs b/gix-pack/src/data/entry/decode.rs index 37b8560ac03..6b38be0f33d 100644 --- a/gix-pack/src/data/entry/decode.rs +++ b/gix-pack/src/data/entry/decode.rs @@ -5,6 +5,14 @@ use gix_features::decode::{leb64, leb64_from_read}; use super::{BLOB, COMMIT, OFS_DELTA, REF_DELTA, TAG, TREE}; use crate::data; +/// The error returned by [data::Entry::from_bytes()]. +#[derive(Debug, thiserror::Error)] +#[allow(missing_docs)] +#[error("Object type {type_id} is unsupported")] +pub struct Error { + pub type_id: u8, +} + /// Decoding impl data::Entry { /// Decode an entry from the given entry data `d`, providing the `pack_offset` to allow tracking the start of the entry data section. @@ -12,7 +20,7 @@ impl data::Entry { /// # Panics /// /// If we cannot understand the header, garbage data is likely to trigger this. - pub fn from_bytes(d: &[u8], pack_offset: data::Offset, hash_len: usize) -> data::Entry { + pub fn from_bytes(d: &[u8], pack_offset: data::Offset, hash_len: usize) -> Result { let (type_id, size, mut consumed) = parse_header_info(d); use crate::data::entry::Header::*; @@ -36,21 +44,17 @@ impl data::Entry { TREE => Tree, COMMIT => Commit, TAG => Tag, - _ => panic!("We currently don't support any V3 features or extensions"), + other => return Err(Error { type_id: other }), }; - data::Entry { + Ok(data::Entry { header: object, decompressed_size: size, data_offset: pack_offset + consumed as u64, - } + }) } /// Instantiate an `Entry` from the reader `r`, providing the `pack_offset` to allow tracking the start of the entry data section. - pub fn from_read( - r: &mut dyn io::Read, - pack_offset: data::Offset, - hash_len: usize, - ) -> Result { + pub fn from_read(r: &mut dyn io::Read, pack_offset: data::Offset, hash_len: usize) -> io::Result { let (type_id, size, mut consumed) = streaming_parse_header_info(r)?; use crate::data::entry::Header::*; @@ -78,7 +82,12 @@ impl data::Entry { TREE => Tree, COMMIT => Commit, TAG => Tag, - _ => panic!("We currently don't support any V3 features or extensions"), + other => { + return Err(io::Error::new( + io::ErrorKind::Other, + format!("Object type {other} is unsupported"), + )) + } }; Ok(data::Entry { header: object, diff --git a/gix-pack/src/data/entry/mod.rs b/gix-pack/src/data/entry/mod.rs index 6225cbf0115..13788663b19 100644 --- a/gix-pack/src/data/entry/mod.rs +++ b/gix-pack/src/data/entry/mod.rs @@ -45,7 +45,9 @@ impl Entry { } } -mod decode; +/// +#[allow(clippy::empty_docs)] +pub mod decode; mod header; pub use header::Header; diff --git a/gix-pack/src/data/file/decode/entry.rs b/gix-pack/src/data/file/decode/entry.rs index 2bec3efbd68..5613d70ee74 100644 --- a/gix-pack/src/data/file/decode/entry.rs +++ b/gix-pack/src/data/file/decode/entry.rs @@ -1,4 +1,4 @@ -use std::{convert::TryInto, ops::Range}; +use std::ops::Range; use gix_features::zlib; use smallvec::SmallVec; @@ -100,18 +100,10 @@ impl File { .map_err(Into::into) } - fn assure_v2(&self) { - assert!( - matches!(self.version, crate::data::Version::V2), - "Only V2 is implemented" - ); - } - /// Obtain the [`Entry`][crate::data::Entry] at the given `offset` into the pack. /// /// The `offset` is typically obtained from the pack index file. - pub fn entry(&self, offset: data::Offset) -> data::Entry { - self.assure_v2(); + pub fn entry(&self, offset: data::Offset) -> Result { let pack_offset: usize = offset.try_into().expect("offset representable by machine"); assert!(pack_offset <= self.data.len(), "offset out of bounds"); @@ -246,7 +238,7 @@ impl File { }); use crate::data::entry::Header; cursor = match cursor.header { - Header::OfsDelta { base_distance } => self.entry(cursor.base_pack_offset(base_distance)), + Header::OfsDelta { base_distance } => self.entry(cursor.base_pack_offset(base_distance))?, Header::RefDelta { base_id } => match resolve(base_id.as_ref(), out) { Some(ResolvedBase::InPack(entry)) => entry, Some(ResolvedBase::OutOfPack { end, kind }) => { diff --git a/gix-pack/src/data/file/decode/header.rs b/gix-pack/src/data/file/decode/header.rs index fb23f482d0d..84568352a59 100644 --- a/gix-pack/src/data/file/decode/header.rs +++ b/gix-pack/src/data/file/decode/header.rs @@ -66,7 +66,7 @@ impl File { if first_delta_decompressed_size.is_none() { first_delta_decompressed_size = Some(self.decode_delta_object_size(inflate, &entry)?); } - entry = self.entry(entry.base_pack_offset(base_distance)) + entry = self.entry(entry.base_pack_offset(base_distance))? } RefDelta { base_id } => { num_deltas += 1; diff --git a/gix-pack/src/data/file/decode/mod.rs b/gix-pack/src/data/file/decode/mod.rs index aa4177424a5..5746dc8e000 100644 --- a/gix-pack/src/data/file/decode/mod.rs +++ b/gix-pack/src/data/file/decode/mod.rs @@ -1,8 +1,10 @@ use std::collections::TryReserveError; /// +#[allow(clippy::empty_docs)] pub mod entry; /// +#[allow(clippy::empty_docs)] pub mod header; /// Returned by [`File::decode_header()`][crate::data::File::decode_header()], @@ -15,6 +17,8 @@ pub enum Error { ZlibInflate(#[from] gix_features::zlib::inflate::Error), #[error("A delta chain could not be followed as the ref base with id {0} could not be found")] DeltaBaseUnresolved(gix_hash::ObjectId), + #[error(transparent)] + EntryType(#[from] crate::data::entry::decode::Error), #[error("Entry too large to fit in memory")] OutOfMemory, } diff --git a/gix-pack/src/data/file/init.rs b/gix-pack/src/data/file/init.rs index b160724175d..a005e517fd7 100644 --- a/gix-pack/src/data/file/init.rs +++ b/gix-pack/src/data/file/init.rs @@ -1,4 +1,4 @@ -use std::{convert::TryInto, path::Path}; +use std::path::Path; use crate::data; diff --git a/gix-pack/src/data/file/mod.rs b/gix-pack/src/data/file/mod.rs index 6bfe0e2721b..936ab51ecb9 100644 --- a/gix-pack/src/data/file/mod.rs +++ b/gix-pack/src/data/file/mod.rs @@ -1,8 +1,10 @@ mod init; /// +#[allow(clippy::empty_docs)] pub mod verify; /// +#[allow(clippy::empty_docs)] pub mod decode; /// The bytes used as header in a pack data file. diff --git a/gix-pack/src/data/file/verify.rs b/gix-pack/src/data/file/verify.rs index ea99144a1e9..9b12209e634 100644 --- a/gix-pack/src/data/file/verify.rs +++ b/gix-pack/src/data/file/verify.rs @@ -5,6 +5,7 @@ use gix_features::progress::Progress; use crate::data::File; /// +#[allow(clippy::empty_docs)] pub mod checksum { /// Returned by [`data::File::verify_checksum()`][crate::data::File::verify_checksum()]. pub type Error = crate::verify::checksum::Error; diff --git a/gix-pack/src/data/header.rs b/gix-pack/src/data/header.rs index 348a4ca24ec..c6c7b8350fd 100644 --- a/gix-pack/src/data/header.rs +++ b/gix-pack/src/data/header.rs @@ -37,6 +37,7 @@ pub fn encode(version: data::Version, num_objects: u32) -> [u8; 12] { } /// +#[allow(clippy::empty_docs)] pub mod decode { /// Returned by [`decode()`][super::decode()]. #[derive(thiserror::Error, Debug)] diff --git a/gix-pack/src/data/input/lookup_ref_delta_objects.rs b/gix-pack/src/data/input/lookup_ref_delta_objects.rs index f6036e20f0e..6b1995293e1 100644 --- a/gix-pack/src/data/input/lookup_ref_delta_objects.rs +++ b/gix-pack/src/data/input/lookup_ref_delta_objects.rs @@ -1,5 +1,3 @@ -use std::convert::TryInto; - use gix_hash::ObjectId; use crate::data::{entry::Header, input}; diff --git a/gix-pack/src/data/mod.rs b/gix-pack/src/data/mod.rs index 9808ae8530c..385aa3d719c 100644 --- a/gix-pack/src/data/mod.rs +++ b/gix-pack/src/data/mod.rs @@ -1,5 +1,5 @@ //! a pack data file -use std::{convert::TryInto, path::Path}; +use std::path::Path; /// The offset to an entry into the pack data file, relative to its beginning. pub type Offset = u64; @@ -26,14 +26,17 @@ pub struct Entry { mod file; pub use file::{decode, verify, Header}; /// +#[allow(clippy::empty_docs)] pub mod header; /// +#[allow(clippy::empty_docs)] pub mod init { pub use super::header::decode::Error; } /// +#[allow(clippy::empty_docs)] pub mod entry; /// diff --git a/gix-pack/src/data/output/count/mod.rs b/gix-pack/src/data/output/count/mod.rs index 481ff65d3c3..c10ec1e51a6 100644 --- a/gix-pack/src/data/output/count/mod.rs +++ b/gix-pack/src/data/output/count/mod.rs @@ -44,6 +44,7 @@ mod objects_impl; pub use objects_impl::{objects, objects_unthreaded}; /// +#[allow(clippy::empty_docs)] pub mod objects { pub use super::objects_impl::{Error, ObjectExpansion, Options, Outcome}; } diff --git a/gix-pack/src/data/output/entry/iter_from_counts.rs b/gix-pack/src/data/output/entry/iter_from_counts.rs index c65f29ced49..5760bbb22fe 100644 --- a/gix-pack/src/data/output/entry/iter_from_counts.rs +++ b/gix-pack/src/data/output/entry/iter_from_counts.rs @@ -40,7 +40,7 @@ pub(crate) mod function { /// /// * ~~currently there is no way to easily write the pack index, even though the state here is uniquely positioned to do /// so with minimal overhead (especially compared to `gix index-from-pack`)~~ Probably works now by chaining Iterators - /// or keeping enough state to write a pack and then generate an index with recorded data. + /// or keeping enough state to write a pack and then generate an index with recorded data. /// pub fn iter_from_counts( mut counts: Vec, diff --git a/gix-pack/src/data/output/entry/mod.rs b/gix-pack/src/data/output/entry/mod.rs index 4ab4879eb69..188ecf51174 100644 --- a/gix-pack/src/data/output/entry/mod.rs +++ b/gix-pack/src/data/output/entry/mod.rs @@ -1,10 +1,11 @@ -use std::{convert::TryFrom, io::Write}; +use std::io::Write; use gix_hash::ObjectId; use crate::{data, data::output, find}; /// +#[allow(clippy::empty_docs)] pub mod iter_from_counts; pub use iter_from_counts::function::iter_from_counts; @@ -37,6 +38,8 @@ pub enum Kind { pub enum Error { #[error("{0}")] ZlibDeflate(#[from] std::io::Error), + #[error(transparent)] + EntryType(#[from] crate::data::entry::decode::Error), } impl output::Entry { @@ -73,7 +76,11 @@ impl output::Entry { }; let pack_offset_must_be_zero = 0; - let pack_entry = data::Entry::from_bytes(&entry.data, pack_offset_must_be_zero, count.id.as_slice().len()); + let pack_entry = match data::Entry::from_bytes(&entry.data, pack_offset_must_be_zero, count.id.as_slice().len()) + { + Ok(e) => e, + Err(err) => return Some(Err(err.into())), + }; use crate::data::entry::Header::*; match pack_entry.header { diff --git a/gix-pack/src/data/output/mod.rs b/gix-pack/src/data/output/mod.rs index 338bb92b3ea..0e1b97256aa 100644 --- a/gix-pack/src/data/output/mod.rs +++ b/gix-pack/src/data/output/mod.rs @@ -1,6 +1,7 @@ use gix_hash::ObjectId; /// +#[allow(clippy::empty_docs)] pub mod count; /// An item representing a future Entry in the leanest way possible. @@ -35,7 +36,9 @@ pub struct Entry { } /// +#[allow(clippy::empty_docs)] pub mod entry; /// +#[allow(clippy::empty_docs)] pub mod bytes; diff --git a/gix-pack/src/index/mod.rs b/gix-pack/src/index/mod.rs index 8d880744241..f83eb51fd0e 100644 --- a/gix-pack/src/index/mod.rs +++ b/gix-pack/src/index/mod.rs @@ -136,6 +136,7 @@ impl File { const V2_SIGNATURE: &[u8] = b"\xfftOc"; /// +#[allow(clippy::empty_docs)] pub mod init; pub(crate) mod access; @@ -143,9 +144,11 @@ pub use access::Entry; pub(crate) mod encode; /// +#[allow(clippy::empty_docs)] pub mod traverse; mod util; /// +#[allow(clippy::empty_docs)] pub mod verify; /// #[cfg(feature = "streaming-input")] diff --git a/gix-pack/src/index/traverse/error.rs b/gix-pack/src/index/traverse/error.rs index 2310c3babca..f6cc7506548 100644 --- a/gix-pack/src/index/traverse/error.rs +++ b/gix-pack/src/index/traverse/error.rs @@ -12,6 +12,8 @@ pub enum Error { Tree(#[from] crate::cache::delta::from_offsets::Error), #[error("The tree traversal failed")] TreeTraversal(#[from] crate::cache::delta::traverse::Error), + #[error(transparent)] + EntryType(#[from] crate::data::entry::decode::Error), #[error("Object {id} at offset {offset} could not be decoded")] PackDecode { id: gix_hash::ObjectId, diff --git a/gix-pack/src/index/traverse/mod.rs b/gix-pack/src/index/traverse/mod.rs index 1edf0b1d5dd..e6731554a0a 100644 --- a/gix-pack/src/index/traverse/mod.rs +++ b/gix-pack/src/index/traverse/mod.rs @@ -6,8 +6,10 @@ use crate::index; mod reduce; /// +#[allow(clippy::empty_docs)] pub mod with_index; /// +#[allow(clippy::empty_docs)] pub mod with_lookup; use reduce::Reducer; @@ -159,7 +161,7 @@ impl index::File { C: crate::cache::DecodeEntry, E: std::error::Error + Send + Sync + 'static, { - let pack_entry = pack.entry(index_entry.pack_offset); + let pack_entry = pack.entry(index_entry.pack_offset)?; let pack_entry_data_offset = pack_entry.data_offset; let entry_stats = pack .decode_entry( @@ -167,9 +169,10 @@ impl index::File { buf, inflate, &|id, _| { - self.lookup(id).map(|index| { - crate::data::decode::entry::ResolvedBase::InPack(pack.entry(self.pack_offset_at_index(index))) - }) + let index = self.lookup(id)?; + pack.entry(self.pack_offset_at_index(index)) + .ok() + .map(crate::data::decode::entry::ResolvedBase::InPack) }, cache, ) diff --git a/gix-pack/src/index/verify.rs b/gix-pack/src/index/verify.rs index 0ee4259a8fc..6dfa817e08e 100644 --- a/gix-pack/src/index/verify.rs +++ b/gix-pack/src/index/verify.rs @@ -6,6 +6,7 @@ use gix_object::WriteTo; use crate::index; /// +#[allow(clippy::empty_docs)] pub mod integrity { use std::marker::PhantomData; @@ -88,6 +89,7 @@ pub mod integrity { } /// +#[allow(clippy::empty_docs)] pub mod checksum { /// Returned by [`index::File::verify_checksum()`][crate::index::File::verify_checksum()]. pub type Error = crate::verify::checksum::Error; diff --git a/gix-pack/src/index/write/mod.rs b/gix-pack/src/index/write/mod.rs index 3ecb7347b8f..ca3d8348088 100644 --- a/gix-pack/src/index/write/mod.rs +++ b/gix-pack/src/index/write/mod.rs @@ -1,4 +1,4 @@ -use std::{convert::TryInto, io, sync::atomic::AtomicBool}; +use std::{io, sync::atomic::AtomicBool}; pub use error::Error; use gix_features::progress::{self, prodash::DynNestedProgress, Count, Progress}; @@ -78,9 +78,9 @@ impl crate::index::File { /// /// * neither in-pack nor out-of-pack Ref Deltas are supported here, these must have been resolved beforehand. /// * `make_resolver()` will only be called after the iterator stopped returning elements and produces a function that - /// provides all bytes belonging to a pack entry writing them to the given mutable output `Vec`. - /// It should return `None` if the entry cannot be resolved from the pack that produced the `entries` iterator, causing - /// the write operation to fail. + /// provides all bytes belonging to a pack entry writing them to the given mutable output `Vec`. + /// It should return `None` if the entry cannot be resolved from the pack that produced the `entries` iterator, causing + /// the write operation to fail. #[allow(clippy::too_many_arguments)] pub fn write_data_iter_to_stream( version: crate::index::Version, diff --git a/gix-pack/src/lib.rs b/gix-pack/src/lib.rs index b56d1fe983e..34c9e1c9d55 100755 --- a/gix-pack/src/lib.rs +++ b/gix-pack/src/lib.rs @@ -19,6 +19,7 @@ #![deny(missing_docs, rust_2018_idioms, unsafe_code)] /// +#[allow(clippy::empty_docs)] pub mod bundle; /// A bundle of pack data and the corresponding pack index pub struct Bundle { @@ -29,22 +30,28 @@ pub struct Bundle { } /// +#[allow(clippy::empty_docs)] pub mod find; /// +#[allow(clippy::empty_docs)] pub mod cache; /// +#[allow(clippy::empty_docs)] pub mod data; mod find_traits; pub use find_traits::{Find, FindExt}; /// +#[allow(clippy::empty_docs)] pub mod index; /// +#[allow(clippy::empty_docs)] pub mod multi_index; /// +#[allow(clippy::empty_docs)] pub mod verify; mod mmap { @@ -55,13 +62,11 @@ mod mmap { // SAFETY: we have to take the risk of somebody changing the file underneath. Git never writes into the same file. #[allow(unsafe_code)] unsafe { - memmap2::Mmap::map(&file) + memmap2::MmapOptions::new().map_copy_read_only(&file) } } } -use std::convert::TryInto; - #[inline] fn read_u32(b: &[u8]) -> u32 { u32::from_be_bytes(b.try_into().unwrap()) diff --git a/gix-pack/src/multi_index/chunk.rs b/gix-pack/src/multi_index/chunk.rs index e9a9aac9cdb..81c640bbf57 100644 --- a/gix-pack/src/multi_index/chunk.rs +++ b/gix-pack/src/multi_index/chunk.rs @@ -8,6 +8,7 @@ pub mod index_names { pub const ID: gix_chunk::Id = *b"PNAM"; /// + #[allow(clippy::empty_docs)] pub mod decode { use gix_object::bstr::BString; @@ -105,8 +106,6 @@ pub mod index_names { /// Information for the chunk with the fanout table pub mod fanout { - use std::convert::TryInto; - use crate::multi_index; /// The size of the fanout table @@ -173,7 +172,7 @@ pub mod lookup { /// Information about the offsets table. pub mod offsets { - use std::{convert::TryInto, ops::Range}; + use std::ops::Range; use crate::multi_index; diff --git a/gix-pack/src/multi_index/init.rs b/gix-pack/src/multi_index/init.rs index 190b40a7b04..beccf78e403 100644 --- a/gix-pack/src/multi_index/init.rs +++ b/gix-pack/src/multi_index/init.rs @@ -1,4 +1,4 @@ -use std::{convert::TryFrom, path::Path}; +use std::path::Path; use crate::multi_index::{chunk, File, Version}; diff --git a/gix-pack/src/multi_index/mod.rs b/gix-pack/src/multi_index/mod.rs index d88ae0baed1..6fbb53676a9 100644 --- a/gix-pack/src/multi_index/mod.rs +++ b/gix-pack/src/multi_index/mod.rs @@ -37,16 +37,21 @@ pub struct File { } /// +#[allow(clippy::empty_docs)] pub mod write; /// +#[allow(clippy::empty_docs)] mod access; /// +#[allow(clippy::empty_docs)] pub mod verify; /// +#[allow(clippy::empty_docs)] pub mod chunk; /// +#[allow(clippy::empty_docs)] pub mod init; diff --git a/gix-pack/src/multi_index/verify.rs b/gix-pack/src/multi_index/verify.rs index 0903b3568cf..2f645e6c881 100644 --- a/gix-pack/src/multi_index/verify.rs +++ b/gix-pack/src/multi_index/verify.rs @@ -5,6 +5,7 @@ use gix_features::progress::{Count, DynNestedProgress, Progress}; use crate::{index, multi_index::File}; /// +#[allow(clippy::empty_docs)] pub mod integrity { use crate::multi_index::EntryIndex; @@ -68,6 +69,7 @@ pub mod integrity { } /// +#[allow(clippy::empty_docs)] pub mod checksum { /// Returned by [`multi_index::File::verify_checksum()`][crate::multi_index::File::verify_checksum()]. pub type Error = crate::verify::checksum::Error; @@ -284,6 +286,7 @@ impl File { TreeTraversal(err) => TreeTraversal(err), PackDecode { id, offset, source } => PackDecode { id, offset, source }, PackMismatch { expected, actual } => PackMismatch { expected, actual }, + EntryType(err) => EntryType(err), PackObjectMismatch { expected, actual, diff --git a/gix-pack/src/multi_index/write.rs b/gix-pack/src/multi_index/write.rs index 881033091bc..3625cc47c7a 100644 --- a/gix-pack/src/multi_index/write.rs +++ b/gix-pack/src/multi_index/write.rs @@ -1,5 +1,4 @@ use std::{ - convert::TryInto, path::PathBuf, sync::atomic::{AtomicBool, Ordering}, time::{Instant, SystemTime}, diff --git a/gix-pack/src/verify.rs b/gix-pack/src/verify.rs index fff99c75d12..3fad0e2467f 100644 --- a/gix-pack/src/verify.rs +++ b/gix-pack/src/verify.rs @@ -3,6 +3,7 @@ use std::{path::Path, sync::atomic::AtomicBool}; use gix_features::progress::Progress; /// +#[allow(clippy::empty_docs)] pub mod checksum { /// Returned by various methods to verify the checksum of a memory mapped file that might also exist on disk. #[derive(thiserror::Error, Debug)] diff --git a/gix-pack/tests/fixtures/generated-archives/make_pack_gen_repo.tar.xz b/gix-pack/tests/fixtures/generated-archives/make_pack_gen_repo.tar.xz index ea3241474a0..5c209dc3d51 100644 Binary files a/gix-pack/tests/fixtures/generated-archives/make_pack_gen_repo.tar.xz and b/gix-pack/tests/fixtures/generated-archives/make_pack_gen_repo.tar.xz differ diff --git a/gix-pack/tests/fixtures/generated-archives/make_pack_gen_repo_multi_index.tar.xz b/gix-pack/tests/fixtures/generated-archives/make_pack_gen_repo_multi_index.tar.xz index e587341dc92..f3d16ee0d90 100644 Binary files a/gix-pack/tests/fixtures/generated-archives/make_pack_gen_repo_multi_index.tar.xz and b/gix-pack/tests/fixtures/generated-archives/make_pack_gen_repo_multi_index.tar.xz differ diff --git a/gix-pack/tests/fixtures/make_pack_gen_repo.sh b/gix-pack/tests/fixtures/make_pack_gen_repo.sh index 6e8c91e4bf0..1be6adc7a48 100755 --- a/gix-pack/tests/fixtures/make_pack_gen_repo.sh +++ b/gix-pack/tests/fixtures/make_pack_gen_repo.sh @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash set -eu -o pipefail git init -q diff --git a/gix-pack/tests/fixtures/make_pack_gen_repo_multi_index.sh b/gix-pack/tests/fixtures/make_pack_gen_repo_multi_index.sh index 45444a7b49e..ee6a13ec6dc 100755 --- a/gix-pack/tests/fixtures/make_pack_gen_repo_multi_index.sh +++ b/gix-pack/tests/fixtures/make_pack_gen_repo_multi_index.sh @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash set -eu -o pipefail git init -q diff --git a/gix-pack/tests/pack/data/file.rs b/gix-pack/tests/pack/data/file.rs index 6a279fa84c0..a4967570e75 100644 --- a/gix-pack/tests/pack/data/file.rs +++ b/gix-pack/tests/pack/data/file.rs @@ -103,7 +103,7 @@ mod decode_entry { } let p = pack_at(SMALL_PACK); - let entry = p.entry(offset); + let entry = p.entry(offset).expect("valid object type"); let mut buf = Vec::new(); p.decode_entry( entry, @@ -159,7 +159,7 @@ mod resolve_header { } let p = pack_at(SMALL_PACK); - let entry = p.entry(offset); + let entry = p.entry(offset).expect("valid object type"); p.decode_header(entry, &mut Default::default(), &resolve_with_panic) .expect("valid offset provides valid entry") } @@ -208,7 +208,7 @@ mod decompress_entry { fn decompress_entry_at_offset(offset: u64) -> Vec { let p = pack_at(SMALL_PACK); - let entry = p.entry(offset); + let entry = p.entry(offset).expect("valid object type"); let size = entry.decompressed_size as usize; let mut buf = vec![0; size]; diff --git a/gix-pack/tests/pack/data/header.rs b/gix-pack/tests/pack/data/header.rs index 45132c9f8a5..8c05e24302e 100644 --- a/gix-pack/tests/pack/data/header.rs +++ b/gix-pack/tests/pack/data/header.rs @@ -1,5 +1,3 @@ -use std::convert::TryInto; - use crate::pack::fixture_path; #[test] diff --git a/gix-pack/tests/pack/data/output/count_and_entries.rs b/gix-pack/tests/pack/data/output/count_and_entries.rs index a98f02d387e..edb4783224a 100644 --- a/gix-pack/tests/pack/data/output/count_and_entries.rs +++ b/gix-pack/tests/pack/data/output/count_and_entries.rs @@ -9,7 +9,6 @@ use gix_pack::data::{ output, output::{count, entry}, }; -use gix_traverse::commit; use crate::pack::{ data::output::{db, DbKind}, @@ -241,7 +240,7 @@ fn traversals() -> crate::Result { .copied() { let head = hex_to_id("dfcb5e39ac6eb30179808bbab721e8a28ce1b52e"); - let mut commits = commit::Ancestors::new(Some(head), commit::ancestors::State::default(), db.clone()) + let mut commits = gix_traverse::commit::Simple::new(Some(head), db.clone()) .map(Result::unwrap) .map(|c| c.id) .collect::>(); @@ -323,13 +322,18 @@ fn traversals() -> crate::Result { #[test] fn empty_pack_is_allowed() { - write_and_verify( - db(DbKind::DeterministicGeneratedContent).unwrap(), - vec![], - hex_to_id("029d08823bd8a8eab510ad6ac75c823cfd3ed31e"), - None, - ) - .unwrap(); + assert_eq!( + write_and_verify( + db(DbKind::DeterministicGeneratedContent).unwrap(), + vec![], + hex_to_id("029d08823bd8a8eab510ad6ac75c823cfd3ed31e"), + None, + ) + .unwrap_err() + .to_string(), + "pack data directory should be set", + "empty packs are not actually written as they would be useless" + ); } fn write_and_verify( @@ -393,7 +397,7 @@ fn write_and_verify( pack::bundle::write::Options::default(), )? .data_path - .expect("directory set"), + .ok_or("pack data directory should be set")?, object_hash, )?; // TODO: figure out why these hashes change, also depending on the machine, even though they are indeed stable. diff --git a/gix-pack/tests/pack/index.rs b/gix-pack/tests/pack/index.rs index 1a168b28e79..bb440b76ced 100644 --- a/gix-pack/tests/pack/index.rs +++ b/gix-pack/tests/pack/index.rs @@ -108,7 +108,7 @@ mod version { } } - #[cfg(feature = "internal-testing-gix-features-parallel")] + #[cfg(feature = "gix-features-parallel")] mod any { use std::{fs, io, sync::atomic::AtomicBool}; @@ -133,7 +133,7 @@ mod version { index_path: &&str, data_path: &&str, ) -> Result<(), Box> { - let pack_iter = pack::data::input::BytesToEntriesIter::new_from_header( + let mut pack_iter = pack::data::input::BytesToEntriesIter::new_from_header( io::BufReader::new(fs::File::open(fixture_path(data_path))?), *mode, *compressed, @@ -148,12 +148,12 @@ mod version { desired_kind, || { let file = std::fs::File::open(fixture_path(data_path))?; - let map = unsafe { memmap2::Mmap::map(&file)? }; + let map = unsafe { memmap2::MmapOptions::new().map_copy_read_only(&file)? }; Ok((slice_map, map)) }, - pack_iter, + &mut pack_iter, None, - progress::Discard, + &mut progress::Discard, &mut actual, &AtomicBool::new(false), gix_hash::Kind::Sha1, @@ -210,7 +210,7 @@ mod version { assert_eq!(outcome.index_version, desired_kind); assert_eq!( outcome.index_hash, - gix_hash::ObjectId::from(&expected[end_of_pack_hash..end_of_index_hash]) + gix_hash::ObjectId::try_from(&expected[end_of_pack_hash..end_of_index_hash])? ); Ok(()) } @@ -227,7 +227,7 @@ mod version { #[test] fn lookup_missing() { let file = index::File::at(&fixture_path(INDEX_V2), gix_hash::Kind::Sha1).unwrap(); - let prefix = gix_hash::Prefix::new(gix_hash::ObjectId::null(gix_hash::Kind::Sha1), 7).unwrap(); + let prefix = gix_hash::Prefix::new(&gix_hash::Kind::Sha1.null(), 7).unwrap(); assert!(file.lookup_prefix(prefix, None).is_none()); let mut candidates = 1..1; @@ -363,8 +363,8 @@ fn pack_lookup() -> Result<(), Box> { }, ), ] { - let idx = index::File::at(&fixture_path(index_path), gix_hash::Kind::Sha1)?; - let pack = pack::data::File::at(&fixture_path(pack_path), gix_hash::Kind::Sha1)?; + let idx = index::File::at(fixture_path(index_path), gix_hash::Kind::Sha1)?; + let pack = pack::data::File::at(fixture_path(pack_path), gix_hash::Kind::Sha1)?; assert_eq!(pack.version(), pack::data::Version::V2); assert_eq!(pack.num_objects(), idx.num_objects()); @@ -398,7 +398,7 @@ fn pack_lookup() -> Result<(), Box> { let sorted_offsets = idx.sorted_offsets(); assert_eq!(num_objects, sorted_offsets.len()); for idx_entry in idx.iter() { - let pack_entry = pack.entry(idx_entry.pack_offset); + let pack_entry = pack.entry(idx_entry.pack_offset)?; assert_ne!(pack_entry.data_offset, idx_entry.pack_offset); assert!(sorted_offsets.binary_search(&idx_entry.pack_offset).is_ok()); } @@ -410,7 +410,7 @@ fn pack_lookup() -> Result<(), Box> { ); let mut buf = vec![0u8; entry.decompressed_size as usize]; - let pack_entry = pack.entry(offset_from_index); + let pack_entry = pack.entry(offset_from_index)?; assert_eq!( pack_entry.pack_offset(), entry.pack_offset, @@ -471,7 +471,7 @@ fn iter() -> Result<(), Box> { "0f3ea84cd1bba10c2a03d736a460635082833e59", ), ] { - let idx = index::File::at(&fixture_path(path), gix_hash::Kind::Sha1)?; + let idx = index::File::at(fixture_path(path), gix_hash::Kind::Sha1)?; assert_eq!(idx.version(), *kind); assert_eq!(idx.num_objects(), *num_objects); assert_eq!( diff --git a/gix-pack/tests/pack/iter.rs b/gix-pack/tests/pack/iter.rs index e814e2c4b6e..c9dd7d445c6 100644 --- a/gix-pack/tests/pack/iter.rs +++ b/gix-pack/tests/pack/iter.rs @@ -37,7 +37,7 @@ mod new_from_header { let mut buf = Vec::::new(); entry.header.write_to(entry.decompressed_size, &mut buf)?; let new_entry = - pack::data::Entry::from_bytes(&buf, entry.pack_offset, gix_hash::Kind::Sha1.len_in_bytes()); + pack::data::Entry::from_bytes(&buf, entry.pack_offset, gix_hash::Kind::Sha1.len_in_bytes())?; assert_eq!( new_entry.header_size(), diff --git a/gix-packetline-blocking/CHANGELOG.md b/gix-packetline-blocking/CHANGELOG.md index 6ee679fb395..be570bf7c6f 100644 --- a/gix-packetline-blocking/CHANGELOG.md +++ b/gix-packetline-blocking/CHANGELOG.md @@ -5,6 +5,32 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## 0.17.4 (2024-04-13) + +A maintenance release without user-facing changes. + +### Commit Statistics + + + + - 4 commits contributed to the release over the course of 4 calendar days. + - 84 days passed between releases. + - 0 commits were understood as [conventional](https://www.conventionalcommits.org). + - 0 issues like '(#ID)' were seen in commit messages + +### Commit Details + + + +
view details + + * **Uncategorized** + - Prepare changelogs prior to release ([`5755271`](https://github.com/Byron/gitoxide/commit/57552717f46f96c35ba4ddc0a64434354ef845e9)) + - Merge branch 'copy-packetline' ([`a9b783d`](https://github.com/Byron/gitoxide/commit/a9b783df45948eb0adf538251315f808f38008f6)) + - Rerun script to get `//` instead of `//!` comments ([`ec2427f`](https://github.com/Byron/gitoxide/commit/ec2427fc78df81901a5636fb1213999a3a5ed969)) + - Replace gix-packetline-blocking/src with generated files ([`a793bde`](https://github.com/Byron/gitoxide/commit/a793bde062553288bfc045bfda75addb25a7be76)) +
+ ## 0.17.3 (2024-01-20) A maintenance release without user-facing changes. @@ -13,7 +39,7 @@ A maintenance release without user-facing changes. - - 2 commits contributed to the release over the course of 4 calendar days. + - 3 commits contributed to the release over the course of 4 calendar days. - 20 days passed between releases. - 0 commits were understood as [conventional](https://www.conventionalcommits.org). - 0 issues like '(#ID)' were seen in commit messages @@ -25,6 +51,7 @@ A maintenance release without user-facing changes.
view details * **Uncategorized** + - Release gix-utils v0.1.9, gix-features v0.38.0, gix-actor v0.30.0, gix-object v0.41.0, gix-path v0.10.4, gix-glob v0.16.0, gix-attributes v0.22.0, gix-command v0.3.3, gix-packetline-blocking v0.17.3, gix-filter v0.9.0, gix-fs v0.10.0, gix-commitgraph v0.24.0, gix-revwalk v0.12.0, gix-traverse v0.37.0, gix-worktree-stream v0.9.0, gix-archive v0.9.0, gix-config-value v0.14.4, gix-tempfile v13.0.0, gix-lock v13.0.0, gix-ref v0.41.0, gix-sec v0.10.4, gix-config v0.34.0, gix-url v0.27.0, gix-credentials v0.24.0, gix-ignore v0.11.0, gix-index v0.29.0, gix-worktree v0.30.0, gix-diff v0.40.0, gix-discover v0.29.0, gix-mailmap v0.22.0, gix-negotiate v0.12.0, gix-pack v0.47.0, gix-odb v0.57.0, gix-pathspec v0.6.0, gix-packetline v0.17.3, gix-transport v0.41.0, gix-protocol v0.44.0, gix-revision v0.26.0, gix-refspec v0.22.0, gix-status v0.5.0, gix-submodule v0.8.0, gix-worktree-state v0.7.0, gix v0.58.0, safety bump 39 crates ([`eb6aa8f`](https://github.com/Byron/gitoxide/commit/eb6aa8f502314f886fc4ea3d52ab220763968208)) - Prepare changelogs prior to release ([`6a2e0be`](https://github.com/Byron/gitoxide/commit/6a2e0bebfdf012dc2ed0ff2604086081f2a0f96d)) - Release gix-trace v0.1.7, gix-features v0.37.2, gix-commitgraph v0.23.2, gix-traverse v0.36.2, gix-index v0.28.2 ([`b6c04c8`](https://github.com/Byron/gitoxide/commit/b6c04c87b426bf36a059df8dc52b56d384b27b79))
diff --git a/gix-packetline-blocking/Cargo.toml b/gix-packetline-blocking/Cargo.toml index 2c4c1d13c6a..28620b38ff4 100644 --- a/gix-packetline-blocking/Cargo.toml +++ b/gix-packetline-blocking/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "gix-packetline-blocking" -version = "0.17.3" +version = "0.17.4" repository = "https://github.com/Byron/gitoxide" license = "MIT OR Apache-2.0" description = "A duplicate of `gix-packetline` with the `blocking-io` feature pre-selected" @@ -27,7 +27,7 @@ async-io = [] serde = ["dep:serde", "bstr/serde"] [dependencies] -gix-trace = { version = "^0.1.7", path = "../gix-trace" } +gix-trace = { version = "^0.1.9", path = "../gix-trace" } serde = { version = "1.0.114", optional = true, default-features = false, features = ["std", "derive"]} thiserror = "1.0.34" diff --git a/gix-packetline-blocking/src b/gix-packetline-blocking/src deleted file mode 120000 index a92fc2d976d..00000000000 --- a/gix-packetline-blocking/src +++ /dev/null @@ -1 +0,0 @@ -../gix-packetline/src \ No newline at end of file diff --git a/gix-packetline-blocking/src/decode.rs b/gix-packetline-blocking/src/decode.rs new file mode 100644 index 00000000000..cde0a58beed --- /dev/null +++ b/gix-packetline-blocking/src/decode.rs @@ -0,0 +1,148 @@ +// DO NOT EDIT - this is a copy of gix-packetline/src/decode.rs. Run `just copy-packetline` to update it. + +use bstr::BString; + +use crate::{PacketLineRef, DELIMITER_LINE, FLUSH_LINE, MAX_DATA_LEN, MAX_LINE_LEN, RESPONSE_END_LINE, U16_HEX_BYTES}; + +/// The error used in the [`decode`][mod@crate::decode] module +#[derive(Debug, thiserror::Error)] +#[allow(missing_docs)] +pub enum Error { + #[error("Failed to decode the first four hex bytes indicating the line length: {err}")] + HexDecode { err: String }, + #[error("The data received claims to be larger than the maximum allowed size: got {length_in_bytes}, exceeds {MAX_DATA_LEN}")] + DataLengthLimitExceeded { length_in_bytes: usize }, + #[error("Received an invalid empty line")] + DataIsEmpty, + #[error("Received an invalid line of length 3")] + InvalidLineLength, + #[error("{data:?} - consumed {bytes_consumed} bytes")] + Line { data: BString, bytes_consumed: usize }, + #[error("Needing {bytes_needed} additional bytes to decode the line successfully")] + NotEnoughData { bytes_needed: usize }, +} + +/// +#[allow(clippy::empty_docs)] +pub mod band { + /// The error used in [`PacketLineRef::decode_band()`][super::PacketLineRef::decode_band()]. + #[derive(Debug, thiserror::Error)] + #[allow(missing_docs)] + pub enum Error { + #[error("attempt to decode a non-side channel line or input was malformed: {band_id}")] + InvalidSideBand { band_id: u8 }, + #[error("attempt to decode a non-data line into a side-channel band")] + NonDataLine, + } +} + +/// A utility return type to support incremental parsing of packet lines. +#[derive(Debug, Clone)] +pub enum Stream<'a> { + /// Indicate a single packet line was parsed completely + Complete { + /// The parsed packet line + line: PacketLineRef<'a>, + /// The amount of bytes consumed from input + bytes_consumed: usize, + }, + /// A packet line could not yet be parsed due to missing bytes + Incomplete { + /// The amount of additional bytes needed for the parsing to complete + bytes_needed: usize, + }, +} + +/// The result of [`hex_prefix()`] indicating either a special packet line or the amount of wanted bytes +pub enum PacketLineOrWantedSize<'a> { + /// The special kind of packet line decoded from the hex prefix. It never contains actual data. + Line(PacketLineRef<'a>), + /// The amount of bytes indicated by the hex prefix of the packet line. + Wanted(u16), +} + +/// Decode the `four_bytes` packet line prefix provided in hexadecimal form and check it for validity. +pub fn hex_prefix(four_bytes: &[u8]) -> Result, Error> { + debug_assert_eq!(four_bytes.len(), 4, "need four hex bytes"); + for (line_bytes, line_type) in &[ + (FLUSH_LINE, PacketLineRef::Flush), + (DELIMITER_LINE, PacketLineRef::Delimiter), + (RESPONSE_END_LINE, PacketLineRef::ResponseEnd), + ] { + if four_bytes == *line_bytes { + return Ok(PacketLineOrWantedSize::Line(*line_type)); + } + } + + let mut buf = [0u8; U16_HEX_BYTES / 2]; + faster_hex::hex_decode(four_bytes, &mut buf).map_err(|err| Error::HexDecode { err: err.to_string() })?; + let wanted_bytes = u16::from_be_bytes(buf); + + if wanted_bytes == 3 { + return Err(Error::InvalidLineLength); + } + if wanted_bytes == 4 { + return Err(Error::DataIsEmpty); + } + debug_assert!( + wanted_bytes as usize > U16_HEX_BYTES, + "by now there should be more wanted bytes than prefix bytes" + ); + Ok(PacketLineOrWantedSize::Wanted(wanted_bytes - U16_HEX_BYTES as u16)) +} + +/// Obtain a `PacketLine` from `data` after assuring `data` is small enough to fit. +pub fn to_data_line(data: &[u8]) -> Result, Error> { + if data.len() > MAX_LINE_LEN { + return Err(Error::DataLengthLimitExceeded { + length_in_bytes: data.len(), + }); + } + + Ok(PacketLineRef::Data(data)) +} + +/// Decode `data` as packet line while reporting whether the data is complete or not using a [`Stream`]. +pub fn streaming(data: &[u8]) -> Result, Error> { + let data_len = data.len(); + if data_len < U16_HEX_BYTES { + return Ok(Stream::Incomplete { + bytes_needed: U16_HEX_BYTES - data_len, + }); + } + let wanted_bytes = match hex_prefix(&data[..U16_HEX_BYTES])? { + PacketLineOrWantedSize::Wanted(s) => s as usize, + PacketLineOrWantedSize::Line(line) => { + return Ok(Stream::Complete { + line, + bytes_consumed: 4, + }) + } + } + U16_HEX_BYTES; + if wanted_bytes > MAX_LINE_LEN { + return Err(Error::DataLengthLimitExceeded { + length_in_bytes: wanted_bytes, + }); + } + if data_len < wanted_bytes { + return Ok(Stream::Incomplete { + bytes_needed: wanted_bytes - data_len, + }); + } + + Ok(Stream::Complete { + line: to_data_line(&data[U16_HEX_BYTES..wanted_bytes])?, + bytes_consumed: wanted_bytes, + }) +} + +/// Decode an entire packet line from data or fail. +/// +/// Note that failure also happens if there is not enough data to parse a complete packet line, as opposed to [`streaming()`] decoding +/// succeeds in that case, stating how much more bytes are required. +pub fn all_at_once(data: &[u8]) -> Result, Error> { + match streaming(data)? { + Stream::Complete { line, .. } => Ok(line), + Stream::Incomplete { bytes_needed } => Err(Error::NotEnoughData { bytes_needed }), + } +} diff --git a/gix-packetline-blocking/src/encode/async_io.rs b/gix-packetline-blocking/src/encode/async_io.rs new file mode 100644 index 00000000000..384ec856f5e --- /dev/null +++ b/gix-packetline-blocking/src/encode/async_io.rs @@ -0,0 +1,215 @@ +// DO NOT EDIT - this is a copy of gix-packetline/src/encode/async_io.rs. Run `just copy-packetline` to update it. + +use std::{ + io, + pin::Pin, + task::{Context, Poll}, +}; + +use futures_io::AsyncWrite; +use futures_lite::AsyncWriteExt; + +use super::u16_to_hex; +use crate::{encode::Error, Channel, DELIMITER_LINE, ERR_PREFIX, FLUSH_LINE, MAX_DATA_LEN, RESPONSE_END_LINE}; + +pin_project_lite::pin_project! { + /// A way of writing packet lines asynchronously. + pub struct LineWriter<'a, W> { + #[pin] + pub(crate) writer: W, + pub(crate) prefix: &'a [u8], + pub(crate) suffix: &'a [u8], + state: State<'a>, + } +} + +enum State<'a> { + Idle, + WriteHexLen([u8; 4], usize), + WritePrefix(&'a [u8]), + WriteData(usize), + WriteSuffix(&'a [u8]), +} + +impl<'a, W: AsyncWrite + Unpin> LineWriter<'a, W> { + /// Create a new line writer writing data with a `prefix` and `suffix`. + /// + /// Keep the additional `prefix` or `suffix` buffers empty if no prefix or suffix should be written. + pub fn new(writer: W, prefix: &'a [u8], suffix: &'a [u8]) -> Self { + LineWriter { + writer, + prefix, + suffix, + state: State::Idle, + } + } + + /// Consume self and reveal the inner writer. + pub fn into_inner(self) -> W { + self.writer + } +} + +fn into_io_err(err: Error) -> io::Error { + io::Error::new(io::ErrorKind::Other, err) +} + +impl AsyncWrite for LineWriter<'_, W> { + fn poll_write(self: Pin<&mut Self>, cx: &mut Context<'_>, data: &[u8]) -> Poll> { + use futures_lite::ready; + let mut this = self.project(); + loop { + match &mut this.state { + State::Idle => { + let data_len = this.prefix.len() + data.len() + this.suffix.len(); + if data_len > MAX_DATA_LEN { + return Poll::Ready(Err(into_io_err(Error::DataLengthLimitExceeded { + length_in_bytes: data_len, + }))); + } + if data.is_empty() { + return Poll::Ready(Err(into_io_err(Error::DataIsEmpty))); + } + let data_len = data_len + 4; + let len_buf = u16_to_hex(data_len as u16); + *this.state = State::WriteHexLen(len_buf, 0) + } + State::WriteHexLen(hex_len, written) => { + while *written != hex_len.len() { + let n = ready!(this.writer.as_mut().poll_write(cx, &hex_len[*written..]))?; + if n == 0 { + return Poll::Ready(Err(io::ErrorKind::WriteZero.into())); + } + *written += n; + } + if this.prefix.is_empty() { + *this.state = State::WriteData(0) + } else { + *this.state = State::WritePrefix(this.prefix) + } + } + State::WritePrefix(buf) => { + while !buf.is_empty() { + let n = ready!(this.writer.as_mut().poll_write(cx, buf))?; + if n == 0 { + return Poll::Ready(Err(io::ErrorKind::WriteZero.into())); + } + let (_, rest) = std::mem::take(buf).split_at(n); + *buf = rest; + } + *this.state = State::WriteData(0) + } + State::WriteData(written) => { + while *written != data.len() { + let n = ready!(this.writer.as_mut().poll_write(cx, &data[*written..]))?; + if n == 0 { + return Poll::Ready(Err(io::ErrorKind::WriteZero.into())); + } + *written += n; + } + if this.suffix.is_empty() { + let written = 4 + this.prefix.len() + *written; + *this.state = State::Idle; + return Poll::Ready(Ok(written)); + } else { + *this.state = State::WriteSuffix(this.suffix) + } + } + State::WriteSuffix(buf) => { + while !buf.is_empty() { + let n = ready!(this.writer.as_mut().poll_write(cx, buf))?; + if n == 0 { + return Poll::Ready(Err(io::ErrorKind::WriteZero.into())); + } + let (_, rest) = std::mem::take(buf).split_at(n); + *buf = rest; + } + *this.state = State::Idle; + return Poll::Ready(Ok(4 + this.prefix.len() + data.len() + this.suffix.len())); + } + } + } + } + + fn poll_flush(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + let this = self.project(); + this.writer.poll_flush(cx) + } + + fn poll_close(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + let this = self.project(); + this.writer.poll_close(cx) + } +} + +async fn prefixed_and_suffixed_data_to_write( + prefix: &[u8], + data: &[u8], + suffix: &[u8], + mut out: impl AsyncWrite + Unpin, +) -> io::Result { + let data_len = prefix.len() + data.len() + suffix.len(); + if data_len > MAX_DATA_LEN { + return Err(into_io_err(Error::DataLengthLimitExceeded { + length_in_bytes: data_len, + })); + } + if data.is_empty() { + return Err(into_io_err(Error::DataIsEmpty)); + } + + let data_len = data_len + 4; + let buf = u16_to_hex(data_len as u16); + + out.write_all(&buf).await?; + if !prefix.is_empty() { + out.write_all(prefix).await?; + } + out.write_all(data).await?; + if !suffix.is_empty() { + out.write_all(suffix).await?; + } + Ok(data_len) +} + +async fn prefixed_data_to_write(prefix: &[u8], data: &[u8], out: impl AsyncWrite + Unpin) -> io::Result { + prefixed_and_suffixed_data_to_write(prefix, data, &[], out).await +} + +/// Write a `text` message to `out`, which is assured to end in a newline. +pub async fn text_to_write(text: &[u8], out: impl AsyncWrite + Unpin) -> io::Result { + prefixed_and_suffixed_data_to_write(&[], text, &[b'\n'], out).await +} + +/// Write a `data` message to `out`. +pub async fn data_to_write(data: &[u8], out: impl AsyncWrite + Unpin) -> io::Result { + prefixed_data_to_write(&[], data, out).await +} + +/// Write an error `message` to `out`. +pub async fn error_to_write(message: &[u8], out: impl AsyncWrite + Unpin) -> io::Result { + prefixed_data_to_write(ERR_PREFIX, message, out).await +} + +/// Write a response-end message to `out`. +pub async fn response_end_to_write(mut out: impl AsyncWrite + Unpin) -> io::Result { + out.write_all(RESPONSE_END_LINE).await?; + Ok(4) +} + +/// Write a delim message to `out`. +pub async fn delim_to_write(mut out: impl AsyncWrite + Unpin) -> io::Result { + out.write_all(DELIMITER_LINE).await?; + Ok(4) +} + +/// Write a flush message to `out`. +pub async fn flush_to_write(mut out: impl AsyncWrite + Unpin) -> io::Result { + out.write_all(FLUSH_LINE).await?; + Ok(4) +} + +/// Write `data` of `kind` to `out` using side-band encoding. +pub async fn band_to_write(kind: Channel, data: &[u8], out: impl AsyncWrite + Unpin) -> io::Result { + prefixed_data_to_write(&[kind as u8], data, out).await +} diff --git a/gix-packetline-blocking/src/encode/blocking_io.rs b/gix-packetline-blocking/src/encode/blocking_io.rs new file mode 100644 index 00000000000..370ab992204 --- /dev/null +++ b/gix-packetline-blocking/src/encode/blocking_io.rs @@ -0,0 +1,78 @@ +// DO NOT EDIT - this is a copy of gix-packetline/src/encode/blocking_io.rs. Run `just copy-packetline` to update it. + +use std::io; + +use super::u16_to_hex; +use crate::{encode::Error, Channel, DELIMITER_LINE, ERR_PREFIX, FLUSH_LINE, MAX_DATA_LEN, RESPONSE_END_LINE}; + +/// Write a response-end message to `out`. +pub fn response_end_to_write(mut out: impl io::Write) -> io::Result { + out.write_all(RESPONSE_END_LINE).map(|_| 4) +} + +/// Write a delim message to `out`. +pub fn delim_to_write(mut out: impl io::Write) -> io::Result { + out.write_all(DELIMITER_LINE).map(|_| 4) +} + +/// Write a flush message to `out`. +pub fn flush_to_write(mut out: impl io::Write) -> io::Result { + out.write_all(FLUSH_LINE).map(|_| 4) +} + +/// Write an error `message` to `out`. +pub fn error_to_write(message: &[u8], out: impl io::Write) -> io::Result { + prefixed_data_to_write(ERR_PREFIX, message, out) +} + +/// Write `data` of `kind` to `out` using side-band encoding. +pub fn band_to_write(kind: Channel, data: &[u8], out: impl io::Write) -> io::Result { + prefixed_data_to_write(&[kind as u8], data, out) +} + +/// Write a `data` message to `out`. +pub fn data_to_write(data: &[u8], out: impl io::Write) -> io::Result { + prefixed_data_to_write(&[], data, out) +} + +/// Write a `text` message to `out`, which is assured to end in a newline. +pub fn text_to_write(text: &[u8], out: impl io::Write) -> io::Result { + prefixed_and_suffixed_data_to_write(&[], text, &[b'\n'], out) +} + +fn prefixed_data_to_write(prefix: &[u8], data: &[u8], out: impl io::Write) -> io::Result { + prefixed_and_suffixed_data_to_write(prefix, data, &[], out) +} + +fn prefixed_and_suffixed_data_to_write( + prefix: &[u8], + data: &[u8], + suffix: &[u8], + mut out: impl io::Write, +) -> io::Result { + let data_len = prefix.len() + data.len() + suffix.len(); + if data_len > MAX_DATA_LEN { + return Err(io::Error::new( + io::ErrorKind::Other, + Error::DataLengthLimitExceeded { + length_in_bytes: data_len, + }, + )); + } + if data.is_empty() { + return Err(io::Error::new(io::ErrorKind::Other, Error::DataIsEmpty)); + } + + let data_len = data_len + 4; + let buf = u16_to_hex(data_len as u16); + + out.write_all(&buf)?; + if !prefix.is_empty() { + out.write_all(prefix)?; + } + out.write_all(data)?; + if !suffix.is_empty() { + out.write_all(suffix)?; + } + Ok(data_len) +} diff --git a/gix-packetline-blocking/src/encode/mod.rs b/gix-packetline-blocking/src/encode/mod.rs new file mode 100644 index 00000000000..238957cc4ca --- /dev/null +++ b/gix-packetline-blocking/src/encode/mod.rs @@ -0,0 +1,29 @@ +// DO NOT EDIT - this is a copy of gix-packetline/src/encode/mod.rs. Run `just copy-packetline` to update it. + +use crate::MAX_DATA_LEN; + +/// The error returned by most functions in the [`encode`][crate::encode] module +#[derive(Debug, thiserror::Error)] +#[allow(missing_docs)] +pub enum Error { + #[error("Cannot encode more than {MAX_DATA_LEN} bytes, got {length_in_bytes}")] + DataLengthLimitExceeded { length_in_bytes: usize }, + #[error("Empty lines are invalid")] + DataIsEmpty, +} + +#[cfg(all(not(feature = "blocking-io"), feature = "async-io"))] +mod async_io; +#[cfg(all(not(feature = "blocking-io"), feature = "async-io"))] +pub use async_io::*; + +#[cfg(feature = "blocking-io")] +mod blocking_io; +#[cfg(feature = "blocking-io")] +pub use blocking_io::*; + +pub(crate) fn u16_to_hex(value: u16) -> [u8; 4] { + let mut buf = [0u8; 4]; + faster_hex::hex_encode(&value.to_be_bytes(), &mut buf).expect("two bytes to 4 hex chars never fails"); + buf +} diff --git a/gix-packetline-blocking/src/lib.rs b/gix-packetline-blocking/src/lib.rs new file mode 100644 index 00000000000..8b7ae4479d5 --- /dev/null +++ b/gix-packetline-blocking/src/lib.rs @@ -0,0 +1,110 @@ +// DO NOT EDIT - this is a copy of gix-packetline/src/lib.rs. Run `just copy-packetline` to update it. + +//! Read and write the git packet line wire format without copying it. +//! +//! For reading the packet line format use the [`StreamingPeekableIter`], and for writing the [`Writer`]. +//! ## Feature Flags +#![cfg_attr( + all(doc, all(doc, feature = "document-features")), + doc = ::document_features::document_features!() +)] +#![cfg_attr(all(doc, feature = "document-features"), feature(doc_cfg, doc_auto_cfg))] +#![deny(missing_docs, rust_2018_idioms, unsafe_code)] + +const U16_HEX_BYTES: usize = 4; +const MAX_DATA_LEN: usize = 65516; +const MAX_LINE_LEN: usize = MAX_DATA_LEN + U16_HEX_BYTES; +const FLUSH_LINE: &[u8] = b"0000"; +const DELIMITER_LINE: &[u8] = b"0001"; +const RESPONSE_END_LINE: &[u8] = b"0002"; +const ERR_PREFIX: &[u8] = b"ERR "; + +/// One of three side-band types allowing to multiplex information over a single connection. +#[derive(PartialEq, Eq, Debug, Hash, Ord, PartialOrd, Clone, Copy)] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +pub enum Channel { + /// The usable data itself in any format. + Data = 1, + /// Progress information in a user-readable format. + Progress = 2, + /// Error information in a user readable format. Receiving it usually terminates the connection. + Error = 3, +} + +mod line; +/// +#[allow(clippy::empty_docs)] +pub mod read; + +/// +#[allow(clippy::empty_docs)] +#[cfg(any(feature = "async-io", feature = "blocking-io"))] +mod write; +#[cfg(all(not(feature = "blocking-io"), feature = "async-io"))] +pub use write::async_io::Writer; +#[cfg(feature = "blocking-io")] +pub use write::blocking_io::Writer; + +/// A borrowed packet line as it refers to a slice of data by reference. +#[derive(PartialEq, Eq, Debug, Hash, Ord, PartialOrd, Clone, Copy)] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +pub enum PacketLineRef<'a> { + /// A chunk of raw data. + Data(&'a [u8]), + /// A flush packet. + Flush, + /// A delimiter packet. + Delimiter, + /// The end of the response. + ResponseEnd, +} + +/// A packet line representing an Error in a side-band channel. +#[derive(PartialEq, Eq, Debug, Hash, Ord, PartialOrd, Clone, Copy)] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +pub struct ErrorRef<'a>(pub &'a [u8]); + +/// A packet line representing text, which may include a trailing newline. +#[derive(PartialEq, Eq, Debug, Hash, Ord, PartialOrd, Clone, Copy)] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +pub struct TextRef<'a>(pub &'a [u8]); + +/// A band in a side-band channel. +#[derive(PartialEq, Eq, Debug, Hash, Ord, PartialOrd, Clone, Copy)] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +pub enum BandRef<'a> { + /// A band carrying data. + Data(&'a [u8]), + /// A band carrying user readable progress information. + Progress(&'a [u8]), + /// A band carrying user readable errors. + Error(&'a [u8]), +} + +/// Read pack lines one after another, without consuming more than needed from the underlying +/// [`Read`][std::io::Read]. [`Flush`][PacketLineRef::Flush] lines cause the reader to stop producing lines forever, +/// leaving [`Read`][std::io::Read] at the start of whatever comes next. +/// +/// This implementation tries hard not to allocate at all which leads to quite some added complexity and plenty of extra memory copies. +pub struct StreamingPeekableIter { + read: T, + peek_buf: Vec, + #[cfg(any(feature = "blocking-io", feature = "async-io"))] + buf: Vec, + fail_on_err_lines: bool, + delimiters: &'static [PacketLineRef<'static>], + is_done: bool, + stopped_at: Option>, + #[cfg_attr(all(not(feature = "async-io"), not(feature = "blocking-io")), allow(dead_code))] + trace: bool, +} + +/// Utilities to help decoding packet lines +pub mod decode; +#[doc(inline)] +pub use decode::all_at_once as decode; +/// Utilities to encode different kinds of packet lines +pub mod encode; + +#[cfg(all(feature = "async-io", feature = "blocking-io"))] +compile_error!("Cannot set both 'blocking-io' and 'async-io' features as they are mutually exclusive"); diff --git a/gix-packetline-blocking/src/line/async_io.rs b/gix-packetline-blocking/src/line/async_io.rs new file mode 100644 index 00000000000..3a631c44c9a --- /dev/null +++ b/gix-packetline-blocking/src/line/async_io.rs @@ -0,0 +1,49 @@ +// DO NOT EDIT - this is a copy of gix-packetline/src/line/async_io.rs. Run `just copy-packetline` to update it. + +use std::io; + +use futures_io::AsyncWrite; + +use crate::{encode, BandRef, Channel, ErrorRef, PacketLineRef, TextRef}; + +impl<'a> BandRef<'a> { + /// Serialize this instance to `out`, returning the amount of bytes written. + /// + /// The data written to `out` can be decoded with [`Borrowed::decode_band()]`. + pub async fn write_to(&self, out: impl AsyncWrite + Unpin) -> io::Result { + match self { + BandRef::Data(d) => encode::band_to_write(Channel::Data, d, out), + BandRef::Progress(d) => encode::band_to_write(Channel::Progress, d, out), + BandRef::Error(d) => encode::band_to_write(Channel::Error, d, out), + } + .await + } +} + +impl<'a> TextRef<'a> { + /// Serialize this instance to `out`, appending a newline if there is none, returning the amount of bytes written. + pub async fn write_to(&self, out: impl AsyncWrite + Unpin) -> io::Result { + encode::text_to_write(self.0, out).await + } +} + +impl<'a> ErrorRef<'a> { + /// Serialize this line as error to `out`. + /// + /// This includes a marker to allow decoding it outside of a side-band channel, returning the amount of bytes written. + pub async fn write_to(&self, out: impl AsyncWrite + Unpin) -> io::Result { + encode::error_to_write(self.0, out).await + } +} + +impl<'a> PacketLineRef<'a> { + /// Serialize this instance to `out` in git `packetline` format, returning the amount of bytes written to `out`. + pub async fn write_to(&self, out: impl AsyncWrite + Unpin) -> io::Result { + match self { + PacketLineRef::Data(d) => encode::data_to_write(d, out).await, + PacketLineRef::Flush => encode::flush_to_write(out).await, + PacketLineRef::Delimiter => encode::delim_to_write(out).await, + PacketLineRef::ResponseEnd => encode::response_end_to_write(out).await, + } + } +} diff --git a/gix-packetline-blocking/src/line/blocking_io.rs b/gix-packetline-blocking/src/line/blocking_io.rs new file mode 100644 index 00000000000..0354e3de77a --- /dev/null +++ b/gix-packetline-blocking/src/line/blocking_io.rs @@ -0,0 +1,46 @@ +// DO NOT EDIT - this is a copy of gix-packetline/src/line/blocking_io.rs. Run `just copy-packetline` to update it. + +use std::io; + +use crate::{encode, BandRef, Channel, ErrorRef, PacketLineRef, TextRef}; + +impl<'a> BandRef<'a> { + /// Serialize this instance to `out`, returning the amount of bytes written. + /// + /// The data written to `out` can be decoded with [`Borrowed::decode_band()]`. + pub fn write_to(&self, out: impl io::Write) -> io::Result { + match self { + BandRef::Data(d) => encode::band_to_write(Channel::Data, d, out), + BandRef::Progress(d) => encode::band_to_write(Channel::Progress, d, out), + BandRef::Error(d) => encode::band_to_write(Channel::Error, d, out), + } + } +} + +impl<'a> TextRef<'a> { + /// Serialize this instance to `out`, appending a newline if there is none, returning the amount of bytes written. + pub fn write_to(&self, out: impl io::Write) -> io::Result { + encode::text_to_write(self.0, out) + } +} + +impl<'a> ErrorRef<'a> { + /// Serialize this line as error to `out`. + /// + /// This includes a marker to allow decoding it outside of a side-band channel, returning the amount of bytes written. + pub fn write_to(&self, out: impl io::Write) -> io::Result { + encode::error_to_write(self.0, out) + } +} + +impl<'a> PacketLineRef<'a> { + /// Serialize this instance to `out` in git `packetline` format, returning the amount of bytes written to `out`. + pub fn write_to(&self, out: impl io::Write) -> io::Result { + match self { + PacketLineRef::Data(d) => encode::data_to_write(d, out), + PacketLineRef::Flush => encode::flush_to_write(out), + PacketLineRef::Delimiter => encode::delim_to_write(out), + PacketLineRef::ResponseEnd => encode::response_end_to_write(out), + } + } +} diff --git a/gix-packetline-blocking/src/line/mod.rs b/gix-packetline-blocking/src/line/mod.rs new file mode 100644 index 00000000000..d584f0d3a8e --- /dev/null +++ b/gix-packetline-blocking/src/line/mod.rs @@ -0,0 +1,90 @@ +// DO NOT EDIT - this is a copy of gix-packetline/src/line/mod.rs. Run `just copy-packetline` to update it. + +use bstr::BStr; + +use crate::{decode, BandRef, Channel, ErrorRef, PacketLineRef, TextRef, ERR_PREFIX}; + +impl<'a> PacketLineRef<'a> { + /// Return this instance as slice if it's [`Data`][PacketLineRef::Data]. + pub fn as_slice(&self) -> Option<&'a [u8]> { + match self { + PacketLineRef::Data(d) => Some(d), + PacketLineRef::Flush | PacketLineRef::Delimiter | PacketLineRef::ResponseEnd => None, + } + } + /// Return this instance's [`as_slice()`][PacketLineRef::as_slice()] as [`BStr`]. + pub fn as_bstr(&self) -> Option<&'a BStr> { + self.as_slice().map(Into::into) + } + /// Interpret this instance's [`as_slice()`][PacketLineRef::as_slice()] as [`ErrorRef`]. + /// + /// This works for any data received in an error [channel][crate::Channel]. + /// + /// Note that this creates an unchecked error using the slice verbatim, which is useful to [serialize it][ErrorRef::write_to()]. + /// See [`check_error()`][PacketLineRef::check_error()] for a version that assures the error information is in the expected format. + pub fn as_error(&self) -> Option> { + self.as_slice().map(ErrorRef) + } + /// Check this instance's [`as_slice()`][PacketLineRef::as_slice()] is a valid [`ErrorRef`] and return it. + /// + /// This works for any data received in an error [channel][crate::Channel]. + pub fn check_error(&self) -> Option> { + self.as_slice().and_then(|data| { + if data.len() >= ERR_PREFIX.len() && &data[..ERR_PREFIX.len()] == ERR_PREFIX { + Some(ErrorRef(&data[ERR_PREFIX.len()..])) + } else { + None + } + }) + } + /// Return this instance as text, with the trailing newline truncated if present. + pub fn as_text(&self) -> Option> { + self.as_slice().map(Into::into) + } + + /// Interpret the data in this [`slice`][PacketLineRef::as_slice()] as [`BandRef`] according to the given `kind` of channel. + /// + /// Note that this is only relevant in a side-band channel. + /// See [`decode_band()`][PacketLineRef::decode_band()] in case `kind` is unknown. + pub fn as_band(&self, kind: Channel) -> Option> { + self.as_slice().map(|d| match kind { + Channel::Data => BandRef::Data(d), + Channel::Progress => BandRef::Progress(d), + Channel::Error => BandRef::Error(d), + }) + } + + /// Decode the band of this [`slice`][PacketLineRef::as_slice()] + pub fn decode_band(&self) -> Result, decode::band::Error> { + let d = self.as_slice().ok_or(decode::band::Error::NonDataLine)?; + Ok(match d[0] { + 1 => BandRef::Data(&d[1..]), + 2 => BandRef::Progress(&d[1..]), + 3 => BandRef::Error(&d[1..]), + band => return Err(decode::band::Error::InvalidSideBand { band_id: band }), + }) + } +} + +impl<'a> From<&'a [u8]> for TextRef<'a> { + fn from(d: &'a [u8]) -> Self { + let d = if d[d.len() - 1] == b'\n' { &d[..d.len() - 1] } else { d }; + TextRef(d) + } +} + +impl<'a> TextRef<'a> { + /// Return this instance's data. + pub fn as_slice(&self) -> &'a [u8] { + self.0 + } + /// Return this instance's data as [`BStr`]. + pub fn as_bstr(&self) -> &'a BStr { + self.0.into() + } +} + +#[cfg(all(not(feature = "blocking-io"), feature = "async-io"))] +mod async_io; +#[cfg(feature = "blocking-io")] +mod blocking_io; diff --git a/gix-packetline-blocking/src/read/async_io.rs b/gix-packetline-blocking/src/read/async_io.rs new file mode 100644 index 00000000000..a0d347d84c7 --- /dev/null +++ b/gix-packetline-blocking/src/read/async_io.rs @@ -0,0 +1,201 @@ +// DO NOT EDIT - this is a copy of gix-packetline/src/read/async_io.rs. Run `just copy-packetline` to update it. + +use std::io; + +use bstr::ByteSlice; +use futures_io::AsyncRead; +use futures_lite::AsyncReadExt; + +use crate::{ + decode, + read::{ExhaustiveOutcome, ProgressAction, WithSidebands}, + PacketLineRef, StreamingPeekableIter, MAX_LINE_LEN, U16_HEX_BYTES, +}; + +/// Non-IO methods +impl StreamingPeekableIter +where + T: AsyncRead + Unpin, +{ + #[allow(clippy::needless_lifetimes)] // TODO: remove once this is clippy false positive is fixed + async fn read_line_inner<'a>( + reader: &mut T, + buf: &'a mut [u8], + ) -> io::Result, decode::Error>> { + let (hex_bytes, data_bytes) = buf.split_at_mut(4); + reader.read_exact(hex_bytes).await?; + let num_data_bytes = match decode::hex_prefix(hex_bytes) { + Ok(decode::PacketLineOrWantedSize::Line(line)) => return Ok(Ok(line)), + Ok(decode::PacketLineOrWantedSize::Wanted(additional_bytes)) => additional_bytes as usize, + Err(err) => return Ok(Err(err)), + }; + + let (data_bytes, _) = data_bytes.split_at_mut(num_data_bytes); + reader.read_exact(data_bytes).await?; + match decode::to_data_line(data_bytes) { + Ok(line) => Ok(Ok(line)), + Err(err) => Ok(Err(err)), + } + } + + /// This function is needed to help the borrow checker allow us to return references all the time + /// It contains a bunch of logic shared between peek and `read_line` invocations. + async fn read_line_inner_exhaustive<'a>( + reader: &mut T, + buf: &'a mut Vec, + delimiters: &[PacketLineRef<'static>], + fail_on_err_lines: bool, + buf_resize: bool, + trace: bool, + ) -> ExhaustiveOutcome<'a> { + ( + false, + None, + Some(match Self::read_line_inner(reader, buf).await { + Ok(Ok(line)) => { + if trace { + match line { + #[allow(unused_variables)] + PacketLineRef::Data(d) => { + gix_trace::trace!("<< {}", d.as_bstr().trim().as_bstr()); + } + PacketLineRef::Flush => { + gix_trace::trace!("<< FLUSH"); + } + PacketLineRef::Delimiter => { + gix_trace::trace!("<< DELIM"); + } + PacketLineRef::ResponseEnd => { + gix_trace::trace!("<< RESPONSE_END"); + } + } + } + if delimiters.contains(&line) { + let stopped_at = delimiters.iter().find(|l| **l == line).copied(); + buf.clear(); + return (true, stopped_at, None); + } else if fail_on_err_lines { + if let Some(err) = line.check_error() { + let err = err.0.as_bstr().to_owned(); + buf.clear(); + return ( + true, + None, + Some(Err(io::Error::new( + io::ErrorKind::Other, + crate::read::Error { message: err }, + ))), + ); + } + } + let len = line.as_slice().map_or(U16_HEX_BYTES, |s| s.len() + U16_HEX_BYTES); + if buf_resize { + buf.resize(len, 0); + } + Ok(Ok(crate::decode(buf).expect("only valid data here"))) + } + Ok(Err(err)) => { + buf.clear(); + Ok(Err(err)) + } + Err(err) => { + buf.clear(); + Err(err) + } + }), + ) + } + + /// Read a packet line into the internal buffer and return it. + /// + /// Returns `None` if the end of iteration is reached because of one of the following: + /// + /// * natural EOF + /// * ERR packet line encountered if [`fail_on_err_lines()`][StreamingPeekableIter::fail_on_err_lines()] is true. + /// * A `delimiter` packet line encountered + pub async fn read_line(&mut self) -> Option, decode::Error>>> { + if self.is_done { + return None; + } + if !self.peek_buf.is_empty() { + std::mem::swap(&mut self.peek_buf, &mut self.buf); + self.peek_buf.clear(); + Some(Ok(Ok(crate::decode(&self.buf).expect("only valid data in peek buf")))) + } else { + if self.buf.len() != MAX_LINE_LEN { + self.buf.resize(MAX_LINE_LEN, 0); + } + let (is_done, stopped_at, res) = Self::read_line_inner_exhaustive( + &mut self.read, + &mut self.buf, + self.delimiters, + self.fail_on_err_lines, + false, + self.trace, + ) + .await; + self.is_done = is_done; + self.stopped_at = stopped_at; + res + } + } + + /// Peek the next packet line without consuming it. Returns `None` if a stop-packet or an error + /// was encountered. + /// + /// Multiple calls to peek will return the same packet line, if there is one. + pub async fn peek_line(&mut self) -> Option, decode::Error>>> { + if self.is_done { + return None; + } + if self.peek_buf.is_empty() { + self.peek_buf.resize(MAX_LINE_LEN, 0); + let (is_done, stopped_at, res) = Self::read_line_inner_exhaustive( + &mut self.read, + &mut self.peek_buf, + self.delimiters, + self.fail_on_err_lines, + true, + self.trace, + ) + .await; + self.is_done = is_done; + self.stopped_at = stopped_at; + res + } else { + Some(Ok(Ok(crate::decode(&self.peek_buf).expect("only valid data here")))) + } + } + + /// Same as [`as_read_with_sidebands(…)`][StreamingPeekableIter::as_read_with_sidebands()], but for channels without side band support. + /// + /// Due to the preconfigured function type this method can be called without 'turbofish'. + #[allow(clippy::type_complexity)] + pub fn as_read(&mut self) -> WithSidebands<'_, T, fn(bool, &[u8]) -> ProgressAction> { + WithSidebands::new(self) + } + + /// Return this instance as implementor of [`Read`][io::Read] assuming side bands to be used in all received packet lines. + /// Each invocation of [`read_line()`][io::BufRead::read_line()] returns a packet line. + /// + /// Progress or error information will be passed to the given `handle_progress(is_error, text)` function, with `is_error: bool` + /// being true in case the `text` is to be interpreted as error. + /// + /// _Please note_ that side bands need to be negotiated with the server. + pub fn as_read_with_sidebands ProgressAction + Unpin>( + &mut self, + handle_progress: F, + ) -> WithSidebands<'_, T, F> { + WithSidebands::with_progress_handler(self, handle_progress) + } + + /// Same as [`as_read_with_sidebands(…)`][StreamingPeekableIter::as_read_with_sidebands()], but for channels without side band support. + /// + /// The type parameter `F` needs to be configured for this method to be callable using the 'turbofish' operator. + /// Use [`as_read()`][StreamingPeekableIter::as_read()]. + pub fn as_read_without_sidebands ProgressAction + Unpin>( + &mut self, + ) -> WithSidebands<'_, T, F> { + WithSidebands::without_progress_handler(self) + } +} diff --git a/gix-packetline-blocking/src/read/blocking_io.rs b/gix-packetline-blocking/src/read/blocking_io.rs new file mode 100644 index 00000000000..ce2035a1a3e --- /dev/null +++ b/gix-packetline-blocking/src/read/blocking_io.rs @@ -0,0 +1,192 @@ +// DO NOT EDIT - this is a copy of gix-packetline/src/read/blocking_io.rs. Run `just copy-packetline` to update it. + +use std::io; + +use bstr::ByteSlice; + +use crate::{ + decode, + read::{ExhaustiveOutcome, ProgressAction, WithSidebands}, + PacketLineRef, StreamingPeekableIter, MAX_LINE_LEN, U16_HEX_BYTES, +}; + +/// Non-IO methods +impl StreamingPeekableIter +where + T: io::Read, +{ + fn read_line_inner<'a>(reader: &mut T, buf: &'a mut [u8]) -> io::Result, decode::Error>> { + let (hex_bytes, data_bytes) = buf.split_at_mut(4); + reader.read_exact(hex_bytes)?; + let num_data_bytes = match decode::hex_prefix(hex_bytes) { + Ok(decode::PacketLineOrWantedSize::Line(line)) => return Ok(Ok(line)), + Ok(decode::PacketLineOrWantedSize::Wanted(additional_bytes)) => additional_bytes as usize, + Err(err) => return Ok(Err(err)), + }; + + let (data_bytes, _) = data_bytes.split_at_mut(num_data_bytes); + reader.read_exact(data_bytes)?; + match decode::to_data_line(data_bytes) { + Ok(line) => Ok(Ok(line)), + Err(err) => Ok(Err(err)), + } + } + + /// This function is needed to help the borrow checker allow us to return references all the time + /// It contains a bunch of logic shared between peek and `read_line` invocations. + fn read_line_inner_exhaustive<'a>( + reader: &mut T, + buf: &'a mut Vec, + delimiters: &[PacketLineRef<'static>], + fail_on_err_lines: bool, + buf_resize: bool, + trace: bool, + ) -> ExhaustiveOutcome<'a> { + ( + false, + None, + Some(match Self::read_line_inner(reader, buf) { + Ok(Ok(line)) => { + if trace { + match line { + #[allow(unused_variables)] + PacketLineRef::Data(d) => { + gix_trace::trace!("<< {}", d.as_bstr().trim().as_bstr()); + } + PacketLineRef::Flush => { + gix_trace::trace!("<< FLUSH"); + } + PacketLineRef::Delimiter => { + gix_trace::trace!("<< DELIM"); + } + PacketLineRef::ResponseEnd => { + gix_trace::trace!("<< RESPONSE_END"); + } + } + } + if delimiters.contains(&line) { + let stopped_at = delimiters.iter().find(|l| **l == line).copied(); + buf.clear(); + return (true, stopped_at, None); + } else if fail_on_err_lines { + if let Some(err) = line.check_error() { + let err = err.0.as_bstr().to_owned(); + buf.clear(); + return ( + true, + None, + Some(Err(io::Error::new( + io::ErrorKind::Other, + crate::read::Error { message: err }, + ))), + ); + } + } + let len = line.as_slice().map_or(U16_HEX_BYTES, |s| s.len() + U16_HEX_BYTES); + if buf_resize { + buf.resize(len, 0); + } + // TODO(borrowchk): remove additional decoding of internal buffer which is needed only to make it past borrowchk + Ok(Ok(crate::decode(buf).expect("only valid data here"))) + } + Ok(Err(err)) => { + buf.clear(); + Ok(Err(err)) + } + Err(err) => { + buf.clear(); + Err(err) + } + }), + ) + } + + /// Read a packet line into the internal buffer and return it. + /// + /// Returns `None` if the end of iteration is reached because of one of the following: + /// + /// * natural EOF + /// * ERR packet line encountered if [`fail_on_err_lines()`][StreamingPeekableIter::fail_on_err_lines()] is true. + /// * A `delimiter` packet line encountered + pub fn read_line(&mut self) -> Option, decode::Error>>> { + if self.is_done { + return None; + } + if !self.peek_buf.is_empty() { + std::mem::swap(&mut self.peek_buf, &mut self.buf); + self.peek_buf.clear(); + Some(Ok(Ok(crate::decode(&self.buf).expect("only valid data in peek buf")))) + } else { + if self.buf.len() != MAX_LINE_LEN { + self.buf.resize(MAX_LINE_LEN, 0); + } + let (is_done, stopped_at, res) = Self::read_line_inner_exhaustive( + &mut self.read, + &mut self.buf, + self.delimiters, + self.fail_on_err_lines, + false, + self.trace, + ); + self.is_done = is_done; + self.stopped_at = stopped_at; + res + } + } + + /// Peek the next packet line without consuming it. Returns `None` if a stop-packet or an error + /// was encountered. + /// + /// Multiple calls to peek will return the same packet line, if there is one. + pub fn peek_line(&mut self) -> Option, decode::Error>>> { + if self.is_done { + return None; + } + if self.peek_buf.is_empty() { + self.peek_buf.resize(MAX_LINE_LEN, 0); + let (is_done, stopped_at, res) = Self::read_line_inner_exhaustive( + &mut self.read, + &mut self.peek_buf, + self.delimiters, + self.fail_on_err_lines, + true, + self.trace, + ); + self.is_done = is_done; + self.stopped_at = stopped_at; + res + } else { + Some(Ok(Ok(crate::decode(&self.peek_buf).expect("only valid data here")))) + } + } + + /// Return this instance as implementor of [`Read`][io::Read] assuming side bands to be used in all received packet lines. + /// Each invocation of [`read_line()`][io::BufRead::read_line()] returns a packet line. + /// + /// Progress or error information will be passed to the given `handle_progress(is_error, text)` function, with `is_error: bool` + /// being true in case the `text` is to be interpreted as error. + /// + /// _Please note_ that side bands need to be negotiated with the server. + pub fn as_read_with_sidebands ProgressAction>( + &mut self, + handle_progress: F, + ) -> WithSidebands<'_, T, F> { + WithSidebands::with_progress_handler(self, handle_progress) + } + + /// Same as [`as_read_with_sidebands(…)`][StreamingPeekableIter::as_read_with_sidebands()], but for channels without side band support. + /// + /// The type parameter `F` needs to be configured for this method to be callable using the 'turbofish' operator. + /// Use [`as_read()`][StreamingPeekableIter::as_read()]. + pub fn as_read_without_sidebands ProgressAction>(&mut self) -> WithSidebands<'_, T, F> { + WithSidebands::without_progress_handler(self) + } + + /// Same as [`as_read_with_sidebands(…)`][StreamingPeekableIter::as_read_with_sidebands()], but for channels without side band support. + /// + /// Due to the preconfigured function type this method can be called without 'turbofish'. + #[allow(clippy::type_complexity)] + pub fn as_read(&mut self) -> WithSidebands<'_, T, fn(bool, &[u8]) -> ProgressAction> { + WithSidebands::new(self) + } +} diff --git a/gix-packetline-blocking/src/read/mod.rs b/gix-packetline-blocking/src/read/mod.rs new file mode 100644 index 00000000000..386fa492b80 --- /dev/null +++ b/gix-packetline-blocking/src/read/mod.rs @@ -0,0 +1,130 @@ +// DO NOT EDIT - this is a copy of gix-packetline/src/read/mod.rs. Run `just copy-packetline` to update it. + +#[cfg(any(feature = "blocking-io", feature = "async-io"))] +use crate::MAX_LINE_LEN; +use crate::{PacketLineRef, StreamingPeekableIter, U16_HEX_BYTES}; + +/// Allow the read-progress handler to determine how to continue. +#[derive(Copy, Clone, Debug, Eq, PartialEq)] +pub enum ProgressAction { + /// Continue reading the next progress if available. + Continue, + /// Abort all IO even if more would be available, claiming the operation was interrupted. + Interrupt, +} + +#[cfg(any(feature = "blocking-io", feature = "async-io"))] +type ExhaustiveOutcome<'a> = ( + bool, // is_done + Option>, // stopped_at + Option, crate::decode::Error>>>, // actual method result +); + +mod error { + use std::fmt::{Debug, Display, Formatter}; + + use bstr::BString; + + /// The error representing an ERR packet line, as possibly wrapped into an `std::io::Error` in + /// [`read_line(…)`][super::StreamingPeekableIter::read_line()]. + #[derive(Debug)] + pub struct Error { + /// The contents of the ERR line, with `ERR` portion stripped. + pub message: BString, + } + + impl Display for Error { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + Display::fmt(&self.message, f) + } + } + + impl std::error::Error for Error {} +} +pub use error::Error; + +impl StreamingPeekableIter { + /// Return a new instance from `read` which will stop decoding packet lines when receiving one of the given `delimiters`. + /// If `trace` is `true`, all packetlines received or sent will be passed to the facilities of the `gix-trace` crate. + pub fn new(read: T, delimiters: &'static [PacketLineRef<'static>], trace: bool) -> Self { + StreamingPeekableIter { + read, + #[cfg(any(feature = "blocking-io", feature = "async-io"))] + buf: vec![0; MAX_LINE_LEN], + peek_buf: Vec::new(), + delimiters, + fail_on_err_lines: false, + is_done: false, + stopped_at: None, + trace, + } + } + + /// Modify the peek buffer, overwriting the byte at `position` with the given byte to `replace_with` while truncating + /// it to contain only bytes until the newly replaced `position`. + /// + /// This is useful if you would want to remove 'special bytes' hidden behind, say a NULL byte to disappear and allow + /// standard line readers to read the next line as usual. + /// + /// **Note** that `position` does not include the 4 bytes prefix (they are invisible outside the reader) + pub fn peek_buffer_replace_and_truncate(&mut self, position: usize, replace_with: u8) { + let position = position + U16_HEX_BYTES; + self.peek_buf[position] = replace_with; + + let new_len = position + 1; + self.peek_buf.truncate(new_len); + self.peek_buf[..4].copy_from_slice(&crate::encode::u16_to_hex((new_len) as u16)); + } + + /// Returns the packet line that stopped the iteration, or + /// `None` if the end wasn't reached yet, on EOF, or if [`fail_on_err_lines()`][StreamingPeekableIter::fail_on_err_lines()] was true. + pub fn stopped_at(&self) -> Option> { + self.stopped_at + } + + /// Reset all iteration state allowing to continue a stopped iteration that is not yet at EOF. + /// + /// This can happen once a delimiter is reached. + pub fn reset(&mut self) { + let delimiters = std::mem::take(&mut self.delimiters); + self.reset_with(delimiters); + } + + /// Similar to [`reset()`][StreamingPeekableIter::reset()] with support to changing the `delimiters`. + pub fn reset_with(&mut self, delimiters: &'static [PacketLineRef<'static>]) { + self.delimiters = delimiters; + self.is_done = false; + self.stopped_at = None; + } + + /// If `value` is `true` the provider will check for special `ERR` packet lines and stop iteration when one is encountered. + /// + /// Use [`stopped_at()]`[`StreamingPeekableIter::stopped_at()`] to inspect the cause of the end of the iteration. + /// ne + pub fn fail_on_err_lines(&mut self, value: bool) { + self.fail_on_err_lines = value; + } + + /// Replace the reader used with the given `read`, resetting all other iteration state as well. + pub fn replace(&mut self, read: T) -> T { + let prev = std::mem::replace(&mut self.read, read); + self.reset(); + self.fail_on_err_lines = false; + prev + } + + /// Return the inner read + pub fn into_inner(self) -> T { + self.read + } +} + +#[cfg(feature = "blocking-io")] +mod blocking_io; + +#[cfg(all(not(feature = "blocking-io"), feature = "async-io"))] +mod async_io; + +mod sidebands; +#[cfg(any(feature = "blocking-io", feature = "async-io"))] +pub use sidebands::WithSidebands; diff --git a/gix-packetline-blocking/src/read/sidebands/async_io.rs b/gix-packetline-blocking/src/read/sidebands/async_io.rs new file mode 100644 index 00000000000..0582ea11ed8 --- /dev/null +++ b/gix-packetline-blocking/src/read/sidebands/async_io.rs @@ -0,0 +1,383 @@ +// DO NOT EDIT - this is a copy of gix-packetline/src/read/sidebands/async_io.rs. Run `just copy-packetline` to update it. + +use std::{ + future::Future, + pin::Pin, + task::{Context, Poll}, +}; + +use futures_io::{AsyncBufRead, AsyncRead}; +use futures_lite::ready; + +use crate::{decode, read::ProgressAction, BandRef, PacketLineRef, StreamingPeekableIter, TextRef, U16_HEX_BYTES}; + +type ReadLineResult<'a> = Option, decode::Error>>>; +/// An implementor of [`AsyncBufRead`] yielding packet lines on each call to [`read_line()`][AsyncBufRead::read_line()]. +/// It's also possible to hide the underlying packet lines using the [`Read`][AsyncRead] implementation which is useful +/// if they represent binary data, like the one of a pack file. +pub struct WithSidebands<'a, T, F> +where + T: AsyncRead, +{ + state: State<'a, T>, + handle_progress: Option, + pos: usize, + cap: usize, +} + +impl<'a, T, F> Drop for WithSidebands<'a, T, F> +where + T: AsyncRead, +{ + fn drop(&mut self) { + if let State::Idle { ref mut parent } = self.state { + parent + .as_mut() + .expect("parent is always available if we are idle") + .reset(); + } + } +} + +impl<'a, T> WithSidebands<'a, T, fn(bool, &[u8]) -> ProgressAction> +where + T: AsyncRead, +{ + /// Create a new instance with the given provider as `parent`. + pub fn new(parent: &'a mut StreamingPeekableIter) -> Self { + WithSidebands { + state: State::Idle { parent: Some(parent) }, + handle_progress: None, + pos: 0, + cap: 0, + } + } +} + +enum State<'a, T> { + Idle { + parent: Option<&'a mut StreamingPeekableIter>, + }, + ReadLine { + read_line: Pin> + 'a>>, + parent_inactive: Option<*mut StreamingPeekableIter>, + }, +} + +/// # SAFETY +/// It's safe because T is `Send` and we have a test that assures that our `StreamingPeekableIter` is `Send` as well, +/// hence the `*mut _` is `Send`. +/// `read_line` isn't send and we can't declare it as such as it forces `Send` in all places (BUT WHY IS THAT A PROBLEM, I don't recall). +/// However, it's only used when pinned and thus isn't actually sent anywhere, it's a secondary state of the future used after it was Send +/// to a thread possibly. +// TODO: Is it possible to declare it as it should be? +#[allow(unsafe_code, clippy::non_send_fields_in_send_ty)] +unsafe impl<'a, T> Send for State<'a, T> where T: Send {} + +impl<'a, T, F> WithSidebands<'a, T, F> +where + T: AsyncRead + Unpin, + F: FnMut(bool, &[u8]) -> ProgressAction + Unpin, +{ + /// Create a new instance with the given `parent` provider and the `handle_progress` function. + /// + /// Progress or error information will be passed to the given `handle_progress(is_error, text)` function, with `is_error: bool` + /// being true in case the `text` is to be interpreted as error. + pub fn with_progress_handler(parent: &'a mut StreamingPeekableIter, handle_progress: F) -> Self { + WithSidebands { + state: State::Idle { parent: Some(parent) }, + handle_progress: Some(handle_progress), + pos: 0, + cap: 0, + } + } + + /// Create a new instance without a progress handler. + pub fn without_progress_handler(parent: &'a mut StreamingPeekableIter) -> Self { + WithSidebands { + state: State::Idle { parent: Some(parent) }, + handle_progress: None, + pos: 0, + cap: 0, + } + } + + /// Forwards to the parent [`StreamingPeekableIter::reset_with()`] + pub fn reset_with(&mut self, delimiters: &'static [PacketLineRef<'static>]) { + if let State::Idle { ref mut parent } = self.state { + parent + .as_mut() + .expect("parent is always available if we are idle") + .reset_with(delimiters) + } + } + + /// Forwards to the parent [`StreamingPeekableIter::stopped_at()`] + pub fn stopped_at(&self) -> Option> { + match self.state { + State::Idle { ref parent } => { + parent + .as_ref() + .expect("parent is always available if we are idle") + .stopped_at + } + _ => None, + } + } + + /// Set or unset the progress handler. + pub fn set_progress_handler(&mut self, handle_progress: Option) { + self.handle_progress = handle_progress; + } + + /// Effectively forwards to the parent [`StreamingPeekableIter::peek_line()`], allowing to see what would be returned + /// next on a call to [`read_line()`][io::BufRead::read_line()]. + /// + /// # Warning + /// + /// This skips all sideband handling and may return an unprocessed line with sidebands still contained in it. + pub async fn peek_data_line(&mut self) -> Option>> { + match self.state { + State::Idle { ref mut parent } => match parent + .as_mut() + .expect("parent is always available if we are idle") + .peek_line() + .await + { + Some(Ok(Ok(PacketLineRef::Data(line)))) => Some(Ok(Ok(line))), + Some(Ok(Err(err))) => Some(Ok(Err(err))), + Some(Err(err)) => Some(Err(err)), + _ => None, + }, + _ => None, + } + } + + /// Read a packet line as string line. + pub fn read_line_to_string<'b>(&'b mut self, buf: &'b mut String) -> ReadLineFuture<'a, 'b, T, F> { + ReadLineFuture { parent: self, buf } + } + + /// Read a packet line from the underlying packet reader, returning empty lines if a stop-packetline was reached. + /// + /// # Warning + /// + /// This skips all sideband handling and may return an unprocessed line with sidebands still contained in it. + pub async fn read_data_line(&mut self) -> Option, decode::Error>>> { + match &mut self.state { + State::Idle { parent: Some(parent) } => { + assert_eq!( + self.cap, 0, + "we don't support partial buffers right now - read-line must be used consistently" + ); + parent.read_line().await + } + _ => None, + } + } +} + +pub struct ReadDataLineFuture<'a, 'b, T: AsyncRead, F> { + parent: &'b mut WithSidebands<'a, T, F>, + buf: &'b mut Vec, +} + +impl<'a, 'b, T, F> Future for ReadDataLineFuture<'a, 'b, T, F> +where + T: AsyncRead + Unpin, + F: FnMut(bool, &[u8]) -> ProgressAction + Unpin, +{ + type Output = std::io::Result; + + fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + assert_eq!( + self.parent.cap, 0, + "we don't support partial buffers right now - read-line must be used consistently" + ); + let Self { buf, parent } = &mut *self; + let line = ready!(Pin::new(parent).poll_fill_buf(cx))?; + buf.clear(); + buf.extend_from_slice(line); + let bytes = line.len(); + self.parent.cap = 0; + Poll::Ready(Ok(bytes)) + } +} + +pub struct ReadLineFuture<'a, 'b, T: AsyncRead, F> { + parent: &'b mut WithSidebands<'a, T, F>, + buf: &'b mut String, +} + +impl<'a, 'b, T, F> Future for ReadLineFuture<'a, 'b, T, F> +where + T: AsyncRead + Unpin, + F: FnMut(bool, &[u8]) -> ProgressAction + Unpin, +{ + type Output = std::io::Result; + + fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + assert_eq!( + self.parent.cap, 0, + "we don't support partial buffers right now - read-line must be used consistently" + ); + let Self { buf, parent } = &mut *self; + let line = std::str::from_utf8(ready!(Pin::new(parent).poll_fill_buf(cx))?) + .map_err(|err| std::io::Error::new(std::io::ErrorKind::Other, err))?; + buf.clear(); + buf.push_str(line); + let bytes = line.len(); + self.parent.cap = 0; + Poll::Ready(Ok(bytes)) + } +} + +impl<'a, T, F> AsyncBufRead for WithSidebands<'a, T, F> +where + T: AsyncRead + Unpin, + F: FnMut(bool, &[u8]) -> ProgressAction + Unpin, +{ + fn poll_fill_buf(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + use std::io; + + use futures_lite::FutureExt; + { + let this = self.as_mut().get_mut(); + if this.pos >= this.cap { + let (ofs, cap) = loop { + match this.state { + State::Idle { ref mut parent } => { + let parent = parent.take().expect("parent to be present here"); + let inactive = parent as *mut _; + this.state = State::ReadLine { + read_line: parent.read_line().boxed_local(), + parent_inactive: Some(inactive), + } + } + State::ReadLine { + ref mut read_line, + ref mut parent_inactive, + } => { + let line = ready!(read_line.poll(cx)); + + this.state = { + let parent = parent_inactive.take().expect("parent pointer always set"); + // SAFETY: It's safe to recover the original mutable reference (from which + // the `read_line` future was created as the latter isn't accessible anymore + // once the state is set to Idle. In other words, either one or the other are + // accessible, never both at the same time. + // Also: We keep a pointer around which is protected by borrowcheck since it's created + // from a legal mutable reference which is moved into the read_line future - if it was manually + // implemented we would be able to re-obtain it from there. + #[allow(unsafe_code)] + let parent = unsafe { &mut *parent }; + State::Idle { parent: Some(parent) } + }; + + let line = match line { + Some(line) => line?.map_err(|err| io::Error::new(io::ErrorKind::Other, err))?, + None => break (0, 0), + }; + + match this.handle_progress.as_mut() { + Some(handle_progress) => { + let band = line + .decode_band() + .map_err(|err| io::Error::new(io::ErrorKind::Other, err))?; + const ENCODED_BAND: usize = 1; + match band { + BandRef::Data(d) => { + if d.is_empty() { + continue; + } + break (U16_HEX_BYTES + ENCODED_BAND, d.len()); + } + BandRef::Progress(d) => { + let text = TextRef::from(d).0; + match handle_progress(false, text) { + ProgressAction::Continue => {} + ProgressAction::Interrupt => { + return Poll::Ready(Err(io::Error::new( + std::io::ErrorKind::Other, + "interrupted by user", + ))) + } + }; + } + BandRef::Error(d) => { + let text = TextRef::from(d).0; + match handle_progress(true, text) { + ProgressAction::Continue => {} + ProgressAction::Interrupt => { + return Poll::Ready(Err(io::Error::new( + io::ErrorKind::Other, + "interrupted by user", + ))) + } + }; + } + }; + } + None => { + break match line.as_slice() { + Some(d) => (U16_HEX_BYTES, d.len()), + None => { + return Poll::Ready(Err(io::Error::new( + io::ErrorKind::UnexpectedEof, + "encountered non-data line in a data-line only context", + ))) + } + } + } + } + } + } + }; + this.cap = cap + ofs; + this.pos = ofs; + } + } + let range = self.pos..self.cap; + match &self.get_mut().state { + State::Idle { parent } => Poll::Ready(Ok(&parent.as_ref().expect("parent always available").buf[range])), + State::ReadLine { .. } => unreachable!("at least in theory"), + } + } + + fn consume(self: Pin<&mut Self>, amt: usize) { + let this = self.get_mut(); + this.pos = std::cmp::min(this.pos + amt, this.cap); + } +} + +impl<'a, T, F> AsyncRead for WithSidebands<'a, T, F> +where + T: AsyncRead + Unpin, + F: FnMut(bool, &[u8]) -> ProgressAction + Unpin, +{ + fn poll_read(mut self: Pin<&mut Self>, cx: &mut Context<'_>, buf: &mut [u8]) -> Poll> { + use std::io::Read; + let mut rem = ready!(self.as_mut().poll_fill_buf(cx))?; + let nread = rem.read(buf)?; + self.consume(nread); + Poll::Ready(Ok(nread)) + } +} + +#[cfg(test)] +mod tests { + use super::*; + fn receiver(_i: T) {} + + /// We want to declare items containing pointers of `StreamingPeekableIter` `Send` as well, so it must be `Send` itself. + #[test] + fn streaming_peekable_iter_is_send() { + receiver(StreamingPeekableIter::new(Vec::::new(), &[], false)); + } + + #[test] + fn state_is_send() { + let mut s = StreamingPeekableIter::new(Vec::::new(), &[], false); + receiver(State::Idle { parent: Some(&mut s) }); + } +} diff --git a/gix-packetline-blocking/src/read/sidebands/blocking_io.rs b/gix-packetline-blocking/src/read/sidebands/blocking_io.rs new file mode 100644 index 00000000000..c4dabb093a1 --- /dev/null +++ b/gix-packetline-blocking/src/read/sidebands/blocking_io.rs @@ -0,0 +1,218 @@ +// DO NOT EDIT - this is a copy of gix-packetline/src/read/sidebands/blocking_io.rs. Run `just copy-packetline` to update it. + +use std::{io, io::BufRead}; + +use crate::{read::ProgressAction, BandRef, PacketLineRef, StreamingPeekableIter, TextRef, U16_HEX_BYTES}; + +/// An implementor of [`BufRead`][io::BufRead] yielding packet lines on each call to [`read_line()`][io::BufRead::read_line()]. +/// It's also possible to hide the underlying packet lines using the [`Read`][io::Read] implementation which is useful +/// if they represent binary data, like the one of a pack file. +pub struct WithSidebands<'a, T, F> +where + T: io::Read, +{ + parent: &'a mut StreamingPeekableIter, + handle_progress: Option, + pos: usize, + cap: usize, +} + +impl<'a, T, F> Drop for WithSidebands<'a, T, F> +where + T: io::Read, +{ + fn drop(&mut self) { + self.parent.reset(); + } +} + +impl<'a, T> WithSidebands<'a, T, fn(bool, &[u8]) -> ProgressAction> +where + T: io::Read, +{ + /// Create a new instance with the given provider as `parent`. + pub fn new(parent: &'a mut StreamingPeekableIter) -> Self { + WithSidebands { + parent, + handle_progress: None, + pos: 0, + cap: 0, + } + } +} + +impl<'a, T, F> WithSidebands<'a, T, F> +where + T: io::Read, + F: FnMut(bool, &[u8]) -> ProgressAction, +{ + /// Create a new instance with the given `parent` provider and the `handle_progress` function. + /// + /// Progress or error information will be passed to the given `handle_progress(is_error, text)` function, with `is_error: bool` + /// being true in case the `text` is to be interpreted as error. + pub fn with_progress_handler(parent: &'a mut StreamingPeekableIter, handle_progress: F) -> Self { + WithSidebands { + parent, + handle_progress: Some(handle_progress), + pos: 0, + cap: 0, + } + } + + /// Create a new instance without a progress handler. + pub fn without_progress_handler(parent: &'a mut StreamingPeekableIter) -> Self { + WithSidebands { + parent, + handle_progress: None, + pos: 0, + cap: 0, + } + } + + /// Forwards to the parent [`StreamingPeekableIter::reset_with()`] + pub fn reset_with(&mut self, delimiters: &'static [PacketLineRef<'static>]) { + self.parent.reset_with(delimiters) + } + + /// Forwards to the parent [`StreamingPeekableIter::stopped_at()`] + pub fn stopped_at(&self) -> Option> { + self.parent.stopped_at + } + + /// Set or unset the progress handler. + pub fn set_progress_handler(&mut self, handle_progress: Option) { + self.handle_progress = handle_progress; + } + + /// Effectively forwards to the parent [`StreamingPeekableIter::peek_line()`], allowing to see what would be returned + /// next on a call to [`read_line()`][io::BufRead::read_line()]. + /// + /// # Warning + /// + /// This skips all sideband handling and may return an unprocessed line with sidebands still contained in it. + pub fn peek_data_line(&mut self) -> Option>> { + match self.parent.peek_line() { + Some(Ok(Ok(PacketLineRef::Data(line)))) => Some(Ok(Ok(line))), + Some(Ok(Err(err))) => Some(Ok(Err(err))), + Some(Err(err)) => Some(Err(err)), + _ => None, + } + } + + /// Read a whole packetline from the underlying reader, with empty lines indicating a stop packetline. + /// + /// # Warning + /// + /// This skips all sideband handling and may return an unprocessed line with sidebands still contained in it. + pub fn read_data_line(&mut self) -> Option, crate::decode::Error>>> { + assert_eq!( + self.cap, 0, + "we don't support partial buffers right now - read-line must be used consistently" + ); + self.parent.read_line() + } + + /// Like `BufRead::read_line()`, but will only read one packetline at a time. + /// + /// It will also be easier to call as sometimes it's unclear which implementation we get on a type like this with + /// plenty of generic parameters. + pub fn read_line_to_string(&mut self, buf: &mut String) -> io::Result { + assert_eq!( + self.cap, 0, + "we don't support partial buffers right now - read-line must be used consistently" + ); + let line = std::str::from_utf8(self.fill_buf()?).map_err(|err| io::Error::new(io::ErrorKind::Other, err))?; + buf.push_str(line); + let bytes = line.len(); + self.cap = 0; + Ok(bytes) + } +} + +impl<'a, T, F> BufRead for WithSidebands<'a, T, F> +where + T: io::Read, + F: FnMut(bool, &[u8]) -> ProgressAction, +{ + fn fill_buf(&mut self) -> io::Result<&[u8]> { + if self.pos >= self.cap { + let (ofs, cap) = loop { + let line = match self.parent.read_line() { + Some(line) => line?.map_err(|err| io::Error::new(io::ErrorKind::Other, err))?, + None => break (0, 0), + }; + match self.handle_progress.as_mut() { + Some(handle_progress) => { + let band = line + .decode_band() + .map_err(|err| io::Error::new(io::ErrorKind::Other, err))?; + const ENCODED_BAND: usize = 1; + match band { + BandRef::Data(d) => { + if d.is_empty() { + continue; + } + break (U16_HEX_BYTES + ENCODED_BAND, d.len()); + } + BandRef::Progress(d) => { + let text = TextRef::from(d).0; + match handle_progress(false, text) { + ProgressAction::Continue => {} + ProgressAction::Interrupt => { + return Err(std::io::Error::new( + std::io::ErrorKind::Other, + "interrupted by user", + )) + } + }; + } + BandRef::Error(d) => { + let text = TextRef::from(d).0; + match handle_progress(true, text) { + ProgressAction::Continue => {} + ProgressAction::Interrupt => { + return Err(std::io::Error::new( + std::io::ErrorKind::Other, + "interrupted by user", + )) + } + }; + } + }; + } + None => { + break match line.as_slice() { + Some(d) => (U16_HEX_BYTES, d.len()), + None => { + return Err(io::Error::new( + io::ErrorKind::UnexpectedEof, + "encountered non-data line in a data-line only context", + )) + } + } + } + } + }; + self.cap = cap + ofs; + self.pos = ofs; + } + Ok(&self.parent.buf[self.pos..self.cap]) + } + + fn consume(&mut self, amt: usize) { + self.pos = std::cmp::min(self.pos + amt, self.cap); + } +} + +impl<'a, T, F> io::Read for WithSidebands<'a, T, F> +where + T: io::Read, + F: FnMut(bool, &[u8]) -> ProgressAction, +{ + fn read(&mut self, buf: &mut [u8]) -> io::Result { + let mut rem = self.fill_buf()?; + let nread = rem.read(buf)?; + self.consume(nread); + Ok(nread) + } +} diff --git a/gix-packetline-blocking/src/read/sidebands/mod.rs b/gix-packetline-blocking/src/read/sidebands/mod.rs new file mode 100644 index 00000000000..d9ff58f7809 --- /dev/null +++ b/gix-packetline-blocking/src/read/sidebands/mod.rs @@ -0,0 +1,11 @@ +// DO NOT EDIT - this is a copy of gix-packetline/src/read/sidebands/mod.rs. Run `just copy-packetline` to update it. + +#[cfg(feature = "blocking-io")] +mod blocking_io; +#[cfg(feature = "blocking-io")] +pub use blocking_io::WithSidebands; + +#[cfg(all(not(feature = "blocking-io"), feature = "async-io"))] +mod async_io; +#[cfg(all(not(feature = "blocking-io"), feature = "async-io"))] +pub use async_io::WithSidebands; diff --git a/gix-packetline-blocking/src/write/async_io.rs b/gix-packetline-blocking/src/write/async_io.rs new file mode 100644 index 00000000000..7b76c011fa9 --- /dev/null +++ b/gix-packetline-blocking/src/write/async_io.rs @@ -0,0 +1,99 @@ +// DO NOT EDIT - this is a copy of gix-packetline/src/write/async_io.rs. Run `just copy-packetline` to update it. + +use std::{ + io, + pin::Pin, + task::{Context, Poll}, +}; + +use futures_io::AsyncWrite; + +use crate::{encode, MAX_DATA_LEN, U16_HEX_BYTES}; + +pin_project_lite::pin_project! { + /// An implementor of [`Write`][io::Write] which passes all input to an inner `Write` in packet line data encoding, + /// one line per `write(…)` call or as many lines as it takes if the data doesn't fit into the maximum allowed line length. + pub struct Writer { + #[pin] + inner: encode::LineWriter<'static, T>, + state: State, + } +} + +enum State { + Idle, + WriteData(usize), +} + +impl Writer { + /// Create a new instance from the given `write` + pub fn new(write: T) -> Self { + Writer { + inner: encode::LineWriter::new(write, &[], &[]), + state: State::Idle, + } + } + + /// Return the inner writer, consuming self. + pub fn into_inner(self) -> T { + self.inner.into_inner() + } + + /// Return a mutable reference to the inner writer, useful if packet lines should be serialized directly. + pub fn inner_mut(&mut self) -> &mut T { + &mut self.inner.writer + } +} + +/// Non-IO methods +impl Writer { + /// If called, each call to [`write()`][io::Write::write()] will write bytes as is. + pub fn enable_binary_mode(&mut self) { + self.inner.suffix = &[]; + } + /// If called, each call to [`write()`][io::Write::write()] will write the input as text, appending a trailing newline + /// if needed before writing. + pub fn enable_text_mode(&mut self) { + self.inner.suffix = &[b'\n']; + } +} + +impl AsyncWrite for Writer { + fn poll_write(self: Pin<&mut Self>, cx: &mut Context<'_>, buf: &[u8]) -> Poll> { + let mut this = self.project(); + loop { + match this.state { + State::Idle => { + if buf.is_empty() { + return Poll::Ready(Err(io::Error::new( + io::ErrorKind::Other, + "empty packet lines are not permitted as '0004' is invalid", + ))); + } + *this.state = State::WriteData(0) + } + State::WriteData(written) => { + while *written != buf.len() { + let data = &buf[*written..*written + (buf.len() - *written).min(MAX_DATA_LEN)]; + let n = futures_lite::ready!(this.inner.as_mut().poll_write(cx, data))?; + if n == 0 { + return Poll::Ready(Err(io::ErrorKind::WriteZero.into())); + } + *written += n; + *written -= U16_HEX_BYTES + this.inner.suffix.len(); + } + *this.state = State::Idle; + return Poll::Ready(Ok(buf.len())); + } + } + } + } + + fn poll_flush(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + self.project().inner.poll_flush(cx) + } + + fn poll_close(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + self.project().inner.poll_close(cx) + } +} diff --git a/gix-packetline-blocking/src/write/blocking_io.rs b/gix-packetline-blocking/src/write/blocking_io.rs new file mode 100644 index 00000000000..6a302ea6de7 --- /dev/null +++ b/gix-packetline-blocking/src/write/blocking_io.rs @@ -0,0 +1,73 @@ +// DO NOT EDIT - this is a copy of gix-packetline/src/write/blocking_io.rs. Run `just copy-packetline` to update it. + +use std::io; + +use crate::{MAX_DATA_LEN, U16_HEX_BYTES}; + +/// An implementor of [`Write`][io::Write] which passes all input to an inner `Write` in packet line data encoding, +/// one line per `write(…)` call or as many lines as it takes if the data doesn't fit into the maximum allowed line length. +pub struct Writer { + /// the `Write` implementation to which to propagate packet lines + inner: T, + binary: bool, +} + +impl Writer { + /// Create a new instance from the given `write` + pub fn new(write: T) -> Self { + Writer { + inner: write, + binary: true, + } + } +} + +/// Non-IO methods +impl Writer { + /// If called, each call to [`write()`][io::Write::write()] will write bytes as is. + pub fn enable_binary_mode(&mut self) { + self.binary = true; + } + /// If called, each call to [`write()`][io::Write::write()] will write the input as text, appending a trailing newline + /// if needed before writing. + pub fn enable_text_mode(&mut self) { + self.binary = false; + } + /// Return the inner writer, consuming self. + pub fn into_inner(self) -> T { + self.inner + } + /// Return a mutable reference to the inner writer, useful if packet lines should be serialized directly. + pub fn inner_mut(&mut self) -> &mut T { + &mut self.inner + } +} + +impl io::Write for Writer { + fn write(&mut self, mut buf: &[u8]) -> io::Result { + if buf.is_empty() { + return Err(io::Error::new( + io::ErrorKind::Other, + "empty packet lines are not permitted as '0004' is invalid", + )); + } + + let mut written = 0; + while !buf.is_empty() { + let (data, rest) = buf.split_at(buf.len().min(MAX_DATA_LEN)); + written += if self.binary { + crate::encode::data_to_write(data, &mut self.inner) + } else { + crate::encode::text_to_write(data, &mut self.inner) + }?; + // subtract header (and trailing NL) because write-all can't handle writing more than it passes in + written -= U16_HEX_BYTES + usize::from(!self.binary); + buf = rest; + } + Ok(written) + } + + fn flush(&mut self) -> io::Result<()> { + self.inner.flush() + } +} diff --git a/gix-packetline-blocking/src/write/mod.rs b/gix-packetline-blocking/src/write/mod.rs new file mode 100644 index 00000000000..73585e91f47 --- /dev/null +++ b/gix-packetline-blocking/src/write/mod.rs @@ -0,0 +1,23 @@ +// DO NOT EDIT - this is a copy of gix-packetline/src/write/mod.rs. Run `just copy-packetline` to update it. + +use crate::Writer; + +#[cfg(all(not(feature = "blocking-io"), feature = "async-io"))] +pub(crate) mod async_io; + +#[cfg(feature = "blocking-io")] +pub(crate) mod blocking_io; + +/// Common methods +impl Writer { + /// As [`enable_text_mode()`][Writer::enable_text_mode()], but suitable for chaining. + pub fn text_mode(mut self) -> Self { + self.enable_text_mode(); + self + } + /// As [`enable_binary_mode()`][Writer::enable_binary_mode()], but suitable for chaining. + pub fn binary_mode(mut self) -> Self { + self.enable_binary_mode(); + self + } +} diff --git a/gix-packetline/CHANGELOG.md b/gix-packetline/CHANGELOG.md index 22bd0922b9e..84cc071e871 100644 --- a/gix-packetline/CHANGELOG.md +++ b/gix-packetline/CHANGELOG.md @@ -5,6 +5,62 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## 0.17.5 (2024-03-22) + +A maintenance release without user-facing changes. + +### Commit Statistics + + + + - 2 commits contributed to the release. + - 7 days passed between releases. + - 0 commits were understood as [conventional](https://www.conventionalcommits.org). + - 0 issues like '(#ID)' were seen in commit messages + +### Commit Details + + + +
view details + + * **Uncategorized** + - Prepare changelogs prior to release ([`7018a92`](https://github.com/Byron/gitoxide/commit/7018a928a405ba0534442f0b538d58f520145376)) + - Fix CI ([`a4af4cb`](https://github.com/Byron/gitoxide/commit/a4af4cb8b47c4b1df6e0cff9e8610bc4163742d2)) +
+ +## 0.17.4 (2024-03-14) + + + +### Chore + + - remove repetitive words + +### Commit Statistics + + + + - 6 commits contributed to the release over the course of 4 calendar days. + - 54 days passed between releases. + - 1 commit was understood as [conventional](https://www.conventionalcommits.org). + - 0 issues like '(#ID)' were seen in commit messages + +### Commit Details + + + +
view details + + * **Uncategorized** + - Release gix-date v0.8.5, gix-hash v0.14.2, gix-trace v0.1.8, gix-utils v0.1.11, gix-features v0.38.1, gix-actor v0.31.0, gix-validate v0.8.4, gix-object v0.42.0, gix-path v0.10.7, gix-glob v0.16.2, gix-quote v0.4.12, gix-attributes v0.22.2, gix-command v0.3.6, gix-filter v0.11.0, gix-fs v0.10.1, gix-chunk v0.4.8, gix-commitgraph v0.24.2, gix-hashtable v0.5.2, gix-revwalk v0.13.0, gix-traverse v0.38.0, gix-worktree-stream v0.11.0, gix-archive v0.11.0, gix-config-value v0.14.6, gix-tempfile v13.1.1, gix-lock v13.1.1, gix-ref v0.43.0, gix-sec v0.10.6, gix-config v0.36.0, gix-prompt v0.8.4, gix-url v0.27.2, gix-credentials v0.24.2, gix-ignore v0.11.2, gix-bitmap v0.2.11, gix-index v0.31.0, gix-worktree v0.32.0, gix-diff v0.42.0, gix-discover v0.31.0, gix-pathspec v0.7.1, gix-dir v0.2.0, gix-macros v0.1.4, gix-mailmap v0.23.0, gix-negotiate v0.13.0, gix-pack v0.49.0, gix-odb v0.59.0, gix-packetline v0.17.4, gix-transport v0.41.2, gix-protocol v0.44.2, gix-revision v0.27.0, gix-refspec v0.23.0, gix-status v0.7.0, gix-submodule v0.10.0, gix-worktree-state v0.9.0, gix v0.60.0, safety bump 26 crates ([`b050327`](https://github.com/Byron/gitoxide/commit/b050327e76f234b19be921b78b7b28e034319fdb)) + - Prepare changelogs prior to release ([`52c3bbd`](https://github.com/Byron/gitoxide/commit/52c3bbd36b9e94a0f3a78b4ada84d0c08eba27f6)) + - Merge branch 'status' ([`3e5c974`](https://github.com/Byron/gitoxide/commit/3e5c974dd62ac134711c6c2f5a5490187a6ea55e)) + - Merge pull request #1314 from avoidalone/main ([`5722e3a`](https://github.com/Byron/gitoxide/commit/5722e3aeeba5dd44e38a6cdbb70717a45345307e)) + - Remove repetitive words ([`39879af`](https://github.com/Byron/gitoxide/commit/39879af6eaf2bf4fe159a5c6371c98d516c4febe)) + - Fix lints for nightly, and clippy ([`f8ce3d0`](https://github.com/Byron/gitoxide/commit/f8ce3d0721b6a53713a9392f2451874f520bc44c)) +
+ ## 0.17.3 (2024-01-20) A maintenance release without user-facing changes. @@ -13,7 +69,7 @@ A maintenance release without user-facing changes. - - 2 commits contributed to the release over the course of 4 calendar days. + - 3 commits contributed to the release over the course of 4 calendar days. - 20 days passed between releases. - 0 commits were understood as [conventional](https://www.conventionalcommits.org). - 0 issues like '(#ID)' were seen in commit messages @@ -25,6 +81,7 @@ A maintenance release without user-facing changes.
view details * **Uncategorized** + - Release gix-utils v0.1.9, gix-features v0.38.0, gix-actor v0.30.0, gix-object v0.41.0, gix-path v0.10.4, gix-glob v0.16.0, gix-attributes v0.22.0, gix-command v0.3.3, gix-packetline-blocking v0.17.3, gix-filter v0.9.0, gix-fs v0.10.0, gix-commitgraph v0.24.0, gix-revwalk v0.12.0, gix-traverse v0.37.0, gix-worktree-stream v0.9.0, gix-archive v0.9.0, gix-config-value v0.14.4, gix-tempfile v13.0.0, gix-lock v13.0.0, gix-ref v0.41.0, gix-sec v0.10.4, gix-config v0.34.0, gix-url v0.27.0, gix-credentials v0.24.0, gix-ignore v0.11.0, gix-index v0.29.0, gix-worktree v0.30.0, gix-diff v0.40.0, gix-discover v0.29.0, gix-mailmap v0.22.0, gix-negotiate v0.12.0, gix-pack v0.47.0, gix-odb v0.57.0, gix-pathspec v0.6.0, gix-packetline v0.17.3, gix-transport v0.41.0, gix-protocol v0.44.0, gix-revision v0.26.0, gix-refspec v0.22.0, gix-status v0.5.0, gix-submodule v0.8.0, gix-worktree-state v0.7.0, gix v0.58.0, safety bump 39 crates ([`eb6aa8f`](https://github.com/Byron/gitoxide/commit/eb6aa8f502314f886fc4ea3d52ab220763968208)) - Prepare changelogs prior to release ([`6a2e0be`](https://github.com/Byron/gitoxide/commit/6a2e0bebfdf012dc2ed0ff2604086081f2a0f96d)) - Release gix-trace v0.1.7, gix-features v0.37.2, gix-commitgraph v0.23.2, gix-traverse v0.36.2, gix-index v0.28.2 ([`b6c04c8`](https://github.com/Byron/gitoxide/commit/b6c04c87b426bf36a059df8dc52b56d384b27b79))
diff --git a/gix-packetline/Cargo.toml b/gix-packetline/Cargo.toml index a8a7ee7275f..6943662fb8c 100644 --- a/gix-packetline/Cargo.toml +++ b/gix-packetline/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "gix-packetline" -version = "0.17.3" +version = "0.17.5" repository = "https://github.com/Byron/gitoxide" license = "MIT OR Apache-2.0" description = "A crate of the gitoxide project implementing the pkt-line serialization format" @@ -39,7 +39,7 @@ path = "tests/blocking-packetline.rs" required-features = ["blocking-io", "maybe-async/is_sync"] [dependencies] -gix-trace = { version = "^0.1.7", path = "../gix-trace" } +gix-trace = { version = "^0.1.8", path = "../gix-trace" } serde = { version = "1.0.114", optional = true, default-features = false, features = ["std", "derive"]} thiserror = "1.0.34" diff --git a/gix-packetline/src/decode.rs b/gix-packetline/src/decode.rs index 35d2b5149ac..77dad4dd7b2 100644 --- a/gix-packetline/src/decode.rs +++ b/gix-packetline/src/decode.rs @@ -8,7 +8,7 @@ use crate::{PacketLineRef, DELIMITER_LINE, FLUSH_LINE, MAX_DATA_LEN, MAX_LINE_LE pub enum Error { #[error("Failed to decode the first four hex bytes indicating the line length: {err}")] HexDecode { err: String }, - #[error("The data received claims to be larger than than the maximum allowed size: got {length_in_bytes}, exceeds {MAX_DATA_LEN}")] + #[error("The data received claims to be larger than the maximum allowed size: got {length_in_bytes}, exceeds {MAX_DATA_LEN}")] DataLengthLimitExceeded { length_in_bytes: usize }, #[error("Received an invalid empty line")] DataIsEmpty, @@ -21,6 +21,7 @@ pub enum Error { } /// +#[allow(clippy::empty_docs)] pub mod band { /// The error used in [`PacketLineRef::decode_band()`][super::PacketLineRef::decode_band()]. #[derive(Debug, thiserror::Error)] diff --git a/gix-packetline/src/lib.rs b/gix-packetline/src/lib.rs index 95ecd59d25c..62978dd7bb1 100644 --- a/gix-packetline/src/lib.rs +++ b/gix-packetline/src/lib.rs @@ -31,9 +31,11 @@ pub enum Channel { mod line; /// +#[allow(clippy::empty_docs)] pub mod read; /// +#[allow(clippy::empty_docs)] #[cfg(any(feature = "async-io", feature = "blocking-io"))] mod write; #[cfg(all(not(feature = "blocking-io"), feature = "async-io"))] diff --git a/gix-packetline/src/read/sidebands/async_io.rs b/gix-packetline/src/read/sidebands/async_io.rs index 6c53e922c1f..e93b0b93592 100644 --- a/gix-packetline/src/read/sidebands/async_io.rs +++ b/gix-packetline/src/read/sidebands/async_io.rs @@ -354,11 +354,9 @@ where F: FnMut(bool, &[u8]) -> ProgressAction + Unpin, { fn poll_read(mut self: Pin<&mut Self>, cx: &mut Context<'_>, buf: &mut [u8]) -> Poll> { - let nread = { - use std::io::Read; - let mut rem = ready!(self.as_mut().poll_fill_buf(cx))?; - rem.read(buf)? - }; + use std::io::Read; + let mut rem = ready!(self.as_mut().poll_fill_buf(cx))?; + let nread = rem.read(buf)?; self.consume(nread); Poll::Ready(Ok(nread)) } diff --git a/gix-packetline/src/read/sidebands/blocking_io.rs b/gix-packetline/src/read/sidebands/blocking_io.rs index f5c87aeb832..dec23a9baa7 100644 --- a/gix-packetline/src/read/sidebands/blocking_io.rs +++ b/gix-packetline/src/read/sidebands/blocking_io.rs @@ -208,10 +208,8 @@ where F: FnMut(bool, &[u8]) -> ProgressAction, { fn read(&mut self, buf: &mut [u8]) -> io::Result { - let nread = { - let mut rem = self.fill_buf()?; - rem.read(buf)? - }; + let mut rem = self.fill_buf()?; + let nread = rem.read(buf)?; self.consume(nread); Ok(nread) } diff --git a/gix-packetline/tests/decode/mod.rs b/gix-packetline/tests/decode/mod.rs index 64d6d9cdde9..ec15d7a638a 100644 --- a/gix-packetline/tests/decode/mod.rs +++ b/gix-packetline/tests/decode/mod.rs @@ -103,7 +103,7 @@ mod streaming { fn error_on_oversized_line() { assert_err_display( streaming(b"ffff"), - "The data received claims to be larger than than the maximum allowed size: got 65535, exceeds 65516", + "The data received claims to be larger than the maximum allowed size: got 65535, exceeds 65516", ); } diff --git a/gix-path/CHANGELOG.md b/gix-path/CHANGELOG.md index c92bec17d28..b5217939fe8 100644 --- a/gix-path/CHANGELOG.md +++ b/gix-path/CHANGELOG.md @@ -5,6 +5,31 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## 0.10.7 (2024-03-14) + +A maintenance release without user-facing changes. + +### Commit Statistics + + + + - 3 commits contributed to the release over the course of 4 calendar days. + - 18 days passed between releases. + - 0 commits were understood as [conventional](https://www.conventionalcommits.org). + - 0 issues like '(#ID)' were seen in commit messages + +### Commit Details + + + +
view details + + * **Uncategorized** + - Prepare changelogs prior to release ([`52c3bbd`](https://github.com/Byron/gitoxide/commit/52c3bbd36b9e94a0f3a78b4ada84d0c08eba27f6)) + - Merge branch 'status' ([`3e5c974`](https://github.com/Byron/gitoxide/commit/3e5c974dd62ac134711c6c2f5a5490187a6ea55e)) + - Fix lints for nightly, and clippy ([`f8ce3d0`](https://github.com/Byron/gitoxide/commit/f8ce3d0721b6a53713a9392f2451874f520bc44c)) +
+ ## 0.10.6 (2024-02-25) ### New Features @@ -17,7 +42,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - - 5 commits contributed to the release over the course of 16 calendar days. + - 6 commits contributed to the release over the course of 16 calendar days. - 30 days passed between releases. - 2 commits were understood as [conventional](https://www.conventionalcommits.org). - 0 issues like '(#ID)' were seen in commit messages @@ -29,6 +54,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
view details * **Uncategorized** + - Release gix-date v0.8.4, gix-utils v0.1.10, gix-actor v0.30.1, gix-object v0.41.1, gix-path v0.10.6, gix-glob v0.16.1, gix-quote v0.4.11, gix-attributes v0.22.1, gix-command v0.3.5, gix-filter v0.10.0, gix-commitgraph v0.24.1, gix-worktree-stream v0.10.0, gix-archive v0.10.0, gix-config-value v0.14.5, gix-ref v0.42.0, gix-sec v0.10.5, gix-config v0.35.0, gix-prompt v0.8.3, gix-url v0.27.1, gix-credentials v0.24.1, gix-ignore v0.11.1, gix-index v0.30.0, gix-worktree v0.31.0, gix-diff v0.41.0, gix-discover v0.30.0, gix-pathspec v0.7.0, gix-dir v0.1.0, gix-pack v0.48.0, gix-odb v0.58.0, gix-transport v0.41.1, gix-protocol v0.44.1, gix-revision v0.26.1, gix-refspec v0.22.1, gix-status v0.6.0, gix-submodule v0.9.0, gix-worktree-state v0.8.0, gix v0.59.0, gix-fsck v0.3.0, gitoxide-core v0.36.0, gitoxide v0.34.0, safety bump 10 crates ([`45b4470`](https://github.com/Byron/gitoxide/commit/45b447045bc826f252129c300c531acde2652c64)) - Prepare changelogs prior to release ([`f2e111f`](https://github.com/Byron/gitoxide/commit/f2e111f768fc1bc6182355261c20b63610cffec7)) - Merge branch 'status' ([`bb48c4c`](https://github.com/Byron/gitoxide/commit/bb48c4ce22650b8c76af3b147e252ebe7cedb205)) - Add `relativize_with_prefix()`. ([`9ba8bca`](https://github.com/Byron/gitoxide/commit/9ba8bcab07f4b3d0048b5a125b632bd70fe788d9)) @@ -111,12 +137,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Chore - - change `rust-version` manifest field back to 1.65. - They didn't actually need to be higher to work, and changing them - unecessarily can break downstream CI. - - Let's keep this value as low as possible, and only increase it when - more recent features are actually used. +- change `rust-version` manifest field back to 1.65. + They didn't actually need to be higher to work, and changing them + unecessarily can break downstream CI. + + Let's keep this value as low as possible, and only increase it when + more recent features are actually used. ### Commit Statistics @@ -145,8 +171,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Chore - - upgrade MSRV to v1.70 - Our MSRV follows the one of `helix`, which in turn follows Firefox. +- upgrade MSRV to v1.70 + Our MSRV follows the one of `helix`, which in turn follows Firefox. ### Commit Statistics @@ -240,11 +266,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Chore - - don't call crate 'WIP' in manifest anymore. +- don't call crate 'WIP' in manifest anymore. ### Other - - make clear that `normalize()` does not touch duplicate path separators nor single `.`. +- make clear that `normalize()` does not touch duplicate path separators nor single `.`. ### New Features (BREAKING) @@ -332,7 +358,7 @@ A maintenance release without user-facing changes. ### Chore - - Add `clippy::redundant-closure-for-method-calls` lint +- Add `clippy::redundant-closure-for-method-calls` lint ### New Features @@ -549,15 +575,15 @@ A maintenance release without user-facing changes. ### Refactor (BREAKING) - - Rename absolutize() to normalize() - The name absolutize implies strongly that the returned path will be - absolute, but the function only converts relative paths to absolute under a - few specific circumstances. - - The new name, normalize(), is inspired by Python's os.path.normpath(), - Java's java.nio.file.Path.normalize(), Node's Path.normalize(), and maybe - some others which have similar semantics to this function. - - various name changes for more convenient API +- Rename absolutize() to normalize() + The name absolutize implies strongly that the returned path will be + absolute, but the function only converts relative paths to absolute under a + few specific circumstances. + + The new name, normalize(), is inspired by Python's os.path.normpath(), + Java's java.nio.file.Path.normalize(), Node's Path.normalize(), and maybe + some others which have similar semantics to this function. +- various name changes for more convenient API ### Bug Fixes (BREAKING) @@ -593,20 +619,20 @@ A maintenance release without user-facing changes. ### Refactor - - rename tests/convert/normalize.rs - This renames the test module to match the new function name. +- rename tests/convert/normalize.rs + This renames the test module to match the new function name. ### Other - - :discover()` now returns the shortest path. - If and only if it canonicalized the source path. That way, users will - still get a familiar path. This is due to `parent()` not operating - in the file system, which otherwise would be equivalent to `..`, - but that's not how we work. - - Maybe we should overhaul the way this works to use `../` instead - and just 'absoluteize' the path later (std::path::absolute()) is - on the way for that. +- :discover()` now returns the shortest path. + If and only if it canonicalized the source path. That way, users will + still get a familiar path. This is due to `parent()` not operating + in the file system, which otherwise would be equivalent to `..`, + but that's not how we work. + + Maybe we should overhaul the way this works to use `../` instead + and just 'absoluteize' the path later (std::path::absolute()) is + on the way for that. ### Bug Fixes @@ -643,8 +669,8 @@ A maintenance release without user-facing changes. ### Chore - - uniformize deny attributes - - remove default link to cargo doc everywhere +- uniformize deny attributes +- remove default link to cargo doc everywhere ### Documentation @@ -891,19 +917,19 @@ A maintenance release without user-facing changes. ### Refactor - - rename tests/convert/normalize.rs - This renames the test module to match the new function name. +- rename tests/convert/normalize.rs + This renames the test module to match the new function name. ### Refactor (BREAKING) - - Rename absolutize() to normalize() - The name absolutize implies strongly that the returned path will be - absolute, but the function only converts relative paths to absolute under a - few specific circumstances. - - The new name, normalize(), is inspired by Python's os.path.normpath(), - Java's java.nio.file.Path.normalize(), Node's Path.normalize(), and maybe - some others which have similar semantics to this function. +- Rename absolutize() to normalize() + The name absolutize implies strongly that the returned path will be + absolute, but the function only converts relative paths to absolute under a + few specific circumstances. + + The new name, normalize(), is inspired by Python's os.path.normpath(), + Java's java.nio.file.Path.normalize(), Node's Path.normalize(), and maybe + some others which have similar semantics to this function. ## 0.6.0 (2022-11-21) @@ -959,8 +985,8 @@ A maintenance release without user-facing changes. ### Chore - - uniformize deny attributes - - remove default link to cargo doc everywhere +- uniformize deny attributes +- remove default link to cargo doc everywhere ### Bug Fixes @@ -1011,15 +1037,15 @@ A maintenance release without user-facing changes. ### Other - - :discover()` now returns the shortest path. - If and only if it canonicalized the source path. That way, users will - still get a familiar path. This is due to `parent()` not operating - in the file system, which otherwise would be equivalent to `..`, - but that's not how we work. - - Maybe we should overhaul the way this works to use `../` instead - and just 'absolutize' the path later (std::path::absolute()) is - on the way for that. +- :discover()` now returns the shortest path. + If and only if it canonicalized the source path. That way, users will + still get a familiar path. This is due to `parent()` not operating + in the file system, which otherwise would be equivalent to `..`, + but that's not how we work. + + Maybe we should overhaul the way this works to use `../` instead + and just 'absolutize' the path later (std::path::absolute()) is + on the way for that. ## 0.1.0 (2022-04-28) @@ -1027,7 +1053,7 @@ A maintenance release without user-facing changes. ### Refactor (BREAKING) - - various name changes for more convenient API +- various name changes for more convenient API ## 0.0.0 (2022-03-31) diff --git a/gix-path/Cargo.toml b/gix-path/Cargo.toml index abdadf3aac0..2089bd17076 100644 --- a/gix-path/Cargo.toml +++ b/gix-path/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "gix-path" -version = "0.10.6" +version = "0.10.7" repository = "https://github.com/Byron/gitoxide" license = "MIT OR Apache-2.0" description = "A crate of the gitoxide project dealing paths and their conversions" @@ -13,7 +13,7 @@ rust-version = "1.65" doctest = false [dependencies] -gix-trace = { version = "^0.1.7", path = "../gix-trace" } +gix-trace = { version = "^0.1.8", path = "../gix-trace" } bstr = { version = "1.3.0", default-features = false, features = ["std"] } thiserror = "1.0.26" once_cell = "1.17.1" diff --git a/gix-path/src/lib.rs b/gix-path/src/lib.rs index 888141d3c23..7f913042fac 100644 --- a/gix-path/src/lib.rs +++ b/gix-path/src/lib.rs @@ -57,6 +57,7 @@ mod util; pub use util::is_absolute; /// +#[allow(clippy::empty_docs)] pub mod realpath; pub use realpath::function::{realpath, realpath_opts}; diff --git a/gix-pathspec/CHANGELOG.md b/gix-pathspec/CHANGELOG.md index ecf96a2a5d4..935eff052d1 100644 --- a/gix-pathspec/CHANGELOG.md +++ b/gix-pathspec/CHANGELOG.md @@ -5,6 +5,144 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## 0.7.5 (2024-05-22) + +### Bug Fixes + + - assure empty paths are always matching a `Search` + This improvement was triggered by [this question](https://github.com/rust-lang/cargo/pull/13777#pullrequestreview-2011470503). + +### Commit Statistics + + + + - 6 commits contributed to the release over the course of 33 calendar days. + - 33 days passed between releases. + - 1 commit was understood as [conventional](https://www.conventionalcommits.org). + - 0 issues like '(#ID)' were seen in commit messages + +### Commit Details + + + +
view details + + * **Uncategorized** + - Adjust changelogs prior to release ([`9511416`](https://github.com/Byron/gitoxide/commit/9511416a6cd0c571233f958c165329c8705c2498)) + - Merge branch 'various-fixes' ([`d6cd449`](https://github.com/Byron/gitoxide/commit/d6cd44930fb204b06e2b70fc6965e7705530c47a)) + - Update dependencies ([`cd4de83`](https://github.com/Byron/gitoxide/commit/cd4de8327fc195eb862ab6e138f2315a87374f85)) + - Merge branch 'status' ([`68fd5b3`](https://github.com/Byron/gitoxide/commit/68fd5b34e1214d5c2cc7d00dd06e19ee86c00c66)) + - Assure empty paths are always matching a `Search` ([`5b47567`](https://github.com/Byron/gitoxide/commit/5b47567bff0b46cb1b6e933bf7ebdb75909bb2ba)) + - Merge branch 'cargo-fixes' ([`977346e`](https://github.com/Byron/gitoxide/commit/977346ee61de6207c66f3de003db6e8c722fb81c)) +
+ +## 0.7.4 (2024-04-18) + +A maintenance release without user-facing changes. + +### Commit Statistics + + + + - 4 commits contributed to the release over the course of 4 calendar days. + - 5 days passed between releases. + - 0 commits were understood as [conventional](https://www.conventionalcommits.org). + - 0 issues like '(#ID)' were seen in commit messages + +### Commit Details + + + +
view details + + * **Uncategorized** + - Release gix-index v0.32.1, gix-pathspec v0.7.4, gix-worktree v0.33.1, gix-dir v0.4.1 ([`54ac559`](https://github.com/Byron/gitoxide/commit/54ac55946bb04635cd74582a1ce2e4bee70f2e60)) + - Prepare changelog prior to `gix-dir` patch release ([`6ca6fa6`](https://github.com/Byron/gitoxide/commit/6ca6fa69b5c21c8d8e9e07e21558e98201504cda)) + - Merge pull request #1345 from EliahKagan/shell-scripts ([`fe24c89`](https://github.com/Byron/gitoxide/commit/fe24c89e326670deaa3aaa643276d612d866072e)) + - Add missing +x bit on scripts that are run and not sourced ([`41bf65a`](https://github.com/Byron/gitoxide/commit/41bf65adef6f7d2cdd28fede262173ec7ba10822)) +
+ +## 0.7.3 (2024-04-13) + +A maintenance release without user-facing changes. + +### Commit Statistics + + + + - 4 commits contributed to the release over the course of 2 calendar days. + - 26 days passed between releases. + - 0 commits were understood as [conventional](https://www.conventionalcommits.org). + - 0 issues like '(#ID)' were seen in commit messages + +### Commit Details + + + +
view details + + * **Uncategorized** + - Release gix-trace v0.1.9, gix-utils v0.1.12, gix-packetline-blocking v0.17.4, gix-filter v0.11.1, gix-fs v0.10.2, gix-traverse v0.39.0, gix-worktree-stream v0.12.0, gix-archive v0.12.0, gix-config v0.36.1, gix-url v0.27.3, gix-index v0.32.0, gix-worktree v0.33.0, gix-diff v0.43.0, gix-pathspec v0.7.3, gix-dir v0.4.0, gix-pack v0.50.0, gix-odb v0.60.0, gix-transport v0.42.0, gix-protocol v0.45.0, gix-status v0.9.0, gix-worktree-state v0.10.0, gix v0.62.0, gix-fsck v0.4.0, gitoxide-core v0.37.0, gitoxide v0.35.0, safety bump 14 crates ([`095c673`](https://github.com/Byron/gitoxide/commit/095c6739b2722a8b9af90776b435ef2da454c0e6)) + - Prepare changelogs prior to release ([`5755271`](https://github.com/Byron/gitoxide/commit/57552717f46f96c35ba4ddc0a64434354ef845e9)) + - Merge pull request #1341 from szepeviktor/typos ([`55f379b`](https://github.com/Byron/gitoxide/commit/55f379bc47065822d078393d83d30c0835a89782)) + - Fix typos ([`f72ecce`](https://github.com/Byron/gitoxide/commit/f72ecce45babcad2a0c9b73c79d01ff502907a57)) +
+ +## 0.7.2 (2024-03-18) + +### Bug Fixes + + - `Search::can_match_relative_path()` won't report false-negatives anymore. + It does this by ignoring exclude patterns, preferring false-positives that way. + +### Commit Statistics + + + + - 4 commits contributed to the release over the course of 1 calendar day. + - 3 days passed between releases. + - 1 commit was understood as [conventional](https://www.conventionalcommits.org). + - 0 issues like '(#ID)' were seen in commit messages + +### Commit Details + + + +
view details + + * **Uncategorized** + - Release gix-actor v0.31.1, gix-object v0.42.1, gix-index v0.31.1, gix-pathspec v0.7.2, gix-dir v0.3.0, gix-status v0.8.0, gix v0.61.0, safety bump 2 crates ([`155cc45`](https://github.com/Byron/gitoxide/commit/155cc45730b259e662d7c4be42a469a3af3750e1)) + - Prepare changelog prior to release ([`129ba3d`](https://github.com/Byron/gitoxide/commit/129ba3deccc9ada0dc571466458845939502763d)) + - Merge branch 'improvements-for-cargo' ([`41cd53e`](https://github.com/Byron/gitoxide/commit/41cd53e2af76e35e047aac4eca6324774df4cb50)) + - `Search::can_match_relative_path()` won't report false-negatives anymore. ([`0386871`](https://github.com/Byron/gitoxide/commit/038687127ac6eecdd229fe054df3c050612837cd)) +
+ +## 0.7.1 (2024-03-14) + +A maintenance release without user-facing changes. + +### Commit Statistics + + + + - 4 commits contributed to the release over the course of 4 calendar days. + - 18 days passed between releases. + - 0 commits were understood as [conventional](https://www.conventionalcommits.org). + - 0 issues like '(#ID)' were seen in commit messages + +### Commit Details + + + +
view details + + * **Uncategorized** + - Release gix-date v0.8.5, gix-hash v0.14.2, gix-trace v0.1.8, gix-utils v0.1.11, gix-features v0.38.1, gix-actor v0.31.0, gix-validate v0.8.4, gix-object v0.42.0, gix-path v0.10.7, gix-glob v0.16.2, gix-quote v0.4.12, gix-attributes v0.22.2, gix-command v0.3.6, gix-filter v0.11.0, gix-fs v0.10.1, gix-chunk v0.4.8, gix-commitgraph v0.24.2, gix-hashtable v0.5.2, gix-revwalk v0.13.0, gix-traverse v0.38.0, gix-worktree-stream v0.11.0, gix-archive v0.11.0, gix-config-value v0.14.6, gix-tempfile v13.1.1, gix-lock v13.1.1, gix-ref v0.43.0, gix-sec v0.10.6, gix-config v0.36.0, gix-prompt v0.8.4, gix-url v0.27.2, gix-credentials v0.24.2, gix-ignore v0.11.2, gix-bitmap v0.2.11, gix-index v0.31.0, gix-worktree v0.32.0, gix-diff v0.42.0, gix-discover v0.31.0, gix-pathspec v0.7.1, gix-dir v0.2.0, gix-macros v0.1.4, gix-mailmap v0.23.0, gix-negotiate v0.13.0, gix-pack v0.49.0, gix-odb v0.59.0, gix-packetline v0.17.4, gix-transport v0.41.2, gix-protocol v0.44.2, gix-revision v0.27.0, gix-refspec v0.23.0, gix-status v0.7.0, gix-submodule v0.10.0, gix-worktree-state v0.9.0, gix v0.60.0, safety bump 26 crates ([`b050327`](https://github.com/Byron/gitoxide/commit/b050327e76f234b19be921b78b7b28e034319fdb)) + - Prepare changelogs prior to release ([`52c3bbd`](https://github.com/Byron/gitoxide/commit/52c3bbd36b9e94a0f3a78b4ada84d0c08eba27f6)) + - Merge branch 'status' ([`3e5c974`](https://github.com/Byron/gitoxide/commit/3e5c974dd62ac134711c6c2f5a5490187a6ea55e)) + - Fix lints for nightly, and clippy ([`f8ce3d0`](https://github.com/Byron/gitoxide/commit/f8ce3d0721b6a53713a9392f2451874f520bc44c)) +
+ ## 0.7.0 (2024-02-25) ### New Features @@ -41,7 +179,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - - 12 commits contributed to the release over the course of 30 calendar days. + - 13 commits contributed to the release over the course of 30 calendar days. - 36 days passed between releases. - 7 commits were understood as [conventional](https://www.conventionalcommits.org). - 0 issues like '(#ID)' were seen in commit messages @@ -53,6 +191,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
view details * **Uncategorized** + - Release gix-date v0.8.4, gix-utils v0.1.10, gix-actor v0.30.1, gix-object v0.41.1, gix-path v0.10.6, gix-glob v0.16.1, gix-quote v0.4.11, gix-attributes v0.22.1, gix-command v0.3.5, gix-filter v0.10.0, gix-commitgraph v0.24.1, gix-worktree-stream v0.10.0, gix-archive v0.10.0, gix-config-value v0.14.5, gix-ref v0.42.0, gix-sec v0.10.5, gix-config v0.35.0, gix-prompt v0.8.3, gix-url v0.27.1, gix-credentials v0.24.1, gix-ignore v0.11.1, gix-index v0.30.0, gix-worktree v0.31.0, gix-diff v0.41.0, gix-discover v0.30.0, gix-pathspec v0.7.0, gix-dir v0.1.0, gix-pack v0.48.0, gix-odb v0.58.0, gix-transport v0.41.1, gix-protocol v0.44.1, gix-revision v0.26.1, gix-refspec v0.22.1, gix-status v0.6.0, gix-submodule v0.9.0, gix-worktree-state v0.8.0, gix v0.59.0, gix-fsck v0.3.0, gitoxide-core v0.36.0, gitoxide v0.34.0, safety bump 10 crates ([`45b4470`](https://github.com/Byron/gitoxide/commit/45b447045bc826f252129c300c531acde2652c64)) - Prepare changelogs prior to release ([`f2e111f`](https://github.com/Byron/gitoxide/commit/f2e111f768fc1bc6182355261c20b63610cffec7)) - Merge branch 'status' ([`bb48c4c`](https://github.com/Byron/gitoxide/commit/bb48c4ce22650b8c76af3b147e252ebe7cedb205)) - Assure that `..` patterns don't end up being `.` that matches nothing ([`5f21e24`](https://github.com/Byron/gitoxide/commit/5f21e24a3d8c98e726d822d2961978d729e66e43)) @@ -101,12 +240,12 @@ A maintenance release without user-facing changes. ### Chore - - change `rust-version` manifest field back to 1.65. - They didn't actually need to be higher to work, and changing them - unecessarily can break downstream CI. - - Let's keep this value as low as possible, and only increase it when - more recent features are actually used. +- change `rust-version` manifest field back to 1.65. + They didn't actually need to be higher to work, and changing them + unecessarily can break downstream CI. + + Let's keep this value as low as possible, and only increase it when + more recent features are actually used. ### Commit Statistics @@ -134,8 +273,8 @@ A maintenance release without user-facing changes. ### Chore - - upgrade MSRV to v1.70 - Our MSRV follows the one of `helix`, which in turn follows Firefox. +- upgrade MSRV to v1.70 + Our MSRV follows the one of `helix`, which in turn follows Firefox. ### Commit Statistics @@ -231,7 +370,7 @@ A maintenance release without user-facing changes. ### Other - - improve documentation of `common_prefix()` and `pattern_matching_relative_path()`. +- improve documentation of `common_prefix()` and `pattern_matching_relative_path()`. ### Commit Statistics @@ -294,8 +433,8 @@ A maintenance release without user-facing changes. ### Chore - - uniformize deny attributes - - remove default link to cargo doc everywhere +- uniformize deny attributes +- remove default link to cargo doc everywhere ### New Features @@ -307,8 +446,8 @@ A maintenance release without user-facing changes. ### Chore - - don't call crate 'WIP' in manifest anymore. - - Add `clippy::redundant-closure-for-method-calls` lint +- don't call crate 'WIP' in manifest anymore. +- Add `clippy::redundant-closure-for-method-calls` lint ### Bug Fixes diff --git a/gix-pathspec/Cargo.toml b/gix-pathspec/Cargo.toml index 6535617c934..bad38a76a58 100644 --- a/gix-pathspec/Cargo.toml +++ b/gix-pathspec/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "gix-pathspec" -version = "0.7.0" +version = "0.7.5" repository = "https://github.com/Byron/gitoxide" license = "MIT OR Apache-2.0" description = "A crate of the gitoxide project dealing magical pathspecs" @@ -13,16 +13,16 @@ include = ["src/**/*", "LICENSE-*", "README.md"] doctest = false [dependencies] -gix-glob = { version = "^0.16.1", path = "../gix-glob" } -gix-path = { version = "^0.10.6", path = "../gix-path" } -gix-attributes = { version = "^0.22.1", path = "../gix-attributes" } -gix-config-value = { version = "^0.14.5", path = "../gix-config-value" } +gix-glob = { version = "^0.16.3", path = "../gix-glob" } +gix-path = { version = "^0.10.7", path = "../gix-path" } +gix-attributes = { version = "^0.22.2", path = "../gix-attributes" } +gix-config-value = { version = "^0.14.6", path = "../gix-config-value" } -bstr = { version = "1.3.0", default-features = false, features = ["std"]} +bstr = { version = "1.3.0", default-features = false, features = ["std"] } bitflags = "2" thiserror = "1.0.26" [dev-dependencies] gix-testtools = { path = "../tests/tools" } once_cell = "1.12.0" -serial_test = "2.0.0" +serial_test = "3.1.1" diff --git a/gix-pathspec/src/defaults.rs b/gix-pathspec/src/defaults.rs index 81be53b38c1..c2bc641f8eb 100644 --- a/gix-pathspec/src/defaults.rs +++ b/gix-pathspec/src/defaults.rs @@ -3,6 +3,7 @@ use std::ffi::OsString; use crate::{Defaults, MagicSignature, SearchMode}; /// +#[allow(clippy::empty_docs)] pub mod from_environment { /// The error returned by [Defaults::from_environment()](super::Defaults::from_environment()). #[derive(Debug, thiserror::Error)] diff --git a/gix-pathspec/src/lib.rs b/gix-pathspec/src/lib.rs index e271cdb06f9..e31d5d50ba1 100644 --- a/gix-pathspec/src/lib.rs +++ b/gix-pathspec/src/lib.rs @@ -11,6 +11,7 @@ use bstr::BString; pub use gix_attributes as attributes; /// +#[allow(clippy::empty_docs)] pub mod normalize { use std::path::PathBuf; @@ -28,9 +29,11 @@ pub mod normalize { mod pattern; /// +#[allow(clippy::empty_docs)] pub mod search; /// +#[allow(clippy::empty_docs)] pub mod parse; /// Default settings for some fields of a [`Pattern`]. @@ -51,6 +54,7 @@ pub struct Defaults { } /// +#[allow(clippy::empty_docs)] pub mod defaults; /// A lists of pathspec patterns, possibly from a file. diff --git a/gix-pathspec/src/search/matching.rs b/gix-pathspec/src/search/matching.rs index 37a14a34b1a..69f62a91c6c 100644 --- a/gix-pathspec/src/search/matching.rs +++ b/gix-pathspec/src/search/matching.rs @@ -20,7 +20,7 @@ impl Search { /// /// ### Deviation /// - /// The case-sensivity of the attribute match is controlled by the sensitivity of the pathspec, instead of being based on the + /// The case-sensitivity of the attribute match is controlled by the sensitivity of the pathspec, instead of being based on the /// case folding settings of the repository. That way we assure that the matching is consistent. /// Higher-level crates should control this default case folding of pathspecs when instantiating them, which is when they can /// set it to match the repository setting for more natural behaviour when, for instance, adding files to a repository: @@ -31,6 +31,21 @@ impl Search { is_dir: Option, attributes: &mut dyn FnMut(&BStr, Case, bool, &mut gix_attributes::search::Outcome) -> bool, ) -> Option> { + static MATCH_ALL_STAND_IN: Pattern = Pattern { + path: BString::new(Vec::new()), + signature: MagicSignature::empty(), + search_mode: SearchMode::ShellGlob, + attributes: Vec::new(), + prefix_len: 0, + nil: true, + }; + if relative_path.is_empty() { + return Some(Match { + pattern: &MATCH_ALL_STAND_IN, + sequence_number: 0, + kind: Always, + }); + } let basename_not_important = None; if relative_path .get(..self.common_prefix_len) @@ -106,14 +121,6 @@ impl Search { }); if res.is_none() && self.all_patterns_are_excluded { - static MATCH_ALL_STAND_IN: Pattern = Pattern { - path: BString::new(Vec::new()), - signature: MagicSignature::empty(), - search_mode: SearchMode::ShellGlob, - attributes: Vec::new(), - prefix_len: 0, - nil: true, - }; Some(Match { pattern: &MATCH_ALL_STAND_IN, sequence_number: patterns_len, @@ -133,7 +140,7 @@ impl Search { /// is ignored. /// Returns `false` if this pathspec has no chance of ever matching `relative_path`. pub fn can_match_relative_path(&self, relative_path: &BStr, is_dir: Option) -> bool { - if self.patterns.is_empty() { + if self.patterns.is_empty() || relative_path.is_empty() { return true; } let common_prefix_len = self.common_prefix_len.min(relative_path.len()); @@ -176,11 +183,11 @@ impl Search { matches!(pattern.path.get(common_len), None | Some(&b'/')) } else { relative_path.get(common_len) == Some(&b'/') - } + }; } } } - if is_match { + if is_match && (!pattern.is_excluded() || pattern.always_matches()) { return !pattern.is_excluded(); } } @@ -191,10 +198,10 @@ impl Search { /// Returns `true` if `relative_path` matches the prefix of this pathspec. /// /// For example, the relative path `d` matches `d/`, `d*/`, `d/` and `d/*`, but not `d/d/*` or `dir`. - /// When `leading` is `true`, then `d` matches `d/d` as well. Thus `relative_path` must may be + /// When `leading` is `true`, then `d` matches `d/d` as well. Thus, `relative_path` must may be /// partially included in `pathspec`, otherwise it has to be fully included. pub fn directory_matches_prefix(&self, relative_path: &BStr, leading: bool) -> bool { - if self.patterns.is_empty() { + if self.patterns.is_empty() || relative_path.is_empty() { return true; } let common_prefix_len = self.common_prefix_len.min(relative_path.len()); @@ -233,7 +240,7 @@ impl Search { }; } } - if is_match { + if is_match && (!pattern.is_excluded() || pattern.always_matches()) { return !pattern.is_excluded(); } } diff --git a/gix-pathspec/tests/fixtures/generated-archives/match_baseline_dirs.tar.xz b/gix-pathspec/tests/fixtures/generated-archives/match_baseline_dirs.tar.xz index 158b91b1116..702a31f3315 100644 Binary files a/gix-pathspec/tests/fixtures/generated-archives/match_baseline_dirs.tar.xz and b/gix-pathspec/tests/fixtures/generated-archives/match_baseline_dirs.tar.xz differ diff --git a/gix-pathspec/tests/fixtures/generated-archives/match_baseline_files.tar.xz b/gix-pathspec/tests/fixtures/generated-archives/match_baseline_files.tar.xz index 3cd83f3fc03..2f94c5126f3 100644 Binary files a/gix-pathspec/tests/fixtures/generated-archives/match_baseline_files.tar.xz and b/gix-pathspec/tests/fixtures/generated-archives/match_baseline_files.tar.xz differ diff --git a/gix-pathspec/tests/fixtures/generated-archives/parse_baseline.tar.xz b/gix-pathspec/tests/fixtures/generated-archives/parse_baseline.tar.xz index 1b2ffb32c8a..5825e1233cf 100644 Binary files a/gix-pathspec/tests/fixtures/generated-archives/parse_baseline.tar.xz and b/gix-pathspec/tests/fixtures/generated-archives/parse_baseline.tar.xz differ diff --git a/gix-pathspec/tests/fixtures/match_baseline_dirs.sh b/gix-pathspec/tests/fixtures/match_baseline_dirs.sh old mode 100644 new mode 100755 index 37af54e36e7..34a4e925e82 --- a/gix-pathspec/tests/fixtures/match_baseline_dirs.sh +++ b/gix-pathspec/tests/fixtures/match_baseline_dirs.sh @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash set -eu -o pipefail git init; diff --git a/gix-pathspec/tests/fixtures/match_baseline_files.sh b/gix-pathspec/tests/fixtures/match_baseline_files.sh old mode 100644 new mode 100755 index b8422ca119c..04a59a7e082 --- a/gix-pathspec/tests/fixtures/match_baseline_files.sh +++ b/gix-pathspec/tests/fixtures/match_baseline_files.sh @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash set -eu -o pipefail git init; diff --git a/gix-pathspec/tests/fixtures/parse_baseline.sh b/gix-pathspec/tests/fixtures/parse_baseline.sh index 56525e9ebec..c22b7831569 100755 --- a/gix-pathspec/tests/fixtures/parse_baseline.sh +++ b/gix-pathspec/tests/fixtures/parse_baseline.sh @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash set -eu -o pipefail git init; diff --git a/gix-pathspec/tests/search/mod.rs b/gix-pathspec/tests/search/mod.rs index 89f51a320ae..07cd328e994 100644 --- a/gix-pathspec/tests/search/mod.rs +++ b/gix-pathspec/tests/search/mod.rs @@ -57,6 +57,31 @@ fn directory_matches_prefix_starting_wildcards_always_match() -> crate::Result { Ok(()) } +#[test] +fn empty_dir_always_matches() -> crate::Result { + for specs in [ + &["*ir"] as &[_], + &[], + &["included", ":!excluded"], + &[":!all", ":!excluded"], + ] { + let mut search = gix_pathspec::Search::from_specs(pathspecs(specs), None, Path::new(""))?; + assert_eq!( + search + .pattern_matching_relative_path("".into(), None, &mut no_attrs) + .map(|m| m.kind), + Some(Always), + "{specs:?}" + ); + assert!(search.directory_matches_prefix("".into(), false)); + assert!(search.directory_matches_prefix("".into(), false)); + for is_dir in [Some(true), Some(false), None] { + assert!(search.can_match_relative_path("".into(), is_dir)); + } + } + Ok(()) +} + #[test] fn directory_matches_prefix_leading() -> crate::Result { let search = gix_pathspec::Search::from_specs(pathspecs(&["d/d/generated/b"]), None, Path::new(""))?; @@ -135,6 +160,47 @@ fn no_pathspecs_match_everything() -> crate::Result { Ok(()) } +#[test] +fn included_directory_and_excluded_subdir_top_level_with_prefix() -> crate::Result { + let mut search = gix_pathspec::Search::from_specs(pathspecs(&[":/foo", ":!/foo/target/"]), None, Path::new("foo"))?; + let m = search + .pattern_matching_relative_path("foo".into(), Some(true), &mut no_attrs) + .expect("matches"); + assert_eq!(m.kind, Verbatim); + + let m = search + .pattern_matching_relative_path("foo/bar".into(), Some(false), &mut no_attrs) + .expect("matches"); + assert_eq!(m.kind, Prefix); + + let m = search + .pattern_matching_relative_path("foo/target".into(), Some(false), &mut no_attrs) + .expect("matches"); + assert_eq!(m.kind, Prefix, "files named `target` are allowed"); + + let m = search + .pattern_matching_relative_path("foo/target".into(), Some(true), &mut no_attrs) + .expect("matches"); + assert!(m.is_excluded(), "directories named `target` are excluded"); + assert_eq!(m.kind, Verbatim); + + let m = search + .pattern_matching_relative_path("foo/target/file".into(), Some(false), &mut no_attrs) + .expect("matches"); + assert!(m.is_excluded(), "everything below `target/` is also excluded"); + assert_eq!(m.kind, Prefix); + + assert!(search.directory_matches_prefix("foo/bar".into(), false)); + assert!(search.directory_matches_prefix("foo/bar".into(), true)); + assert!(search.directory_matches_prefix("foo".into(), false)); + assert!(search.directory_matches_prefix("foo".into(), true)); + assert!(search.can_match_relative_path("foo".into(), Some(true))); + assert!(search.can_match_relative_path("foo".into(), Some(false))); + assert!(search.can_match_relative_path("foo/hi".into(), Some(true))); + assert!(search.can_match_relative_path("foo/hi".into(), Some(false))); + Ok(()) +} + #[test] fn starts_with() -> crate::Result { let mut search = gix_pathspec::Search::from_specs(pathspecs(&["a/*"]), None, Path::new(""))?; @@ -261,8 +327,14 @@ fn simplified_search_respects_all_excluded() -> crate::Result { None, Path::new(""), )?; - assert!(!search.can_match_relative_path("b".into(), None)); - assert!(!search.can_match_relative_path("a".into(), None)); + assert!( + search.can_match_relative_path("b".into(), None), + "non-trivial excludes are ignored in favor of false-positives" + ); + assert!( + search.can_match_relative_path("a".into(), None), + "non-trivial excludes are ignored in favor of false-positives" + ); assert!(search.can_match_relative_path("c".into(), None)); assert!(search.can_match_relative_path("c/".into(), None)); @@ -370,7 +442,7 @@ fn init_with_exclude() -> crate::Result { assert_eq!(search.patterns().count(), 2, "nothing artificial is added"); assert!( search.patterns().next().expect("first of two").is_excluded(), - "re-orded so that excluded are first" + "re-ordered so that excluded are first" ); assert_eq!(search.common_prefix(), "tests"); assert_eq!( diff --git a/gix-prompt/CHANGELOG.md b/gix-prompt/CHANGELOG.md index 3858503d368..0d0042e4631 100644 --- a/gix-prompt/CHANGELOG.md +++ b/gix-prompt/CHANGELOG.md @@ -5,6 +5,57 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## 0.8.5 (2024-05-22) + +A maintenance release without user-facing changes. + +### Commit Statistics + + + + - 3 commits contributed to the release. + - 68 days passed between releases. + - 0 commits were understood as [conventional](https://www.conventionalcommits.org). + - 0 issues like '(#ID)' were seen in commit messages + +### Commit Details + + + +
view details + + * **Uncategorized** + - Adjust changelogs prior to release ([`9511416`](https://github.com/Byron/gitoxide/commit/9511416a6cd0c571233f958c165329c8705c2498)) + - Merge branch 'various-fixes' ([`d6cd449`](https://github.com/Byron/gitoxide/commit/d6cd44930fb204b06e2b70fc6965e7705530c47a)) + - Update dependencies ([`cd4de83`](https://github.com/Byron/gitoxide/commit/cd4de8327fc195eb862ab6e138f2315a87374f85)) +
+ +## 0.8.4 (2024-03-14) + +A maintenance release without user-facing changes. + +### Commit Statistics + + + + - 4 commits contributed to the release over the course of 4 calendar days. + - 18 days passed between releases. + - 0 commits were understood as [conventional](https://www.conventionalcommits.org). + - 0 issues like '(#ID)' were seen in commit messages + +### Commit Details + + + +
view details + + * **Uncategorized** + - Release gix-date v0.8.5, gix-hash v0.14.2, gix-trace v0.1.8, gix-utils v0.1.11, gix-features v0.38.1, gix-actor v0.31.0, gix-validate v0.8.4, gix-object v0.42.0, gix-path v0.10.7, gix-glob v0.16.2, gix-quote v0.4.12, gix-attributes v0.22.2, gix-command v0.3.6, gix-filter v0.11.0, gix-fs v0.10.1, gix-chunk v0.4.8, gix-commitgraph v0.24.2, gix-hashtable v0.5.2, gix-revwalk v0.13.0, gix-traverse v0.38.0, gix-worktree-stream v0.11.0, gix-archive v0.11.0, gix-config-value v0.14.6, gix-tempfile v13.1.1, gix-lock v13.1.1, gix-ref v0.43.0, gix-sec v0.10.6, gix-config v0.36.0, gix-prompt v0.8.4, gix-url v0.27.2, gix-credentials v0.24.2, gix-ignore v0.11.2, gix-bitmap v0.2.11, gix-index v0.31.0, gix-worktree v0.32.0, gix-diff v0.42.0, gix-discover v0.31.0, gix-pathspec v0.7.1, gix-dir v0.2.0, gix-macros v0.1.4, gix-mailmap v0.23.0, gix-negotiate v0.13.0, gix-pack v0.49.0, gix-odb v0.59.0, gix-packetline v0.17.4, gix-transport v0.41.2, gix-protocol v0.44.2, gix-revision v0.27.0, gix-refspec v0.23.0, gix-status v0.7.0, gix-submodule v0.10.0, gix-worktree-state v0.9.0, gix v0.60.0, safety bump 26 crates ([`b050327`](https://github.com/Byron/gitoxide/commit/b050327e76f234b19be921b78b7b28e034319fdb)) + - Prepare changelogs prior to release ([`52c3bbd`](https://github.com/Byron/gitoxide/commit/52c3bbd36b9e94a0f3a78b4ada84d0c08eba27f6)) + - Merge branch 'status' ([`3e5c974`](https://github.com/Byron/gitoxide/commit/3e5c974dd62ac134711c6c2f5a5490187a6ea55e)) + - Fix lints for nightly, and clippy ([`f8ce3d0`](https://github.com/Byron/gitoxide/commit/f8ce3d0721b6a53713a9392f2451874f520bc44c)) +
+ ## 0.8.3 (2024-02-25) A maintenance release without user-facing changes. @@ -13,7 +64,7 @@ A maintenance release without user-facing changes. - - 2 commits contributed to the release over the course of 21 calendar days. + - 3 commits contributed to the release over the course of 21 calendar days. - 57 days passed between releases. - 0 commits were understood as [conventional](https://www.conventionalcommits.org). - 0 issues like '(#ID)' were seen in commit messages @@ -25,6 +76,7 @@ A maintenance release without user-facing changes.
view details * **Uncategorized** + - Release gix-date v0.8.4, gix-utils v0.1.10, gix-actor v0.30.1, gix-object v0.41.1, gix-path v0.10.6, gix-glob v0.16.1, gix-quote v0.4.11, gix-attributes v0.22.1, gix-command v0.3.5, gix-filter v0.10.0, gix-commitgraph v0.24.1, gix-worktree-stream v0.10.0, gix-archive v0.10.0, gix-config-value v0.14.5, gix-ref v0.42.0, gix-sec v0.10.5, gix-config v0.35.0, gix-prompt v0.8.3, gix-url v0.27.1, gix-credentials v0.24.1, gix-ignore v0.11.1, gix-index v0.30.0, gix-worktree v0.31.0, gix-diff v0.41.0, gix-discover v0.30.0, gix-pathspec v0.7.0, gix-dir v0.1.0, gix-pack v0.48.0, gix-odb v0.58.0, gix-transport v0.41.1, gix-protocol v0.44.1, gix-revision v0.26.1, gix-refspec v0.22.1, gix-status v0.6.0, gix-submodule v0.9.0, gix-worktree-state v0.8.0, gix v0.59.0, gix-fsck v0.3.0, gitoxide-core v0.36.0, gitoxide v0.34.0, safety bump 10 crates ([`45b4470`](https://github.com/Byron/gitoxide/commit/45b447045bc826f252129c300c531acde2652c64)) - Prepare changelogs prior to release ([`f2e111f`](https://github.com/Byron/gitoxide/commit/f2e111f768fc1bc6182355261c20b63610cffec7)) - Release gix-command v0.3.4 ([`8a62fb5`](https://github.com/Byron/gitoxide/commit/8a62fb57f7751d3d57273d9430517487e555f999))
diff --git a/gix-prompt/Cargo.toml b/gix-prompt/Cargo.toml index 1d06f0559c2..827ecbb9369 100644 --- a/gix-prompt/Cargo.toml +++ b/gix-prompt/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "gix-prompt" -version = "0.8.3" +version = "0.8.5" repository = "https://github.com/Byron/gitoxide" license = "MIT OR Apache-2.0" description = "A crate of the gitoxide project for handling prompts in the terminal" @@ -13,8 +13,8 @@ rust-version = "1.65" doctest = false [dependencies] -gix-command = { version = "^0.3.5", path = "../gix-command" } -gix-config-value = { version = "^0.14.5", path = "../gix-config-value" } +gix-command = { version = "^0.3.7", path = "../gix-command" } +gix-config-value = { version = "^0.14.6", path = "../gix-config-value" } thiserror = "1.0.32" parking_lot = "0.12.1" @@ -23,6 +23,6 @@ parking_lot = "0.12.1" rustix = { version = "0.38.4", features = ["termios"] } [dev-dependencies] -gix-testtools = { path = "../tests/tools"} -serial_test = { version = "2.0.0", default-features = false } +gix-testtools = { path = "../tests/tools" } +serial_test = { version = "3.1.0", default-features = false } expectrl = "0.7.0" diff --git a/gix-prompt/src/lib.rs b/gix-prompt/src/lib.rs index dba6cf80405..e25f6058fff 100644 --- a/gix-prompt/src/lib.rs +++ b/gix-prompt/src/lib.rs @@ -11,6 +11,7 @@ mod types; pub use types::{Error, Mode, Options}; /// +#[allow(clippy::empty_docs)] pub mod unix; #[cfg(unix)] use unix::imp; diff --git a/gix-prompt/src/types.rs b/gix-prompt/src/types.rs index 6f3bb2c0430..ccf0f7c9493 100644 --- a/gix-prompt/src/types.rs +++ b/gix-prompt/src/types.rs @@ -1,4 +1,4 @@ -use std::{borrow::Cow, convert::TryFrom, path::Path}; +use std::{borrow::Cow, path::Path}; /// The error returned by [ask()][crate::ask()]. #[derive(Debug, thiserror::Error)] diff --git a/gix-protocol/CHANGELOG.md b/gix-protocol/CHANGELOG.md index 25c195a4a90..afc4359380d 100644 --- a/gix-protocol/CHANGELOG.md +++ b/gix-protocol/CHANGELOG.md @@ -5,6 +5,80 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## 0.45.1 (2024-05-22) + +A maintenance release without user-facing changes. + +### Commit Statistics + + + + - 2 commits contributed to the release over the course of 8 calendar days. + - 38 days passed between releases. + - 0 commits were understood as [conventional](https://www.conventionalcommits.org). + - 0 issues like '(#ID)' were seen in commit messages + +### Commit Details + + + +
view details + + * **Uncategorized** + - Adjust changelogs prior to release ([`9511416`](https://github.com/Byron/gitoxide/commit/9511416a6cd0c571233f958c165329c8705c2498)) + - Release gix-date v0.8.6 ([`d3588ca`](https://github.com/Byron/gitoxide/commit/d3588ca4fe0364c88e42cdac24ceae548355d99d)) +
+ +## 0.45.0 (2024-04-13) + +A maintenance release without user-facing changes. + +### Commit Statistics + + + + - 2 commits contributed to the release. + - 29 days passed between releases. + - 0 commits were understood as [conventional](https://www.conventionalcommits.org). + - 0 issues like '(#ID)' were seen in commit messages + +### Commit Details + + + +
view details + + * **Uncategorized** + - Release gix-trace v0.1.9, gix-utils v0.1.12, gix-packetline-blocking v0.17.4, gix-filter v0.11.1, gix-fs v0.10.2, gix-traverse v0.39.0, gix-worktree-stream v0.12.0, gix-archive v0.12.0, gix-config v0.36.1, gix-url v0.27.3, gix-index v0.32.0, gix-worktree v0.33.0, gix-diff v0.43.0, gix-pathspec v0.7.3, gix-dir v0.4.0, gix-pack v0.50.0, gix-odb v0.60.0, gix-transport v0.42.0, gix-protocol v0.45.0, gix-status v0.9.0, gix-worktree-state v0.10.0, gix v0.62.0, gix-fsck v0.4.0, gitoxide-core v0.37.0, gitoxide v0.35.0, safety bump 14 crates ([`095c673`](https://github.com/Byron/gitoxide/commit/095c6739b2722a8b9af90776b435ef2da454c0e6)) + - Prepare changelogs prior to release ([`5755271`](https://github.com/Byron/gitoxide/commit/57552717f46f96c35ba4ddc0a64434354ef845e9)) +
+ +## 0.44.2 (2024-03-14) + +A maintenance release without user-facing changes. + +### Commit Statistics + + + + - 4 commits contributed to the release over the course of 4 calendar days. + - 18 days passed between releases. + - 0 commits were understood as [conventional](https://www.conventionalcommits.org). + - 0 issues like '(#ID)' were seen in commit messages + +### Commit Details + + + +
view details + + * **Uncategorized** + - Release gix-date v0.8.5, gix-hash v0.14.2, gix-trace v0.1.8, gix-utils v0.1.11, gix-features v0.38.1, gix-actor v0.31.0, gix-validate v0.8.4, gix-object v0.42.0, gix-path v0.10.7, gix-glob v0.16.2, gix-quote v0.4.12, gix-attributes v0.22.2, gix-command v0.3.6, gix-filter v0.11.0, gix-fs v0.10.1, gix-chunk v0.4.8, gix-commitgraph v0.24.2, gix-hashtable v0.5.2, gix-revwalk v0.13.0, gix-traverse v0.38.0, gix-worktree-stream v0.11.0, gix-archive v0.11.0, gix-config-value v0.14.6, gix-tempfile v13.1.1, gix-lock v13.1.1, gix-ref v0.43.0, gix-sec v0.10.6, gix-config v0.36.0, gix-prompt v0.8.4, gix-url v0.27.2, gix-credentials v0.24.2, gix-ignore v0.11.2, gix-bitmap v0.2.11, gix-index v0.31.0, gix-worktree v0.32.0, gix-diff v0.42.0, gix-discover v0.31.0, gix-pathspec v0.7.1, gix-dir v0.2.0, gix-macros v0.1.4, gix-mailmap v0.23.0, gix-negotiate v0.13.0, gix-pack v0.49.0, gix-odb v0.59.0, gix-packetline v0.17.4, gix-transport v0.41.2, gix-protocol v0.44.2, gix-revision v0.27.0, gix-refspec v0.23.0, gix-status v0.7.0, gix-submodule v0.10.0, gix-worktree-state v0.9.0, gix v0.60.0, safety bump 26 crates ([`b050327`](https://github.com/Byron/gitoxide/commit/b050327e76f234b19be921b78b7b28e034319fdb)) + - Prepare changelogs prior to release ([`52c3bbd`](https://github.com/Byron/gitoxide/commit/52c3bbd36b9e94a0f3a78b4ada84d0c08eba27f6)) + - Merge branch 'status' ([`3e5c974`](https://github.com/Byron/gitoxide/commit/3e5c974dd62ac134711c6c2f5a5490187a6ea55e)) + - Fix lints for nightly, and clippy ([`f8ce3d0`](https://github.com/Byron/gitoxide/commit/f8ce3d0721b6a53713a9392f2451874f520bc44c)) +
+ ## 0.44.1 (2024-02-25) A maintenance release without user-facing changes. @@ -13,7 +87,7 @@ A maintenance release without user-facing changes. - - 9 commits contributed to the release over the course of 24 calendar days. + - 10 commits contributed to the release over the course of 24 calendar days. - 36 days passed between releases. - 0 commits were understood as [conventional](https://www.conventionalcommits.org). - 0 issues like '(#ID)' were seen in commit messages @@ -25,6 +99,7 @@ A maintenance release without user-facing changes.
view details * **Uncategorized** + - Release gix-date v0.8.4, gix-utils v0.1.10, gix-actor v0.30.1, gix-object v0.41.1, gix-path v0.10.6, gix-glob v0.16.1, gix-quote v0.4.11, gix-attributes v0.22.1, gix-command v0.3.5, gix-filter v0.10.0, gix-commitgraph v0.24.1, gix-worktree-stream v0.10.0, gix-archive v0.10.0, gix-config-value v0.14.5, gix-ref v0.42.0, gix-sec v0.10.5, gix-config v0.35.0, gix-prompt v0.8.3, gix-url v0.27.1, gix-credentials v0.24.1, gix-ignore v0.11.1, gix-index v0.30.0, gix-worktree v0.31.0, gix-diff v0.41.0, gix-discover v0.30.0, gix-pathspec v0.7.0, gix-dir v0.1.0, gix-pack v0.48.0, gix-odb v0.58.0, gix-transport v0.41.1, gix-protocol v0.44.1, gix-revision v0.26.1, gix-refspec v0.22.1, gix-status v0.6.0, gix-submodule v0.9.0, gix-worktree-state v0.8.0, gix v0.59.0, gix-fsck v0.3.0, gitoxide-core v0.36.0, gitoxide v0.34.0, safety bump 10 crates ([`45b4470`](https://github.com/Byron/gitoxide/commit/45b447045bc826f252129c300c531acde2652c64)) - Prepare changelogs prior to release ([`f2e111f`](https://github.com/Byron/gitoxide/commit/f2e111f768fc1bc6182355261c20b63610cffec7)) - Merge branch 'btoi' ([`5fc379d`](https://github.com/Byron/gitoxide/commit/5fc379d1dc867d15a50cb086e30beefde2b42d86)) - Refactor ([`c5c69bd`](https://github.com/Byron/gitoxide/commit/c5c69bd355771a6fb3e4f6db0c5f49aa2bf7f42f)) diff --git a/gix-protocol/Cargo.toml b/gix-protocol/Cargo.toml index 1d8621eb3a0..eed322d9949 100644 --- a/gix-protocol/Cargo.toml +++ b/gix-protocol/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "gix-protocol" -version = "0.44.1" +version = "0.45.1" repository = "https://github.com/Byron/gitoxide" license = "MIT OR Apache-2.0" description = "A crate of the gitoxide project for implementing git protocols" @@ -45,14 +45,14 @@ path = "tests/async-protocol.rs" required-features = ["async-client"] [dependencies] -gix-features = { version = "^0.38.0", path = "../gix-features", features = [ +gix-features = { version = "^0.38.2", path = "../gix-features", features = [ "progress", ] } -gix-transport = { version = "^0.41.1", path = "../gix-transport" } -gix-hash = { version = "^0.14.1", path = "../gix-hash" } -gix-date = { version = "^0.8.4", path = "../gix-date" } -gix-credentials = { version = "^0.24.1", path = "../gix-credentials" } -gix-utils = { version = "^0.1.10", path = "../gix-utils" } +gix-transport = { version = "^0.42.1", path = "../gix-transport" } +gix-hash = { version = "^0.14.2", path = "../gix-hash" } +gix-date = { version = "^0.8.6", path = "../gix-date" } +gix-credentials = { version = "^0.24.2", path = "../gix-credentials" } +gix-utils = { version = "^0.1.12", path = "../gix-utils" } thiserror = "1.0.32" serde = { version = "1.0.114", optional = true, default-features = false, features = [ @@ -74,7 +74,7 @@ document-features = { version = "0.2.0", optional = true } [dev-dependencies] async-std = { version = "1.9.0", features = ["attributes"] } -gix-packetline = { path = "../gix-packetline", version = "^0.17.3" } +gix-packetline = { path = "../gix-packetline", version = "^0.17.4" } gix-testtools = { path = "../tests/tools" } [package.metadata.docs.rs] diff --git a/gix-protocol/src/fetch/mod.rs b/gix-protocol/src/fetch/mod.rs index 0828ea733a2..56e85f4babd 100644 --- a/gix-protocol/src/fetch/mod.rs +++ b/gix-protocol/src/fetch/mod.rs @@ -2,6 +2,7 @@ mod arguments; pub use arguments::Arguments; /// +#[allow(clippy::empty_docs)] pub mod delegate; #[cfg(any(feature = "async-client", feature = "blocking-client"))] pub use delegate::Delegate; @@ -10,6 +11,7 @@ pub use delegate::{Action, DelegateBlocking}; mod error; pub use error::Error; /// +#[allow(clippy::empty_docs)] pub mod response; pub use response::Response; diff --git a/gix-protocol/src/handshake/mod.rs b/gix-protocol/src/handshake/mod.rs index 28243e96da7..cc388e96d2e 100644 --- a/gix-protocol/src/handshake/mod.rs +++ b/gix-protocol/src/handshake/mod.rs @@ -98,4 +98,5 @@ pub use error::Error; pub(crate) mod function; /// +#[allow(clippy::empty_docs)] pub mod refs; diff --git a/gix-protocol/src/handshake/refs/mod.rs b/gix-protocol/src/handshake/refs/mod.rs index 39c6c85a942..cbefc358aab 100644 --- a/gix-protocol/src/handshake/refs/mod.rs +++ b/gix-protocol/src/handshake/refs/mod.rs @@ -3,6 +3,7 @@ use bstr::BStr; use super::Ref; /// +#[allow(clippy::empty_docs)] pub mod parse { use bstr::BString; diff --git a/gix-protocol/src/lib.rs b/gix-protocol/src/lib.rs index 6b4ee69d2b6..63f26e632aa 100644 --- a/gix-protocol/src/lib.rs +++ b/gix-protocol/src/lib.rs @@ -32,6 +32,7 @@ pub use gix_transport as transport; pub use maybe_async; /// +#[allow(clippy::empty_docs)] #[cfg(any(feature = "blocking-client", feature = "async-client"))] pub mod fetch; @@ -48,11 +49,13 @@ compile_error!("Cannot set both 'blocking-client' and 'async-client' features as /// #[cfg(any(feature = "blocking-client", feature = "async-client"))] +#[allow(clippy::empty_docs)] pub mod handshake; #[cfg(any(feature = "blocking-client", feature = "async-client"))] pub use handshake::function::handshake; /// +#[allow(clippy::empty_docs)] #[cfg(any(feature = "blocking-client", feature = "async-client"))] pub mod ls_refs; #[cfg(any(feature = "blocking-client", feature = "async-client"))] diff --git a/gix-protocol/src/remote_progress.rs b/gix-protocol/src/remote_progress.rs index d7baa3bf9f5..deba3e6749d 100644 --- a/gix-protocol/src/remote_progress.rs +++ b/gix-protocol/src/remote_progress.rs @@ -1,5 +1,3 @@ -use std::convert::TryFrom; - use bstr::ByteSlice; use winnow::{ combinator::{opt, preceded, terminated}, diff --git a/gix-protocol/tests/fetch/mod.rs b/gix-protocol/tests/fetch/mod.rs index a7c29fbd6ec..b579b080f80 100644 --- a/gix-protocol/tests/fetch/mod.rs +++ b/gix-protocol/tests/fetch/mod.rs @@ -89,7 +89,7 @@ impl fetch::DelegateBlocking for CloneRefInWantDelegate { _features: &mut Vec<(&str, Option>)>, refs: &[handshake::Ref], ) -> io::Result { - self.refs = refs.to_owned(); + refs.clone_into(&mut self.refs); Ok(Action::Continue) } @@ -135,7 +135,7 @@ impl fetch::DelegateBlocking for LsRemoteDelegate { _features: &mut Vec<(&str, Option>)>, refs: &[handshake::Ref], ) -> io::Result { - self.refs = refs.to_owned(); + refs.clone_into(&mut self.refs); Ok(fetch::Action::Cancel) } fn negotiate( diff --git a/gix-quote/CHANGELOG.md b/gix-quote/CHANGELOG.md index 8482c19dd2c..b30bd24a8bd 100644 --- a/gix-quote/CHANGELOG.md +++ b/gix-quote/CHANGELOG.md @@ -5,6 +5,31 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## 0.4.12 (2024-03-14) + +A maintenance release without user-facing changes. + +### Commit Statistics + + + + - 3 commits contributed to the release over the course of 4 calendar days. + - 18 days passed between releases. + - 0 commits were understood as [conventional](https://www.conventionalcommits.org). + - 0 issues like '(#ID)' were seen in commit messages + +### Commit Details + + + +
view details + + * **Uncategorized** + - Prepare changelogs prior to release ([`52c3bbd`](https://github.com/Byron/gitoxide/commit/52c3bbd36b9e94a0f3a78b4ada84d0c08eba27f6)) + - Merge branch 'status' ([`3e5c974`](https://github.com/Byron/gitoxide/commit/3e5c974dd62ac134711c6c2f5a5490187a6ea55e)) + - Fix lints for nightly, and clippy ([`f8ce3d0`](https://github.com/Byron/gitoxide/commit/f8ce3d0721b6a53713a9392f2451874f520bc44c)) +
+ ## 0.4.11 (2024-02-25) A maintenance release without user-facing changes. @@ -13,7 +38,7 @@ A maintenance release without user-facing changes. - - 4 commits contributed to the release over the course of 10 calendar days. + - 5 commits contributed to the release over the course of 10 calendar days. - 57 days passed between releases. - 0 commits were understood as [conventional](https://www.conventionalcommits.org). - 0 issues like '(#ID)' were seen in commit messages @@ -25,6 +50,7 @@ A maintenance release without user-facing changes.
view details * **Uncategorized** + - Release gix-date v0.8.4, gix-utils v0.1.10, gix-actor v0.30.1, gix-object v0.41.1, gix-path v0.10.6, gix-glob v0.16.1, gix-quote v0.4.11, gix-attributes v0.22.1, gix-command v0.3.5, gix-filter v0.10.0, gix-commitgraph v0.24.1, gix-worktree-stream v0.10.0, gix-archive v0.10.0, gix-config-value v0.14.5, gix-ref v0.42.0, gix-sec v0.10.5, gix-config v0.35.0, gix-prompt v0.8.3, gix-url v0.27.1, gix-credentials v0.24.1, gix-ignore v0.11.1, gix-index v0.30.0, gix-worktree v0.31.0, gix-diff v0.41.0, gix-discover v0.30.0, gix-pathspec v0.7.0, gix-dir v0.1.0, gix-pack v0.48.0, gix-odb v0.58.0, gix-transport v0.41.1, gix-protocol v0.44.1, gix-revision v0.26.1, gix-refspec v0.22.1, gix-status v0.6.0, gix-submodule v0.9.0, gix-worktree-state v0.8.0, gix v0.59.0, gix-fsck v0.3.0, gitoxide-core v0.36.0, gitoxide v0.34.0, safety bump 10 crates ([`45b4470`](https://github.com/Byron/gitoxide/commit/45b447045bc826f252129c300c531acde2652c64)) - Prepare changelogs prior to release ([`f2e111f`](https://github.com/Byron/gitoxide/commit/f2e111f768fc1bc6182355261c20b63610cffec7)) - Merge branch 'btoi' ([`5fc379d`](https://github.com/Byron/gitoxide/commit/5fc379d1dc867d15a50cb086e30beefde2b42d86)) - Refactor ([`c5c69bd`](https://github.com/Byron/gitoxide/commit/c5c69bd355771a6fb3e4f6db0c5f49aa2bf7f42f)) diff --git a/gix-quote/Cargo.toml b/gix-quote/Cargo.toml index 2361259beaf..2b4124c0a08 100644 --- a/gix-quote/Cargo.toml +++ b/gix-quote/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "gix-quote" -version = "0.4.11" +version = "0.4.12" repository = "https://github.com/Byron/gitoxide" license = "MIT OR Apache-2.0" description = "A crate of the gitoxide project dealing with various quotations used by git" @@ -13,7 +13,7 @@ include = ["src/**/*", "LICENSE-*"] doctest = false [dependencies] -gix-utils = { version = "^0.1.10", path = "../gix-utils" } +gix-utils = { version = "^0.1.11", path = "../gix-utils" } bstr = { version = "1.3.0", default-features = false, features = ["std"] } thiserror = "1.0.38" diff --git a/gix-quote/src/ansi_c.rs b/gix-quote/src/ansi_c.rs index 43856f8992e..1b3bf3a66fd 100644 --- a/gix-quote/src/ansi_c.rs +++ b/gix-quote/src/ansi_c.rs @@ -1,4 +1,5 @@ /// +#[allow(clippy::empty_docs)] pub mod undo { use bstr::{BStr, BString}; diff --git a/gix-quote/src/lib.rs b/gix-quote/src/lib.rs index b86336b3b0b..f0ed9785995 100644 --- a/gix-quote/src/lib.rs +++ b/gix-quote/src/lib.rs @@ -3,6 +3,7 @@ #![forbid(unsafe_code)] /// +#[allow(clippy::empty_docs)] pub mod ansi_c; mod single; diff --git a/gix-ref/CHANGELOG.md b/gix-ref/CHANGELOG.md index c0bc9f86362..653c88fdbe9 100644 --- a/gix-ref/CHANGELOG.md +++ b/gix-ref/CHANGELOG.md @@ -5,6 +5,121 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## 0.44.1 (2024-05-27) + +### Bug Fixes + + - `LineRef` is now `Copy` and remove non-exhaustive + After all, this struct refers to what's in a reflog line, which is stable. + +### Commit Statistics + + + + - 1 commit contributed to the release. + - 4 days passed between releases. + - 1 commit was understood as [conventional](https://www.conventionalcommits.org). + - 0 issues like '(#ID)' were seen in commit messages + +### Commit Details + + + +
view details + + * **Uncategorized** + - `LineRef` is now `Copy` and remove non-exhaustive ([`cd969f6`](https://github.com/Byron/gitoxide/commit/cd969f69d1b86030d4ea5c5a77100f36caffb8d8)) +
+ +## 0.44.0 (2024-05-22) + +### New Features + + - add `file::Store::force_refresh_packed_buffer()` to public API + That way it's possible to explicitly refresh if the caller knows that some other + operation might have invalidated the in-memory cache. The `mtime` based approach + doesn't work reliably on all filesystems due to coarse granularity of time. + +### Bug Fixes + + - don't ignore packed ref deletion in non-default transaction mode + Before, Change::Delete edits weren't propagated to packed refs if the mode was + DeletionsAndNonSymbolicUpdates/RemoveLooseSourceReference. + +### Bug Fixes (BREAKING) + + - assure that special device names on Windows aren't allowed. + Otherwise it's possible to read or write to devices when interacting + with references of the 'right' name. + + This behaviour can be controlled with the new `prohibit_windows_device_names` flag, + which is adjustable on the `Store` instance as field, and which now has to be + passed during instantiation as part of the new `store::init::Options` struct. + +### Commit Statistics + + + + - 13 commits contributed to the release over the course of 30 calendar days. + - 68 days passed between releases. + - 3 commits were understood as [conventional](https://www.conventionalcommits.org). + - 1 unique issue was worked on: [#1348](https://github.com/Byron/gitoxide/issues/1348) + +### Commit Details + + + +
view details + + * **[#1348](https://github.com/Byron/gitoxide/issues/1348)** + - Add `file::Store::force_refresh_packed_buffer()` to public API ([`f2d8955`](https://github.com/Byron/gitoxide/commit/f2d8955eacc1a0ce8678f3e9026e696926bae654)) + * **Uncategorized** + - Release gix-features v0.38.2, gix-actor v0.31.2, gix-validate v0.8.5, gix-object v0.42.2, gix-command v0.3.7, gix-filter v0.11.2, gix-fs v0.11.0, gix-revwalk v0.13.1, gix-traverse v0.39.1, gix-worktree-stream v0.13.0, gix-archive v0.13.0, gix-tempfile v14.0.0, gix-lock v14.0.0, gix-ref v0.44.0, gix-config v0.37.0, gix-prompt v0.8.5, gix-index v0.33.0, gix-worktree v0.34.0, gix-diff v0.44.0, gix-discover v0.32.0, gix-pathspec v0.7.5, gix-dir v0.5.0, gix-macros v0.1.5, gix-mailmap v0.23.1, gix-negotiate v0.13.1, gix-pack v0.51.0, gix-odb v0.61.0, gix-transport v0.42.1, gix-protocol v0.45.1, gix-revision v0.27.1, gix-status v0.10.0, gix-submodule v0.11.0, gix-worktree-state v0.11.0, gix v0.63.0, gitoxide-core v0.38.0, gitoxide v0.36.0, safety bump 19 crates ([`4f98e94`](https://github.com/Byron/gitoxide/commit/4f98e94e0e8b79ed2899b35bef40f3c30b3025b0)) + - Adjust changelogs prior to release ([`9511416`](https://github.com/Byron/gitoxide/commit/9511416a6cd0c571233f958c165329c8705c2498)) + - Merge branch 'various-fixes' ([`d6cd449`](https://github.com/Byron/gitoxide/commit/d6cd44930fb204b06e2b70fc6965e7705530c47a)) + - Fix-CI ([`6f55f2a`](https://github.com/Byron/gitoxide/commit/6f55f2abd13078f94e8c4e10922806f195ae0d8b)) + - Merge pull request from GHSA-7w47-3wg8-547c ([`79dce79`](https://github.com/Byron/gitoxide/commit/79dce79c62f6072aa2653780d590dc3993dfa401)) + - Apply suggestions from code review ([`1242151`](https://github.com/Byron/gitoxide/commit/1242151079004ae99fae7b80966de151961a6159)) + - Assure that special device names on Windows aren't allowed. ([`9555efe`](https://github.com/Byron/gitoxide/commit/9555efe8964d3de3c692f79cef390916e34daefb)) + - Release gix-date v0.8.6 ([`d3588ca`](https://github.com/Byron/gitoxide/commit/d3588ca4fe0364c88e42cdac24ceae548355d99d)) + - Merge branch 'push-wwxrqxuzmolm' ([`048e43e`](https://github.com/Byron/gitoxide/commit/048e43e26908b0572852a75780a451460dc152ff)) + - Refactor - address fix suggested by `cargo check` ([`4faf10e`](https://github.com/Byron/gitoxide/commit/4faf10e27a942da3da25e659a43e97eb160e79f2)) + - Don't ignore packed ref deletion in non-default transaction mode ([`e33efa2`](https://github.com/Byron/gitoxide/commit/e33efa23c2ed45b3c2826fe2c3c70707fcb9100d)) + - Merge branch 'status' ([`68fd5b3`](https://github.com/Byron/gitoxide/commit/68fd5b34e1214d5c2cc7d00dd06e19ee86c00c66)) +
+ +## 0.43.0 (2024-03-14) + +### Bug Fixes + + - assure memory maps are created with `MAP_PRIVATE` + That way, the mmap process should work under more circumstances. + +### Commit Statistics + + + + - 6 commits contributed to the release over the course of 5 calendar days. + - 18 days passed between releases. + - 1 commit was understood as [conventional](https://www.conventionalcommits.org). + - 1 unique issue was worked on: [#1312](https://github.com/Byron/gitoxide/issues/1312) + +### Commit Details + + + +
view details + + * **[#1312](https://github.com/Byron/gitoxide/issues/1312)** + - Assure memory maps are created with `MAP_PRIVATE` ([`88061a1`](https://github.com/Byron/gitoxide/commit/88061a176b2f4b5a377a4cff513979ddb1e306a1)) + * **Uncategorized** + - Release gix-date v0.8.5, gix-hash v0.14.2, gix-trace v0.1.8, gix-utils v0.1.11, gix-features v0.38.1, gix-actor v0.31.0, gix-validate v0.8.4, gix-object v0.42.0, gix-path v0.10.7, gix-glob v0.16.2, gix-quote v0.4.12, gix-attributes v0.22.2, gix-command v0.3.6, gix-filter v0.11.0, gix-fs v0.10.1, gix-chunk v0.4.8, gix-commitgraph v0.24.2, gix-hashtable v0.5.2, gix-revwalk v0.13.0, gix-traverse v0.38.0, gix-worktree-stream v0.11.0, gix-archive v0.11.0, gix-config-value v0.14.6, gix-tempfile v13.1.1, gix-lock v13.1.1, gix-ref v0.43.0, gix-sec v0.10.6, gix-config v0.36.0, gix-prompt v0.8.4, gix-url v0.27.2, gix-credentials v0.24.2, gix-ignore v0.11.2, gix-bitmap v0.2.11, gix-index v0.31.0, gix-worktree v0.32.0, gix-diff v0.42.0, gix-discover v0.31.0, gix-pathspec v0.7.1, gix-dir v0.2.0, gix-macros v0.1.4, gix-mailmap v0.23.0, gix-negotiate v0.13.0, gix-pack v0.49.0, gix-odb v0.59.0, gix-packetline v0.17.4, gix-transport v0.41.2, gix-protocol v0.44.2, gix-revision v0.27.0, gix-refspec v0.23.0, gix-status v0.7.0, gix-submodule v0.10.0, gix-worktree-state v0.9.0, gix v0.60.0, safety bump 26 crates ([`b050327`](https://github.com/Byron/gitoxide/commit/b050327e76f234b19be921b78b7b28e034319fdb)) + - Prepare changelogs prior to release ([`52c3bbd`](https://github.com/Byron/gitoxide/commit/52c3bbd36b9e94a0f3a78b4ada84d0c08eba27f6)) + - Merge branch 'status' ([`3e5c974`](https://github.com/Byron/gitoxide/commit/3e5c974dd62ac134711c6c2f5a5490187a6ea55e)) + - Fix lints for nightly, and clippy ([`f8ce3d0`](https://github.com/Byron/gitoxide/commit/f8ce3d0721b6a53713a9392f2451874f520bc44c)) + - Merge branch 'mmap-mode' ([`9e9b9fe`](https://github.com/Byron/gitoxide/commit/9e9b9fe6f63759d3bf479a8ec2f9dd1fbef87a08)) +
+ ## 0.42.0 (2024-02-25) A maintenance release without user-facing changes. @@ -13,7 +128,7 @@ A maintenance release without user-facing changes. - - 11 commits contributed to the release over the course of 30 calendar days. + - 12 commits contributed to the release over the course of 30 calendar days. - 36 days passed between releases. - 0 commits were understood as [conventional](https://www.conventionalcommits.org). - 0 issues like '(#ID)' were seen in commit messages @@ -25,6 +140,7 @@ A maintenance release without user-facing changes.
view details * **Uncategorized** + - Release gix-date v0.8.4, gix-utils v0.1.10, gix-actor v0.30.1, gix-object v0.41.1, gix-path v0.10.6, gix-glob v0.16.1, gix-quote v0.4.11, gix-attributes v0.22.1, gix-command v0.3.5, gix-filter v0.10.0, gix-commitgraph v0.24.1, gix-worktree-stream v0.10.0, gix-archive v0.10.0, gix-config-value v0.14.5, gix-ref v0.42.0, gix-sec v0.10.5, gix-config v0.35.0, gix-prompt v0.8.3, gix-url v0.27.1, gix-credentials v0.24.1, gix-ignore v0.11.1, gix-index v0.30.0, gix-worktree v0.31.0, gix-diff v0.41.0, gix-discover v0.30.0, gix-pathspec v0.7.0, gix-dir v0.1.0, gix-pack v0.48.0, gix-odb v0.58.0, gix-transport v0.41.1, gix-protocol v0.44.1, gix-revision v0.26.1, gix-refspec v0.22.1, gix-status v0.6.0, gix-submodule v0.9.0, gix-worktree-state v0.8.0, gix v0.59.0, gix-fsck v0.3.0, gitoxide-core v0.36.0, gitoxide v0.34.0, safety bump 10 crates ([`45b4470`](https://github.com/Byron/gitoxide/commit/45b447045bc826f252129c300c531acde2652c64)) - Prepare changelogs prior to release ([`f2e111f`](https://github.com/Byron/gitoxide/commit/f2e111f768fc1bc6182355261c20b63610cffec7)) - Merge pull request #1290 from epage/winnow ([`a663e9f`](https://github.com/Byron/gitoxide/commit/a663e9fcdb5a3aedc9200da77ebae17d5c3e7135)) - Update winnow to 0.6 ([`e175b20`](https://github.com/Byron/gitoxide/commit/e175b20d431faa6859fbcc52f78400e50f91cad1)) diff --git a/gix-ref/Cargo.toml b/gix-ref/Cargo.toml index b2d3a7ba4ac..244545c8fc1 100644 --- a/gix-ref/Cargo.toml +++ b/gix-ref/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "gix-ref" -version = "0.42.0" +version = "0.44.1" repository = "https://github.com/Byron/gitoxide" license = "MIT OR Apache-2.0" description = "A crate to handle git references" @@ -19,17 +19,17 @@ test = true serde = ["dep:serde", "gix-hash/serde", "gix-actor/serde", "gix-object/serde"] [dependencies] -gix-features = { version = "^0.38.0", path = "../gix-features", features = ["walkdir"]} -gix-fs = { version = "^0.10.0", path = "../gix-fs" } -gix-path = { version = "^0.10.6", path = "../gix-path" } -gix-hash = { version = "^0.14.1", path = "../gix-hash" } -gix-date = { version = "^0.8.4", path = "../gix-date" } -gix-object = { version = "^0.41.1", path = "../gix-object" } -gix-utils = { version = "^0.1.10", path = "../gix-utils" } -gix-validate = { version = "^0.8.3", path = "../gix-validate" } -gix-actor = { version = "^0.30.1", path = "../gix-actor" } -gix-lock = { version = "^13.0.0", path = "../gix-lock" } -gix-tempfile = { version = "^13.0.0", default-features = false, path = "../gix-tempfile" } +gix-features = { version = "^0.38.2", path = "../gix-features", features = ["walkdir"]} +gix-fs = { version = "^0.11.1", path = "../gix-fs" } +gix-path = { version = "^0.10.7", path = "../gix-path" } +gix-hash = { version = "^0.14.2", path = "../gix-hash" } +gix-date = { version = "^0.8.6", path = "../gix-date" } +gix-object = { version = "^0.42.2", path = "../gix-object" } +gix-utils = { version = "^0.1.11", path = "../gix-utils" } +gix-validate = { version = "^0.8.5", path = "../gix-validate" } +gix-actor = { version = "^0.31.2", path = "../gix-actor" } +gix-lock = { version = "^14.0.0", path = "../gix-lock" } +gix-tempfile = { version = "^14.0.0", default-features = false, path = "../gix-tempfile" } thiserror = "1.0.34" winnow = { version = "0.6.0", features = ["simd"] } diff --git a/gix-ref/fuzz/fuzz_targets/fuzz_log.rs b/gix-ref/fuzz/fuzz_targets/fuzz_log.rs index 423da5479de..204ab6e7ba2 100644 --- a/gix-ref/fuzz/fuzz_targets/fuzz_log.rs +++ b/gix-ref/fuzz/fuzz_targets/fuzz_log.rs @@ -20,10 +20,10 @@ fn fuzz(ctx: Ctx) -> Result<()> { let mut buf = [0u8; 1024]; let read = std::io::Cursor::new(ctx.multi_line_reverse); - let mut iter = gix_ref::file::log::iter::reverse(read, &mut buf)?; + let iter = gix_ref::file::log::iter::reverse(read, &mut buf)?; _ = black_box(iter.map(|x| black_box(x)).count()); - let mut iter = gix_ref::file::log::iter::forward(ctx.multi_line_forward); + let iter = gix_ref::file::log::iter::forward(ctx.multi_line_forward); _ = black_box(iter.map(|x| black_box(x)).count()); Ok(()) diff --git a/gix-ref/fuzz/fuzz_targets/fuzz_names.rs b/gix-ref/fuzz/fuzz_targets/fuzz_names.rs index 651a28fb23e..6729f2d5899 100644 --- a/gix-ref/fuzz/fuzz_targets/fuzz_names.rs +++ b/gix-ref/fuzz/fuzz_targets/fuzz_names.rs @@ -3,7 +3,7 @@ use anyhow::Result; use arbitrary::Arbitrary; use bstr::{BStr, BString}; -use gix_ref::{namespace::expand, FullName, FullNameRef, PartialName, PartialNameCow, PartialNameRef}; +use gix_ref::{namespace::expand, FullName, FullNameRef, PartialName}; use libfuzzer_sys::fuzz_target; use std::hint::black_box; diff --git a/gix-ref/src/fullname.rs b/gix-ref/src/fullname.rs index 5957943dc72..e74dd05dd04 100644 --- a/gix-ref/src/fullname.rs +++ b/gix-ref/src/fullname.rs @@ -1,4 +1,4 @@ -use std::{borrow::Borrow, convert::TryFrom, path::Path}; +use std::{borrow::Borrow, path::Path}; use gix_object::bstr::{BStr, BString, ByteSlice}; diff --git a/gix-ref/src/lib.rs b/gix-ref/src/lib.rs index 2a0b8987632..6059fd2d675 100644 --- a/gix-ref/src/lib.rs +++ b/gix-ref/src/lib.rs @@ -23,8 +23,6 @@ #![cfg_attr(all(doc, feature = "document-features"), feature(doc_cfg, doc_auto_cfg))] #![deny(missing_docs, rust_2018_idioms, unsafe_code)] -use std::borrow::Cow; - use gix_hash::{oid, ObjectId}; pub use gix_object::bstr; use gix_object::bstr::{BStr, BString}; @@ -35,10 +33,13 @@ pub use store_impl::{file, packed}; mod fullname; /// +#[allow(clippy::empty_docs)] pub mod name; /// +#[allow(clippy::empty_docs)] pub mod namespace; /// +#[allow(clippy::empty_docs)] pub mod transaction; mod parse; @@ -49,13 +50,35 @@ pub use raw::Reference; mod target; /// +#[allow(clippy::empty_docs)] pub mod log; /// +#[allow(clippy::empty_docs)] pub mod peel; /// +#[allow(clippy::empty_docs)] pub mod store { + /// + #[allow(clippy::empty_docs)] + pub mod init { + + /// Options for use during [initialization](crate::file::Store::at). + #[derive(Debug, Copy, Clone, Default)] + pub struct Options { + /// How to write the ref-log. + pub write_reflog: super::WriteReflog, + /// The kind of hash to expect in + pub object_hash: gix_hash::Kind, + /// The equivalent of `core.precomposeUnicode`. + pub precompose_unicode: bool, + /// If `true`, we will avoid reading from or writing to references that contains Windows device names + /// to avoid side effects. This only needs to be `true` on Windows, but can be `true` on other platforms + /// if they need to remain compatible with Windows. + pub prohibit_windows_device_names: bool, + } + } /// The way a file store handles the reflog #[derive(Default, Debug, PartialOrd, PartialEq, Ord, Eq, Hash, Clone, Copy)] pub enum WriteReflog { @@ -87,9 +110,8 @@ pub mod store { /// #[path = "general/handle/mod.rs"] mod handle; - pub use handle::find; - use crate::file; + pub use handle::find; } /// The git reference store. @@ -109,10 +131,6 @@ pub struct FullName(pub(crate) BString); #[repr(transparent)] pub struct FullNameRef(BStr); -/// A validated and potentially partial reference name, safe to use for common operations. -#[derive(PartialEq, Eq, Debug, Hash, Ord, PartialOrd, Clone)] -pub struct PartialNameCow<'a>(Cow<'a, BStr>); - /// A validated and potentially partial reference name, safe to use for common operations. #[derive(PartialEq, Eq, Debug, Hash, Ord, PartialOrd)] #[repr(transparent)] diff --git a/gix-ref/src/namespace.rs b/gix-ref/src/namespace.rs index 34040f606a0..1483b1c47e8 100644 --- a/gix-ref/src/namespace.rs +++ b/gix-ref/src/namespace.rs @@ -1,7 +1,4 @@ -use std::{ - convert::TryInto, - path::{Path, PathBuf}, -}; +use std::path::{Path, PathBuf}; use gix_object::bstr::{BStr, BString, ByteSlice, ByteVec}; diff --git a/gix-ref/src/peel.rs b/gix-ref/src/peel.rs index b119a907334..02bee162050 100644 --- a/gix-ref/src/peel.rs +++ b/gix-ref/src/peel.rs @@ -1,4 +1,5 @@ /// +#[allow(clippy::empty_docs)] pub mod to_id { use std::path::PathBuf; diff --git a/gix-ref/src/store/file/find.rs b/gix-ref/src/store/file/find.rs index 6bf7767043b..b148d3f2e73 100644 --- a/gix-ref/src/store/file/find.rs +++ b/gix-ref/src/store/file/find.rs @@ -1,6 +1,5 @@ use std::{ borrow::Cow, - convert::TryInto, io::{self, Read}, path::{Path, PathBuf}, }; @@ -252,8 +251,20 @@ impl file::Store { /// Read the file contents with a verified full reference path and return it in the given vector if possible. pub(crate) fn ref_contents(&self, name: &FullNameRef) -> io::Result>> { - let ref_path = self.reference_path(name); + let (base, relative_path) = self.reference_path_with_base(name); + if self.prohibit_windows_device_names + && relative_path + .components() + .filter_map(|c| gix_path::try_os_str_into_bstr(c.as_os_str().into()).ok()) + .any(|c| gix_validate::path::component_is_windows_device(c.as_ref())) + { + return Err(std::io::Error::new( + std::io::ErrorKind::Other, + format!("Illegal use of reserved Windows device name in \"{}\"", name.as_bstr()), + )); + } + let ref_path = base.join(relative_path); match std::fs::File::open(&ref_path) { Ok(mut file) => { let mut buf = Vec::with_capacity(128); @@ -271,9 +282,8 @@ impl file::Store { } /// +#[allow(clippy::empty_docs)] pub mod existing { - use std::convert::TryInto; - pub use error::Error; use crate::{ diff --git a/gix-ref/src/store/file/log/iter.rs b/gix-ref/src/store/file/log/iter.rs index d62df680014..e3147151513 100644 --- a/gix-ref/src/store/file/log/iter.rs +++ b/gix-ref/src/store/file/log/iter.rs @@ -8,6 +8,7 @@ use crate::{ }; /// +#[allow(clippy::empty_docs)] pub mod decode { use crate::store_impl::file::log; @@ -143,6 +144,7 @@ where } /// +#[allow(clippy::empty_docs)] pub mod reverse { use super::decode; diff --git a/gix-ref/src/store/file/log/line.rs b/gix-ref/src/store/file/log/line.rs index 01de31a7c60..7e43e5ebc5e 100644 --- a/gix-ref/src/store/file/log/line.rs +++ b/gix-ref/src/store/file/log/line.rs @@ -5,7 +5,7 @@ use crate::{log::Line, store_impl::file::log::LineRef}; impl<'a> LineRef<'a> { /// Convert this instance into its mutable counterpart pub fn to_owned(&self) -> Line { - self.clone().into() + (*self).into() } } @@ -72,6 +72,7 @@ impl<'a> From> for Line { } /// +#[allow(clippy::empty_docs)] pub mod decode { use gix_object::bstr::{BStr, ByteSlice}; use winnow::{ @@ -84,6 +85,7 @@ pub mod decode { use crate::{file::log::LineRef, parse::hex_hash}; /// + #[allow(clippy::empty_docs)] mod error { use gix_object::bstr::{BString, ByteSlice}; @@ -167,10 +169,8 @@ pub mod decode { #[cfg(test)] mod test { - use gix_date::{time::Sign, Time}; - use gix_object::bstr::ByteSlice; - use super::*; + use gix_date::{time::Sign, Time}; /// Convert a hexadecimal hash into its corresponding `ObjectId` or _panic_. fn hex_to_oid(hex: &str) -> gix_hash::ObjectId { diff --git a/gix-ref/src/store/file/log/mod.rs b/gix-ref/src/store/file/log/mod.rs index a9199be2b79..af62a97c634 100644 --- a/gix-ref/src/store/file/log/mod.rs +++ b/gix-ref/src/store/file/log/mod.rs @@ -3,13 +3,13 @@ use gix_object::bstr::BStr; pub use super::loose::reflog::{create_or_update, Error}; /// +#[allow(clippy::empty_docs)] pub mod iter; mod line; /// A parsed ref log line. -#[derive(PartialEq, Eq, Debug, Hash, Ord, PartialOrd, Clone)] +#[derive(PartialEq, Eq, Debug, Hash, Ord, PartialOrd, Clone, Copy)] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] -#[non_exhaustive] pub struct LineRef<'a> { /// The previous object id in hexadecimal. Use [`LineRef::previous_oid()`] to get a more usable form. pub previous_oid: &'a BStr, diff --git a/gix-ref/src/store/file/loose/mod.rs b/gix-ref/src/store/file/loose/mod.rs index 0e271247793..57c1317d8dd 100644 --- a/gix-ref/src/store/file/loose/mod.rs +++ b/gix-ref/src/store/file/loose/mod.rs @@ -17,11 +17,14 @@ impl Reference { } /// +#[allow(clippy::empty_docs)] pub(crate) mod reflog; /// +#[allow(clippy::empty_docs)] pub(crate) mod iter; /// +#[allow(clippy::empty_docs)] pub mod reference; mod init { @@ -32,15 +35,18 @@ mod init { impl file::Store { /// Create a new instance at the given `git_dir`, which commonly is a standard git repository with a /// `refs/` subdirectory. - /// The `object_hash` defines which kind of hash we should recognize. + /// Use [`Options`](crate::store::init::Options) to adjust settings. /// - /// Note that if `precompose_unicode` is set, the `git_dir` is also expected to use precomposed unicode, - /// or else some operations that strip prefixes will fail. + /// Note that if [`precompose_unicode`](crate::store::init::Options::precompose_unicode) is set in the options, + /// the `git_dir` is also expected to use precomposed unicode, or else some operations that strip prefixes will fail. pub fn at( git_dir: PathBuf, - write_reflog: file::WriteReflog, - object_hash: gix_hash::Kind, - precompose_unicode: bool, + crate::store::init::Options { + write_reflog, + object_hash, + precompose_unicode, + prohibit_windows_device_names, + }: crate::store::init::Options, ) -> Self { file::Store { git_dir, @@ -48,6 +54,7 @@ mod init { common_dir: None, write_reflog, namespace: None, + prohibit_windows_device_names, packed: gix_fs::SharedFileSnapshotMut::new().into(), object_hash, precompose_unicode, @@ -57,14 +64,17 @@ mod init { /// Like [`at()`][file::Store::at()], but for _linked_ work-trees which use `git_dir` as private ref store and `common_dir` for /// shared references. /// - /// Note that if `precompose_unicode` is set, the `git_dir` and `common_dir` are also expected to use precomposed unicode, - /// or else some operations that strip prefixes will fail. + /// Note that if [`precompose_unicode`](crate::store::init::Options::precompose_unicode) is set, the `git_dir` and + /// `common_dir` are also expected to use precomposed unicode, or else some operations that strip prefixes will fail. pub fn for_linked_worktree( git_dir: PathBuf, common_dir: PathBuf, - write_reflog: file::WriteReflog, - object_hash: gix_hash::Kind, - precompose_unicode: bool, + crate::store::init::Options { + write_reflog, + object_hash, + precompose_unicode, + prohibit_windows_device_names, + }: crate::store::init::Options, ) -> Self { file::Store { git_dir, @@ -72,6 +82,7 @@ mod init { common_dir: Some(common_dir), write_reflog, namespace: None, + prohibit_windows_device_names, packed: gix_fs::SharedFileSnapshotMut::new().into(), object_hash, precompose_unicode, diff --git a/gix-ref/src/store/file/loose/reference/decode.rs b/gix-ref/src/store/file/loose/reference/decode.rs index 7ba89837383..68fb4f65d70 100644 --- a/gix-ref/src/store/file/loose/reference/decode.rs +++ b/gix-ref/src/store/file/loose/reference/decode.rs @@ -1,5 +1,3 @@ -use std::convert::{TryFrom, TryInto}; - use gix_hash::ObjectId; use gix_object::bstr::BString; use winnow::{ diff --git a/gix-ref/src/store/file/loose/reference/mod.rs b/gix-ref/src/store/file/loose/reference/mod.rs index 3e5ce06838b..e79ff6b95bd 100644 --- a/gix-ref/src/store/file/loose/reference/mod.rs +++ b/gix-ref/src/store/file/loose/reference/mod.rs @@ -1,4 +1,5 @@ pub(crate) mod logiter; /// +#[allow(clippy::empty_docs)] pub mod decode; diff --git a/gix-ref/src/store/file/loose/reflog.rs b/gix-ref/src/store/file/loose/reflog.rs index 614dd59a9e0..cdf342bcb88 100644 --- a/gix-ref/src/store/file/loose/reflog.rs +++ b/gix-ref/src/store/file/loose/reflog.rs @@ -1,4 +1,4 @@ -use std::{convert::TryInto, io::Read, path::PathBuf}; +use std::{io::Read, path::PathBuf}; use crate::{ store_impl::{file, file::log}, @@ -84,6 +84,7 @@ impl file::Store { } /// +#[allow(clippy::empty_docs)] pub mod create_or_update { use std::{ borrow::Cow, diff --git a/gix-ref/src/store/file/loose/reflog/create_or_update/tests.rs b/gix-ref/src/store/file/loose/reflog/create_or_update/tests.rs index 4173b175875..45d74cead09 100644 --- a/gix-ref/src/store/file/loose/reflog/create_or_update/tests.rs +++ b/gix-ref/src/store/file/loose/reflog/create_or_update/tests.rs @@ -1,12 +1,9 @@ -use std::{convert::TryInto, path::Path}; - use gix_actor::Signature; use gix_date::{time::Sign, Time}; use gix_object::bstr::ByteSlice; use gix_testtools::tempfile::TempDir; use super::*; -use crate::{file::WriteReflog, FullNameRef}; type Result = std::result::Result>; @@ -17,7 +14,13 @@ fn hex_to_id(hex: &str) -> gix_hash::ObjectId { fn empty_store(writemode: WriteReflog) -> Result<(TempDir, file::Store)> { let dir = TempDir::new()?; - let store = file::Store::at(dir.path().into(), writemode, gix_hash::Kind::Sha1, false); + let store = file::Store::at( + dir.path().into(), + crate::store::init::Options { + write_reflog: writemode, + ..Default::default() + }, + ); Ok((dir, store)) } diff --git a/gix-ref/src/store/file/mod.rs b/gix-ref/src/store/file/mod.rs index 758dcb438cc..c01c9deb4d8 100644 --- a/gix-ref/src/store/file/mod.rs +++ b/gix-ref/src/store/file/mod.rs @@ -27,6 +27,9 @@ pub struct Store { pub write_reflog: WriteReflog, /// The namespace to use for edits and reads pub namespace: Option, + /// This is only needed on Windows, where some device names are reserved at any level of a path, so that + /// reading or writing `refs/heads/CON` for example would read from the console, or write to it. + pub prohibit_windows_device_names: bool, /// If set, we will convert decomposed unicode like `a\u308` into precomposed unicode like `ä` when reading /// ref names from disk. /// Note that this is an internal operation that isn't observable on the outside, but it's needed for lookups @@ -95,29 +98,36 @@ pub(in crate::store_impl::file) fn path_to_name<'a>(path: impl Into>; impl file::Store { - pub(crate) fn force_refresh_packed_buffer(&self) -> Result<(), packed::buffer::open::Error> { + /// Forcefully reload the packed refs buffer. + /// + /// This method should be used if it's clear that the buffer on disk has changed, to + /// make the latest changes visible before other operations are done on this instance. + /// + /// As some filesystems don't have nanosecond granularity, changes are likely to be missed + /// if they happen within one second otherwise. + pub fn force_refresh_packed_buffer(&self) -> Result<(), packed::buffer::open::Error> { self.packed.force_refresh(|| { let modified = self.packed_refs_path().metadata()?.modified()?; self.open_packed_buffer().map(|packed| Some(modified).zip(packed)) diff --git a/gix-ref/src/store/file/transaction/mod.rs b/gix-ref/src/store/file/transaction/mod.rs index f22493ad86e..7b1119fefd2 100644 --- a/gix-ref/src/store/file/transaction/mod.rs +++ b/gix-ref/src/store/file/transaction/mod.rs @@ -95,7 +95,9 @@ impl std::fmt::Debug for Transaction<'_, '_> { } /// +#[allow(clippy::empty_docs)] pub mod prepare; /// +#[allow(clippy::empty_docs)] pub mod commit; diff --git a/gix-ref/src/store/file/transaction/prepare.rs b/gix-ref/src/store/file/transaction/prepare.rs index 22a4c78b37d..79d86c6cbd5 100644 --- a/gix-ref/src/store/file/transaction/prepare.rs +++ b/gix-ref/src/store/file/transaction/prepare.rs @@ -51,7 +51,7 @@ impl<'s, 'p> Transaction<'s, 'p> { .map_err(Error::from), (None, None) => Ok(None), (maybe_loose, _) => Ok(maybe_loose), - }); + })?; let lock = match &mut change.update.change { Change::Delete { expected, .. } => { let (base, relative_path) = store.reference_path_with_base(change.update.name.as_ref()); @@ -70,7 +70,6 @@ impl<'s, 'p> Transaction<'s, 'p> { .into() }; - let existing_ref = existing_ref?; match (&expected, &existing_ref) { (PreviousValue::MustNotExist, _) => { panic!("BUG: MustNotExist constraint makes no sense if references are to be deleted") @@ -120,7 +119,6 @@ impl<'s, 'p> Transaction<'s, 'p> { }; let mut lock = (!has_global_lock).then(obtain_lock).transpose()?; - let existing_ref = existing_ref?; match (&expected, &existing_ref) { (PreviousValue::Any, _) | (PreviousValue::MustExist, Some(_)) @@ -287,8 +285,8 @@ impl<'s, 'p> Transaction<'s, 'p> { ..edit.update.clone() }); *num_updates += 1; + continue; } - continue; } match edit.update.change { Change::Update { diff --git a/gix-ref/src/store/general/handle/find.rs b/gix-ref/src/store/general/handle/find.rs index 2f11f17557d..40be732dbac 100644 --- a/gix-ref/src/store/general/handle/find.rs +++ b/gix-ref/src/store/general/handle/find.rs @@ -1,5 +1,3 @@ -use std::convert::TryInto; - use crate::{store, PartialNameRef, Reference}; mod error { @@ -57,8 +55,6 @@ mod existing { } } - use std::convert::TryInto; - pub use error::Error; use crate::{store, PartialNameRef, Reference}; diff --git a/gix-ref/src/store/general/handle/mod.rs b/gix-ref/src/store/general/handle/mod.rs index 44d9e060d81..50264084088 100644 --- a/gix-ref/src/store/general/handle/mod.rs +++ b/gix-ref/src/store/general/handle/mod.rs @@ -34,6 +34,7 @@ impl crate::Store { } /// +#[allow(clippy::empty_docs)] pub mod find; mod iter { diff --git a/gix-ref/src/store/general/init.rs b/gix-ref/src/store/general/init.rs index 6b5ee9e87f2..efe5dacfaf5 100644 --- a/gix-ref/src/store/general/init.rs +++ b/gix-ref/src/store/general/init.rs @@ -1,7 +1,5 @@ use std::path::PathBuf; -use crate::store::WriteReflog; - mod error { /// The error returned by [`crate::Store::at()`]. #[derive(Debug, thiserror::Error)] @@ -19,23 +17,16 @@ use crate::file; #[allow(dead_code)] impl crate::Store { /// Create a new store at the given location, typically the `.git/` directory. + /// Use [`opts`](crate::store::init::Options) to adjust settings. /// - /// `object_hash` defines the kind of hash to assume when dealing with refs. - /// `precompose_unicode` is used to set to the value of [`crate::file::Store::precompose_unicode]. - /// - /// Note that if `precompose_unicode` is set, the `git_dir` is also expected to use precomposed unicode, - /// or else some operations that strip prefixes will fail. - pub fn at( - git_dir: PathBuf, - reflog_mode: WriteReflog, - object_hash: gix_hash::Kind, - precompose_unicode: bool, - ) -> Result { + /// Note that if [`precompose_unicode`](crate::store::init::Options::precompose_unicode) is set in the options, + /// the `git_dir` is also expected to use precomposed unicode, or else some operations that strip prefixes will fail. + pub fn at(git_dir: PathBuf, opts: crate::store::init::Options) -> Result { // for now, just try to read the directory - later we will do that naturally as we have to figure out if it's a ref-table or not. std::fs::read_dir(&git_dir)?; Ok(crate::Store { inner: crate::store::State::Loose { - store: file::Store::at(git_dir, reflog_mode, object_hash, precompose_unicode), + store: file::Store::at(git_dir, opts), }, }) } diff --git a/gix-ref/src/store/mod.rs b/gix-ref/src/store/mod.rs index 6691098e211..d8e0a01c432 100644 --- a/gix-ref/src/store/mod.rs +++ b/gix-ref/src/store/mod.rs @@ -1,5 +1,7 @@ /// +#[allow(clippy::empty_docs)] pub mod file; /// +#[allow(clippy::empty_docs)] pub mod packed; diff --git a/gix-ref/src/store/packed/buffer.rs b/gix-ref/src/store/packed/buffer.rs index 40fbf88e24a..76e0f3bc422 100644 --- a/gix-ref/src/store/packed/buffer.rs +++ b/gix-ref/src/store/packed/buffer.rs @@ -16,10 +16,10 @@ impl AsRef<[u8]> for packed::Backing { } /// +#[allow(clippy::empty_docs)] pub mod open { use std::path::PathBuf; - use memmap2::Mmap; use winnow::{prelude::*, stream::Offset}; use crate::store_impl::packed; @@ -81,7 +81,7 @@ pub mod open { // SAFETY: we have to take the risk of somebody changing the file underneath. Git never writes into the same file. #[allow(unsafe_code)] unsafe { - Mmap::map(&std::fs::File::open(&path)?)? + memmap2::MmapOptions::new().map_copy_read_only(&std::fs::File::open(&path)?)? }, ) }; diff --git a/gix-ref/src/store/packed/decode.rs b/gix-ref/src/store/packed/decode.rs index b4f8e8562e6..84856af13d0 100644 --- a/gix-ref/src/store/packed/decode.rs +++ b/gix-ref/src/store/packed/decode.rs @@ -1,5 +1,3 @@ -use std::convert::TryInto; - use gix_object::bstr::{BStr, ByteSlice}; use winnow::{ combinator::{delimited, opt, preceded, terminated}, diff --git a/gix-ref/src/store/packed/find.rs b/gix-ref/src/store/packed/find.rs index 002f76b0f50..b2c2c7565a2 100644 --- a/gix-ref/src/store/packed/find.rs +++ b/gix-ref/src/store/packed/find.rs @@ -1,5 +1,3 @@ -use std::convert::TryInto; - use gix_object::bstr::{BStr, BString, ByteSlice}; use winnow::prelude::*; @@ -131,6 +129,7 @@ mod error { pub use error::Error; /// +#[allow(clippy::empty_docs)] pub mod existing { /// The error returned by [`find_existing()`][super::packed::Buffer::find()] diff --git a/gix-ref/src/store/packed/mod.rs b/gix-ref/src/store/packed/mod.rs index 12aee9f815f..59a7ab02d24 100644 --- a/gix-ref/src/store/packed/mod.rs +++ b/gix-ref/src/store/packed/mod.rs @@ -84,13 +84,17 @@ pub struct Iter<'a> { mod decode; /// +#[allow(clippy::empty_docs)] pub mod iter; /// +#[allow(clippy::empty_docs)] pub mod buffer; /// +#[allow(clippy::empty_docs)] pub mod find; /// +#[allow(clippy::empty_docs)] pub mod transaction; diff --git a/gix-ref/src/store/packed/transaction.rs b/gix-ref/src/store/packed/transaction.rs index e03f9b987da..8c615a10d4b 100644 --- a/gix-ref/src/store/packed/transaction.rs +++ b/gix-ref/src/store/packed/transaction.rs @@ -273,6 +273,7 @@ pub(crate) fn buffer_into_transaction( } /// +#[allow(clippy::empty_docs)] pub mod prepare { /// The error used in [`Transaction::prepare(…)`][crate::file::Transaction::prepare()]. #[derive(Debug, thiserror::Error)] @@ -286,6 +287,7 @@ pub mod prepare { } /// +#[allow(clippy::empty_docs)] pub mod commit { use crate::store_impl::packed; diff --git a/gix-ref/src/target.rs b/gix-ref/src/target.rs index 6d4f69991dd..15d1e3fc0e9 100644 --- a/gix-ref/src/target.rs +++ b/gix-ref/src/target.rs @@ -1,4 +1,4 @@ -use std::{convert::TryFrom, fmt}; +use std::fmt; use gix_hash::{oid, ObjectId}; diff --git a/gix-ref/tests/file/mod.rs b/gix-ref/tests/file/mod.rs index 3f932075af8..9c2aa843418 100644 --- a/gix-ref/tests/file/mod.rs +++ b/gix-ref/tests/file/mod.rs @@ -15,26 +15,13 @@ pub fn store_with_packed_refs() -> crate::Result { pub fn store_at(name: &str) -> crate::Result { let path = gix_testtools::scripted_fixture_read_only_standalone(name)?; - Ok(Store::at( - path.join(".git"), - gix_ref::store::WriteReflog::Normal, - gix_hash::Kind::Sha1, - false, - )) + Ok(Store::at(path.join(".git"), Default::default())) } fn store_writable(name: &str) -> crate::Result<(gix_testtools::tempfile::TempDir, Store)> { let dir = gix_testtools::scripted_fixture_writable_standalone(name)?; let git_dir = dir.path().join(".git"); - Ok(( - dir, - Store::at( - git_dir, - gix_ref::store::WriteReflog::Normal, - gix_hash::Kind::Sha1, - false, - ), - )) + Ok((dir, Store::at(git_dir, Default::default()))) } struct EmptyCommit; diff --git a/gix-ref/tests/file/store/find.rs b/gix-ref/tests/file/store/find.rs index b4003e3fc62..2343b33fb42 100644 --- a/gix-ref/tests/file/store/find.rs +++ b/gix-ref/tests/file/store/find.rs @@ -1,8 +1,4 @@ mod existing { - use std::convert::{TryFrom, TryInto}; - - use gix_ref::{PartialName, PartialNameRef}; - use crate::{file::store_at, hex_to_id}; #[test] @@ -15,12 +11,41 @@ mod existing { Ok(()) } - // TODO: figure this out - #[test] - fn possible_inputs() -> crate::Result { - let store = crate::file::store()?; - store.find_loose("dt1")?; - store.find_loose(&String::from("dt1"))?; // Owned Strings don't have an impl for PartialName + mod convert { + use gix_ref::{PartialName, PartialNameRef}; + + // TODO: figure this out + #[test] + fn possible_inputs() -> crate::Result { + let store = crate::file::store()?; + store.find_loose("dt1")?; + store.find_loose(&String::from("dt1"))?; // Owned Strings don't have an impl for PartialName + + store.find_loose(&CustomType("dt1".into()))?; + + let name = CustomName { + remote: "origin", + branch: "main", + }; + store.find_loose(&name.to_partial_name())?; + // TODO: this effectively needs a `Cow<'_, PartialNameRef>`, but we are not allowed to implement conversions for it. + // After having been there, I don't want to have a `PartialNameCow(Cow<'_, PartialNameRef)` anymore, nor + // copies of `TryFrom/TryInto` traits in our crate. + // Make it work once we can implement standard traits for Cow. + // store.find_loose(&name)?; + // store.find_loose(name.to_partial_name())?; + store.find_loose(&name.to_partial_name_from_string())?; + store.find_loose(&name.to_partial_name_from_bstring())?; + store.find_loose(&name.to_full_name())?; + store.find_loose(name.to_full_name().as_ref())?; + store.find_loose(name.to_full_name().as_ref().as_partial_name())?; + store.find_loose(&PartialName::try_from(name.remote)?.join(name.branch.into())?)?; + store.find_loose(&PartialName::try_from("origin")?.join("main".into())?)?; + store.find_loose(&PartialName::try_from("origin")?.join(String::from("main").as_str().into())?)?; + store.find_loose(&PartialName::try_from("origin")?.join("main".into())?)?; + + Ok(()) + } struct CustomType(String); impl<'a> TryFrom<&'a CustomType> for &'a PartialNameRef { @@ -30,7 +55,6 @@ mod existing { value.0.as_str().try_into() } } - store.find_loose(&CustomType("dt1".into()))?; struct CustomName { remote: &'static str, @@ -63,29 +87,6 @@ mod existing { PartialName::try_from(value.to_partial_name()) } } - - let name = CustomName { - remote: "origin", - branch: "main", - }; - store.find_loose(&name.to_partial_name())?; - // TODO: this effectively needs a `Cow<'_, PartialNameRef>`, but we are not allowed to implement conversions for it. - // After having been there, I don't want to have a `PartialNameCow(Cow<'_, PartialNameRef)` anymore, nor - // copies of `TryFrom/TryInto` traits in our crate. - // Make it work once we can implement standard traits for Cow. - // store.find_loose(&name)?; - // store.find_loose(name.to_partial_name())?; - store.find_loose(&name.to_partial_name_from_string())?; - store.find_loose(&name.to_partial_name_from_bstring())?; - store.find_loose(&name.to_full_name())?; - store.find_loose(name.to_full_name().as_ref())?; - store.find_loose(name.to_full_name().as_ref().as_partial_name())?; - store.find_loose(&PartialName::try_from(name.remote)?.join(name.branch.into())?)?; - store.find_loose(&PartialName::try_from("origin")?.join("main".into())?)?; - store.find_loose(&PartialName::try_from("origin")?.join(String::from("main").as_str().into())?)?; - store.find_loose(&PartialName::try_from("origin")?.join("main".into())?)?; - - Ok(()) } } diff --git a/gix-ref/tests/file/store/iter.rs b/gix-ref/tests/file/store/iter.rs index 4fa6b8e54db..0ed5a971843 100644 --- a/gix-ref/tests/file/store/iter.rs +++ b/gix-ref/tests/file/store/iter.rs @@ -1,5 +1,3 @@ -use std::convert::TryInto; - use gix_object::bstr::ByteSlice; use crate::{ diff --git a/gix-ref/tests/file/store/mod.rs b/gix-ref/tests/file/store/mod.rs index a3e7db39164..4cfd6120b03 100644 --- a/gix-ref/tests/file/store/mod.rs +++ b/gix-ref/tests/file/store/mod.rs @@ -21,9 +21,11 @@ fn precompose_unicode_journey() -> crate::Result { let store_decomposed = gix_ref::file::Store::at( root, - WriteReflog::Always, - gix_hash::Kind::Sha1, - false, /* precompose_unicode */ + gix_ref::store::init::Options { + write_reflog: WriteReflog::Always, + precompose_unicode: false, + ..Default::default() + }, ); assert!(!store_decomposed.precompose_unicode); @@ -46,9 +48,11 @@ fn precompose_unicode_journey() -> crate::Result { let store_precomposed = gix_ref::file::Store::at( tmp.path().join(precomposed_a), // it's important that root paths are also precomposed then. - WriteReflog::Always, - gix_hash::Kind::Sha1, - true, /* precompose_unicode */ + gix_ref::store::init::Options { + write_reflog: WriteReflog::Always, + precompose_unicode: true, + ..Default::default() + }, ); let precomposed_ref = format!("refs/heads/{precomposed_a}"); diff --git a/gix-ref/tests/file/store/reflog.rs b/gix-ref/tests/file/store/reflog.rs index 27ffdd82630..a0a2ad3b124 100644 --- a/gix-ref/tests/file/store/reflog.rs +++ b/gix-ref/tests/file/store/reflog.rs @@ -1,9 +1,10 @@ fn store() -> crate::Result { Ok(crate::file::Store::at( gix_testtools::scripted_fixture_read_only_standalone("make_repo_for_reflog.sh")?.join(".git"), - gix_ref::store::WriteReflog::Disable, - gix_hash::Kind::Sha1, - false, + gix_ref::store::init::Options { + write_reflog: gix_ref::store::WriteReflog::Disable, + ..Default::default() + }, )) } diff --git a/gix-ref/tests/file/transaction/mod.rs b/gix-ref/tests/file/transaction/mod.rs index aafdaeba66f..348f76df6e7 100644 --- a/gix-ref/tests/file/transaction/mod.rs +++ b/gix-ref/tests/file/transaction/mod.rs @@ -1,6 +1,4 @@ pub(crate) mod prepare_and_commit { - use std::convert::TryInto; - use gix_date::{time::Sign, Time}; use gix_hash::ObjectId; use gix_object::bstr::BString; @@ -24,12 +22,7 @@ pub(crate) mod prepare_and_commit { pub(crate) fn empty_store() -> crate::Result<(gix_testtools::tempfile::TempDir, file::Store)> { let dir = gix_testtools::tempfile::TempDir::new().unwrap(); - let store = file::Store::at( - dir.path().into(), - gix_ref::store::WriteReflog::Normal, - gix_hash::Kind::Sha1, - false, - ); + let store = file::Store::at(dir.path().into(), Default::default()); Ok((dir, store)) } diff --git a/gix-ref/tests/file/transaction/prepare_and_commit/create_or_update/collisions.rs b/gix-ref/tests/file/transaction/prepare_and_commit/create_or_update/collisions.rs index 97cdb1af335..5ef7a0ad1eb 100644 --- a/gix-ref/tests/file/transaction/prepare_and_commit/create_or_update/collisions.rs +++ b/gix-ref/tests/file/transaction/prepare_and_commit/create_or_update/collisions.rs @@ -1,5 +1,3 @@ -use std::convert::TryInto; - use gix_lock::acquire::Fail; use gix_ref::{ file::transaction::PackedRefs, diff --git a/gix-ref/tests/file/transaction/prepare_and_commit/create_or_update/mod.rs b/gix-ref/tests/file/transaction/prepare_and_commit/create_or_update/mod.rs index bbc42d54d85..6c71b98ca76 100644 --- a/gix-ref/tests/file/transaction/prepare_and_commit/create_or_update/mod.rs +++ b/gix-ref/tests/file/transaction/prepare_and_commit/create_or_update/mod.rs @@ -1,5 +1,3 @@ -use std::convert::TryInto; - use gix_hash::ObjectId; use gix_lock::acquire::Fail; use gix_object::bstr::{BString, ByteSlice}; @@ -12,6 +10,7 @@ use gix_ref::{ transaction::{Change, LogChange, PreviousValue, RefEdit, RefLog}, Target, }; +use std::error::Error; use crate::{ file::{ @@ -432,6 +431,63 @@ fn symbolic_reference_writes_reflog_if_previous_value_is_set() -> crate::Result Ok(()) } +#[test] +fn windows_device_name_is_illegal_with_enabled_windows_protections() -> crate::Result { + let (_keep, mut store) = empty_store()?; + store.prohibit_windows_device_names = true; + let log_ignored = LogChange { + mode: RefLog::AndReference, + force_create_reflog: false, + message: "ignored".into(), + }; + + let new = Target::Peeled(hex_to_id("28ce6a8b26aa170e1de65536fe8abe1832bd3242")); + for invalid_name in ["refs/heads/CON", "refs/CON/still-invalid"] { + let err = store + .transaction() + .prepare( + Some(RefEdit { + change: Change::Update { + log: log_ignored.clone(), + new: new.clone(), + expected: PreviousValue::Any, + }, + name: invalid_name.try_into()?, + deref: false, + }), + Fail::Immediately, + Fail::Immediately, + ) + .unwrap_err(); + assert_eq!( + err.source().expect("inner").to_string(), + format!("Illegal use of reserved Windows device name in \"{invalid_name}\""), + "it's notable that the check also kicks in when the previous value doesn't matter - we expect a 'read' to happen anyway \ + - it can't be optimized away as the previous value is stored in the transaction result right now." + ); + } + + #[cfg(not(windows))] + { + store.prohibit_windows_device_names = false; + let _prepared_transaction = store.transaction().prepare( + Some(RefEdit { + change: Change::Update { + log: log_ignored.clone(), + new, + expected: PreviousValue::Any, + }, + name: "refs/heads/CON".try_into()?, + deref: false, + }), + Fail::Immediately, + Fail::Immediately, + )?; + } + + Ok(()) +} + #[test] fn symbolic_head_missing_referent_then_update_referent() -> crate::Result { for reflog_writemode in &[WriteReflog::Normal, WriteReflog::Disable, WriteReflog::Always] { @@ -815,3 +871,39 @@ fn packed_refs_creation_with_packed_refs_mode_leave_keeps_original_loose_refs() ); Ok(()) } + +#[test] +fn packed_refs_deletion_in_deletions_and_updates_mode() -> crate::Result { + let (_keep, store) = store_writable("make_packed_ref_repository.sh")?; + assert!( + store.try_find_loose("refs/heads/d1")?.is_none(), + "no loose d1 available, it's packed" + ); + let odb = gix_odb::at(store.git_dir().join("objects"))?; + let old_id = hex_to_id("134385f6d781b7e97062102c6a483440bfda2a03"); + let edits = store + .transaction() + .packed_refs(PackedRefs::DeletionsAndNonSymbolicUpdates(Box::new(odb))) + .prepare( + Some(RefEdit { + change: Change::Delete { + expected: PreviousValue::MustExistAndMatch(Target::Peeled(old_id)), + log: RefLog::AndReference, + }, + name: "refs/heads/d1".try_into()?, + deref: false, + }), + Fail::Immediately, + Fail::Immediately, + )? + .commit(committer().to_ref())?; + + assert_eq!(edits.len(), 1, "only one edit was performed in the packed refs store"); + + let packed = store.open_packed_buffer().unwrap().expect("packed refs is available"); + assert!( + packed.try_find("refs/heads/d1")?.is_none(), + "d1 should be removed from packed refs" + ); + Ok(()) +} diff --git a/gix-ref/tests/file/transaction/prepare_and_commit/delete.rs b/gix-ref/tests/file/transaction/prepare_and_commit/delete.rs index 41114643df2..c4a8c94c4fd 100644 --- a/gix-ref/tests/file/transaction/prepare_and_commit/delete.rs +++ b/gix-ref/tests/file/transaction/prepare_and_commit/delete.rs @@ -1,5 +1,3 @@ -use std::convert::TryInto; - use gix_lock::acquire::Fail; use gix_ref::{ file::ReferenceExt, diff --git a/gix-ref/tests/file/worktree.rs b/gix-ref/tests/file/worktree.rs index e9ee71edbe6..7b723eca107 100644 --- a/gix-ref/tests/file/worktree.rs +++ b/gix-ref/tests/file/worktree.rs @@ -29,7 +29,7 @@ fn main_store( let (dir, tmp) = dir(packed, writable)?; let git_dir = dir.join("repo").join(".git"); Ok(( - gix_ref::file::Store::at(git_dir.clone(), Default::default(), Default::default(), false), + gix_ref::file::Store::at(git_dir.clone(), Default::default()), gix_odb::at(git_dir.join("objects"))?, tmp, )) @@ -50,13 +50,7 @@ fn worktree_store( .into_repository_and_work_tree_directories(); let common_dir = git_dir.join("../.."); Ok(( - gix_ref::file::Store::for_linked_worktree( - git_dir, - common_dir.clone(), - Default::default(), - Default::default(), - false, - ), + gix_ref::file::Store::for_linked_worktree(git_dir, common_dir.clone(), Default::default()), gix_odb::at(common_dir.join("objects"))?, tmp, )) @@ -195,8 +189,6 @@ mod read_only { } mod writable { - use std::convert::TryInto; - use gix_lock::acquire::Fail; use gix_ref::{ file::{transaction::PackedRefs, Store}, diff --git a/gix-ref/tests/fixtures/generated-archives/make_namespaced_packed_ref_repository.tar.xz b/gix-ref/tests/fixtures/generated-archives/make_namespaced_packed_ref_repository.tar.xz index a7f20905683..fc978af675a 100644 Binary files a/gix-ref/tests/fixtures/generated-archives/make_namespaced_packed_ref_repository.tar.xz and b/gix-ref/tests/fixtures/generated-archives/make_namespaced_packed_ref_repository.tar.xz differ diff --git a/gix-ref/tests/fixtures/generated-archives/make_packed_ref_repository.tar.xz b/gix-ref/tests/fixtures/generated-archives/make_packed_ref_repository.tar.xz index 9712e27bf13..b6b0a26f118 100644 Binary files a/gix-ref/tests/fixtures/generated-archives/make_packed_ref_repository.tar.xz and b/gix-ref/tests/fixtures/generated-archives/make_packed_ref_repository.tar.xz differ diff --git a/gix-ref/tests/fixtures/generated-archives/make_packed_ref_repository_for_overlay.tar.xz b/gix-ref/tests/fixtures/generated-archives/make_packed_ref_repository_for_overlay.tar.xz index 90e67696768..9854c8a8585 100644 Binary files a/gix-ref/tests/fixtures/generated-archives/make_packed_ref_repository_for_overlay.tar.xz and b/gix-ref/tests/fixtures/generated-archives/make_packed_ref_repository_for_overlay.tar.xz differ diff --git a/gix-ref/tests/fixtures/generated-archives/make_packed_refs_for_lookup_rules.tar.xz b/gix-ref/tests/fixtures/generated-archives/make_packed_refs_for_lookup_rules.tar.xz index f40c6380881..d0d97268a46 100644 Binary files a/gix-ref/tests/fixtures/generated-archives/make_packed_refs_for_lookup_rules.tar.xz and b/gix-ref/tests/fixtures/generated-archives/make_packed_refs_for_lookup_rules.tar.xz differ diff --git a/gix-ref/tests/fixtures/generated-archives/make_ref_repository.tar.xz b/gix-ref/tests/fixtures/generated-archives/make_ref_repository.tar.xz index 9e647f907bf..219c6d3262e 100644 Binary files a/gix-ref/tests/fixtures/generated-archives/make_ref_repository.tar.xz and b/gix-ref/tests/fixtures/generated-archives/make_ref_repository.tar.xz differ diff --git a/gix-ref/tests/fixtures/generated-archives/make_repo_for_reflog.tar.xz b/gix-ref/tests/fixtures/generated-archives/make_repo_for_reflog.tar.xz index 63747b9818e..94b394600e9 100644 Binary files a/gix-ref/tests/fixtures/generated-archives/make_repo_for_reflog.tar.xz and b/gix-ref/tests/fixtures/generated-archives/make_repo_for_reflog.tar.xz differ diff --git a/gix-ref/tests/fixtures/generated-archives/make_repository_with_lots_of_packed_refs.tar.xz b/gix-ref/tests/fixtures/generated-archives/make_repository_with_lots_of_packed_refs.tar.xz index 24cc086941d..d9a682025f6 100644 Binary files a/gix-ref/tests/fixtures/generated-archives/make_repository_with_lots_of_packed_refs.tar.xz and b/gix-ref/tests/fixtures/generated-archives/make_repository_with_lots_of_packed_refs.tar.xz differ diff --git a/gix-ref/tests/fixtures/make_namespaced_packed_ref_repository.sh b/gix-ref/tests/fixtures/make_namespaced_packed_ref_repository.sh index 27477427b48..7c028c42718 100755 --- a/gix-ref/tests/fixtures/make_namespaced_packed_ref_repository.sh +++ b/gix-ref/tests/fixtures/make_namespaced_packed_ref_repository.sh @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash set -eu -o pipefail git init -q diff --git a/gix-ref/tests/fixtures/make_packed_ref_repository.sh b/gix-ref/tests/fixtures/make_packed_ref_repository.sh index 3a4077b7af8..daea997523b 100755 --- a/gix-ref/tests/fixtures/make_packed_ref_repository.sh +++ b/gix-ref/tests/fixtures/make_packed_ref_repository.sh @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash set -eu -o pipefail git init -q diff --git a/gix-ref/tests/fixtures/make_packed_ref_repository_for_overlay.sh b/gix-ref/tests/fixtures/make_packed_ref_repository_for_overlay.sh index 8f1365b7a07..9410375c73d 100755 --- a/gix-ref/tests/fixtures/make_packed_ref_repository_for_overlay.sh +++ b/gix-ref/tests/fixtures/make_packed_ref_repository_for_overlay.sh @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash set -eu -o pipefail git init -q diff --git a/gix-ref/tests/fixtures/make_packed_refs_for_lookup_rules.sh b/gix-ref/tests/fixtures/make_packed_refs_for_lookup_rules.sh index 2262c29c8fb..9d6cbeaa137 100755 --- a/gix-ref/tests/fixtures/make_packed_refs_for_lookup_rules.sh +++ b/gix-ref/tests/fixtures/make_packed_refs_for_lookup_rules.sh @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash set -eu -o pipefail git init -q diff --git a/gix-ref/tests/fixtures/make_ref_repository.sh b/gix-ref/tests/fixtures/make_ref_repository.sh index fd2b5b375f3..7288ffe59f0 100755 --- a/gix-ref/tests/fixtures/make_ref_repository.sh +++ b/gix-ref/tests/fixtures/make_ref_repository.sh @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash set -eu -o pipefail git init -q diff --git a/gix-ref/tests/fixtures/make_repo_for_reflog.sh b/gix-ref/tests/fixtures/make_repo_for_reflog.sh index f81bd784764..f96c4dde8f9 100755 --- a/gix-ref/tests/fixtures/make_repo_for_reflog.sh +++ b/gix-ref/tests/fixtures/make_repo_for_reflog.sh @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash set -eu -o pipefail git init -q diff --git a/gix-ref/tests/fixtures/make_repository_with_lots_of_packed_refs.sh b/gix-ref/tests/fixtures/make_repository_with_lots_of_packed_refs.sh index 63ff797ef94..bf210b84a3f 100755 --- a/gix-ref/tests/fixtures/make_repository_with_lots_of_packed_refs.sh +++ b/gix-ref/tests/fixtures/make_repository_with_lots_of_packed_refs.sh @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash set -eu -o pipefail git init -q diff --git a/gix-ref/tests/fixtures/make_worktree_repo.sh b/gix-ref/tests/fixtures/make_worktree_repo.sh index 209cfad9cfd..960869ebb40 100755 --- a/gix-ref/tests/fixtures/make_worktree_repo.sh +++ b/gix-ref/tests/fixtures/make_worktree_repo.sh @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash set -eu -o pipefail mkdir repo && cd repo diff --git a/gix-ref/tests/fullname/mod.rs b/gix-ref/tests/fullname/mod.rs index 94c99d9a483..5cb5b07f5a9 100644 --- a/gix-ref/tests/fullname/mod.rs +++ b/gix-ref/tests/fullname/mod.rs @@ -1,4 +1,4 @@ -use std::{borrow::Cow, convert::TryInto}; +use std::borrow::Cow; use gix_ref::{Category, FullNameRef, PartialNameRef}; diff --git a/gix-ref/tests/packed/find.rs b/gix-ref/tests/packed/find.rs index f8146b66579..9cc6753c6c4 100644 --- a/gix-ref/tests/packed/find.rs +++ b/gix-ref/tests/packed/find.rs @@ -1,5 +1,3 @@ -use std::convert::{TryFrom, TryInto}; - use gix_ref::packed; use gix_testtools::fixture_path_standalone; diff --git a/gix-ref/tests/packed/iter.rs b/gix-ref/tests/packed/iter.rs index 3a546340695..28d15ab1ca3 100644 --- a/gix-ref/tests/packed/iter.rs +++ b/gix-ref/tests/packed/iter.rs @@ -1,5 +1,3 @@ -use std::convert::TryInto; - use gix_object::bstr::ByteSlice; use gix_ref::packed; diff --git a/gix-ref/tests/reference/mod.rs b/gix-ref/tests/reference/mod.rs index d93729473a5..9662462818d 100644 --- a/gix-ref/tests/reference/mod.rs +++ b/gix-ref/tests/reference/mod.rs @@ -1,5 +1,3 @@ -use std::convert::TryInto; - use gix_ref::{FullName, Target}; #[test] diff --git a/gix-ref/tests/store/mod.rs b/gix-ref/tests/store/mod.rs index d6263358735..9f6ae14b7d9 100644 --- a/gix-ref/tests/store/mod.rs +++ b/gix-ref/tests/store/mod.rs @@ -1,12 +1,15 @@ #[test] -#[cfg(feature = "internal-testing-gix-features-parallel")] +#[cfg(feature = "gix-features-parallel")] fn is_send_and_sync() { pub fn store_at(name: &str) -> crate::Result { let path = gix_testtools::scripted_fixture_read_only_standalone(name)?; Ok(gix_ref::file::Store::at( path.join(".git"), - gix_ref::store::WriteReflog::Normal, - gix_hash::Kind::Sha1, + gix_ref::store::init::Options { + write_reflog: gix_ref::store::WriteReflog::Normal, + object_hash: gix_hash::Kind::Sha1, + ..Default::default() + }, )) } diff --git a/gix-ref/tests/transaction/mod.rs b/gix-ref/tests/transaction/mod.rs index 59766eb1802..64d7627c0dd 100644 --- a/gix-ref/tests/transaction/mod.rs +++ b/gix-ref/tests/transaction/mod.rs @@ -1,5 +1,5 @@ mod refedit_ext { - use std::{cell::RefCell, collections::BTreeMap, convert::TryInto}; + use std::{cell::RefCell, collections::BTreeMap}; use gix_object::bstr::{BString, ByteSlice}; use gix_ref::{ @@ -96,7 +96,7 @@ mod refedit_ext { } mod splitting { - use std::{cell::Cell, convert::TryInto}; + use std::cell::Cell; use gix_ref::{ transaction::{Change, LogChange, PreviousValue, RefEdit, RefEditsExt, RefLog}, diff --git a/gix-refspec/CHANGELOG.md b/gix-refspec/CHANGELOG.md index b1eb083982b..1540258a60e 100644 --- a/gix-refspec/CHANGELOG.md +++ b/gix-refspec/CHANGELOG.md @@ -5,6 +5,31 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## 0.23.0 (2024-03-14) + +A maintenance release without user-facing changes. + +### Commit Statistics + + + + - 3 commits contributed to the release over the course of 4 calendar days. + - 18 days passed between releases. + - 0 commits were understood as [conventional](https://www.conventionalcommits.org). + - 0 issues like '(#ID)' were seen in commit messages + +### Commit Details + + + +
view details + + * **Uncategorized** + - Prepare changelogs prior to release ([`52c3bbd`](https://github.com/Byron/gitoxide/commit/52c3bbd36b9e94a0f3a78b4ada84d0c08eba27f6)) + - Merge branch 'status' ([`3e5c974`](https://github.com/Byron/gitoxide/commit/3e5c974dd62ac134711c6c2f5a5490187a6ea55e)) + - Fix lints for nightly, and clippy ([`f8ce3d0`](https://github.com/Byron/gitoxide/commit/f8ce3d0721b6a53713a9392f2451874f520bc44c)) +
+ ## 0.22.1 (2024-02-25) A maintenance release without user-facing changes. @@ -13,7 +38,7 @@ A maintenance release without user-facing changes. - - 4 commits contributed to the release over the course of 16 calendar days. + - 5 commits contributed to the release over the course of 16 calendar days. - 36 days passed between releases. - 0 commits were understood as [conventional](https://www.conventionalcommits.org). - 0 issues like '(#ID)' were seen in commit messages @@ -31,6 +56,7 @@ A maintenance release without user-facing changes.
view details * **Uncategorized** + - Release gix-date v0.8.4, gix-utils v0.1.10, gix-actor v0.30.1, gix-object v0.41.1, gix-path v0.10.6, gix-glob v0.16.1, gix-quote v0.4.11, gix-attributes v0.22.1, gix-command v0.3.5, gix-filter v0.10.0, gix-commitgraph v0.24.1, gix-worktree-stream v0.10.0, gix-archive v0.10.0, gix-config-value v0.14.5, gix-ref v0.42.0, gix-sec v0.10.5, gix-config v0.35.0, gix-prompt v0.8.3, gix-url v0.27.1, gix-credentials v0.24.1, gix-ignore v0.11.1, gix-index v0.30.0, gix-worktree v0.31.0, gix-diff v0.41.0, gix-discover v0.30.0, gix-pathspec v0.7.0, gix-dir v0.1.0, gix-pack v0.48.0, gix-odb v0.58.0, gix-transport v0.41.1, gix-protocol v0.44.1, gix-revision v0.26.1, gix-refspec v0.22.1, gix-status v0.6.0, gix-submodule v0.9.0, gix-worktree-state v0.8.0, gix v0.59.0, gix-fsck v0.3.0, gitoxide-core v0.36.0, gitoxide v0.34.0, safety bump 10 crates ([`45b4470`](https://github.com/Byron/gitoxide/commit/45b447045bc826f252129c300c531acde2652c64)) - Prepare changelogs prior to release ([`f2e111f`](https://github.com/Byron/gitoxide/commit/f2e111f768fc1bc6182355261c20b63610cffec7)) - Merge branch 'dirwalk' ([`face359`](https://github.com/Byron/gitoxide/commit/face359443ba33e8985ec1525d5ec38b743ea7a9)) - Adjust gitignore files with precious declarations ([`ae86a6a`](https://github.com/Byron/gitoxide/commit/ae86a6a206074b85ff1eba32aea9c8b40c087b17)) diff --git a/gix-refspec/Cargo.toml b/gix-refspec/Cargo.toml index d27393d3cdb..5f0740e7687 100644 --- a/gix-refspec/Cargo.toml +++ b/gix-refspec/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "gix-refspec" -version = "0.22.1" +version = "0.23.0" repository = "https://github.com/Byron/gitoxide" license = "MIT OR Apache-2.0" description = "A crate of the gitoxide project for parsing and representing refspecs" @@ -13,9 +13,9 @@ rust-version = "1.65" doctest = false [dependencies] -gix-revision = { version = "^0.26.1", path = "../gix-revision", default-features = false } -gix-validate = { version = "^0.8.3", path = "../gix-validate" } -gix-hash = { version = "^0.14.1", path = "../gix-hash" } +gix-revision = { version = "^0.27.0", path = "../gix-revision", default-features = false } +gix-validate = { version = "^0.8.4", path = "../gix-validate" } +gix-hash = { version = "^0.14.2", path = "../gix-hash" } bstr = { version = "1.3.0", default-features = false, features = ["std"]} thiserror = "1.0.26" diff --git a/gix-refspec/src/lib.rs b/gix-refspec/src/lib.rs index 54d5f3057a9..ec5b4afca80 100644 --- a/gix-refspec/src/lib.rs +++ b/gix-refspec/src/lib.rs @@ -3,10 +3,12 @@ #![forbid(unsafe_code)] /// +#[allow(clippy::empty_docs)] pub mod parse; pub use parse::function::parse; /// +#[allow(clippy::empty_docs)] pub mod instruction; /// A refspec with references to the memory it was parsed from. @@ -32,6 +34,7 @@ mod spec; mod write; /// +#[allow(clippy::empty_docs)] pub mod match_group; pub use match_group::types::MatchGroup; diff --git a/gix-refspec/src/match_group/mod.rs b/gix-refspec/src/match_group/mod.rs index 2d859cde82f..da5b0f54165 100644 --- a/gix-refspec/src/match_group/mod.rs +++ b/gix-refspec/src/match_group/mod.rs @@ -6,6 +6,7 @@ pub(crate) mod types; pub use types::{Item, Mapping, Outcome, Source, SourceRef}; /// +#[allow(clippy::empty_docs)] pub mod validate; /// Initialization diff --git a/gix-refspec/tests/fixtures/generated-archives/parse_baseline.tar.xz b/gix-refspec/tests/fixtures/generated-archives/parse_baseline.tar.xz index 07862ade181..9751ce7c3cb 100644 Binary files a/gix-refspec/tests/fixtures/generated-archives/parse_baseline.tar.xz and b/gix-refspec/tests/fixtures/generated-archives/parse_baseline.tar.xz differ diff --git a/gix-refspec/tests/fixtures/match_baseline.sh b/gix-refspec/tests/fixtures/match_baseline.sh index b6e2eee8a56..74980db9dd0 100755 --- a/gix-refspec/tests/fixtures/match_baseline.sh +++ b/gix-refspec/tests/fixtures/match_baseline.sh @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash set -eu -o pipefail git init; diff --git a/gix-refspec/tests/fixtures/parse_baseline.sh b/gix-refspec/tests/fixtures/parse_baseline.sh index cf87060ab6c..93475e10cc5 100755 --- a/gix-refspec/tests/fixtures/parse_baseline.sh +++ b/gix-refspec/tests/fixtures/parse_baseline.sh @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash set -eu -o pipefail git init; diff --git a/gix-refspec/tests/impls/mod.rs b/gix-refspec/tests/impls/mod.rs index e64c29341bb..c32f5d92060 100644 --- a/gix-refspec/tests/impls/mod.rs +++ b/gix-refspec/tests/impls/mod.rs @@ -1,7 +1,4 @@ -use std::{ - collections::{BTreeSet, HashSet}, - iter::FromIterator, -}; +use std::collections::{BTreeSet, HashSet}; use gix_refspec::{parse::Operation, RefSpec}; diff --git a/gix-revision/CHANGELOG.md b/gix-revision/CHANGELOG.md index 64a333dfbdc..0a9bc9135c5 100644 --- a/gix-revision/CHANGELOG.md +++ b/gix-revision/CHANGELOG.md @@ -5,6 +5,56 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## 0.27.1 (2024-05-22) + +A maintenance release without user-facing changes. + +### Commit Statistics + + + + - 2 commits contributed to the release over the course of 8 calendar days. + - 68 days passed between releases. + - 0 commits were understood as [conventional](https://www.conventionalcommits.org). + - 0 issues like '(#ID)' were seen in commit messages + +### Commit Details + + + +
view details + + * **Uncategorized** + - Adjust changelogs prior to release ([`9511416`](https://github.com/Byron/gitoxide/commit/9511416a6cd0c571233f958c165329c8705c2498)) + - Release gix-date v0.8.6 ([`d3588ca`](https://github.com/Byron/gitoxide/commit/d3588ca4fe0364c88e42cdac24ceae548355d99d)) +
+ +## 0.27.0 (2024-03-14) + +A maintenance release without user-facing changes. + +### Commit Statistics + + + + - 4 commits contributed to the release over the course of 4 calendar days. + - 18 days passed between releases. + - 0 commits were understood as [conventional](https://www.conventionalcommits.org). + - 0 issues like '(#ID)' were seen in commit messages + +### Commit Details + + + +
view details + + * **Uncategorized** + - Release gix-date v0.8.5, gix-hash v0.14.2, gix-trace v0.1.8, gix-utils v0.1.11, gix-features v0.38.1, gix-actor v0.31.0, gix-validate v0.8.4, gix-object v0.42.0, gix-path v0.10.7, gix-glob v0.16.2, gix-quote v0.4.12, gix-attributes v0.22.2, gix-command v0.3.6, gix-filter v0.11.0, gix-fs v0.10.1, gix-chunk v0.4.8, gix-commitgraph v0.24.2, gix-hashtable v0.5.2, gix-revwalk v0.13.0, gix-traverse v0.38.0, gix-worktree-stream v0.11.0, gix-archive v0.11.0, gix-config-value v0.14.6, gix-tempfile v13.1.1, gix-lock v13.1.1, gix-ref v0.43.0, gix-sec v0.10.6, gix-config v0.36.0, gix-prompt v0.8.4, gix-url v0.27.2, gix-credentials v0.24.2, gix-ignore v0.11.2, gix-bitmap v0.2.11, gix-index v0.31.0, gix-worktree v0.32.0, gix-diff v0.42.0, gix-discover v0.31.0, gix-pathspec v0.7.1, gix-dir v0.2.0, gix-macros v0.1.4, gix-mailmap v0.23.0, gix-negotiate v0.13.0, gix-pack v0.49.0, gix-odb v0.59.0, gix-packetline v0.17.4, gix-transport v0.41.2, gix-protocol v0.44.2, gix-revision v0.27.0, gix-refspec v0.23.0, gix-status v0.7.0, gix-submodule v0.10.0, gix-worktree-state v0.9.0, gix v0.60.0, safety bump 26 crates ([`b050327`](https://github.com/Byron/gitoxide/commit/b050327e76f234b19be921b78b7b28e034319fdb)) + - Prepare changelogs prior to release ([`52c3bbd`](https://github.com/Byron/gitoxide/commit/52c3bbd36b9e94a0f3a78b4ada84d0c08eba27f6)) + - Merge branch 'status' ([`3e5c974`](https://github.com/Byron/gitoxide/commit/3e5c974dd62ac134711c6c2f5a5490187a6ea55e)) + - Fix lints for nightly, and clippy ([`f8ce3d0`](https://github.com/Byron/gitoxide/commit/f8ce3d0721b6a53713a9392f2451874f520bc44c)) +
+ ## 0.26.1 (2024-02-25) A maintenance release without user-facing changes. @@ -13,7 +63,7 @@ A maintenance release without user-facing changes. - - 3 commits contributed to the release over the course of 13 calendar days. + - 4 commits contributed to the release over the course of 13 calendar days. - 36 days passed between releases. - 0 commits were understood as [conventional](https://www.conventionalcommits.org). - 0 issues like '(#ID)' were seen in commit messages @@ -25,6 +75,7 @@ A maintenance release without user-facing changes.
view details * **Uncategorized** + - Release gix-date v0.8.4, gix-utils v0.1.10, gix-actor v0.30.1, gix-object v0.41.1, gix-path v0.10.6, gix-glob v0.16.1, gix-quote v0.4.11, gix-attributes v0.22.1, gix-command v0.3.5, gix-filter v0.10.0, gix-commitgraph v0.24.1, gix-worktree-stream v0.10.0, gix-archive v0.10.0, gix-config-value v0.14.5, gix-ref v0.42.0, gix-sec v0.10.5, gix-config v0.35.0, gix-prompt v0.8.3, gix-url v0.27.1, gix-credentials v0.24.1, gix-ignore v0.11.1, gix-index v0.30.0, gix-worktree v0.31.0, gix-diff v0.41.0, gix-discover v0.30.0, gix-pathspec v0.7.0, gix-dir v0.1.0, gix-pack v0.48.0, gix-odb v0.58.0, gix-transport v0.41.1, gix-protocol v0.44.1, gix-revision v0.26.1, gix-refspec v0.22.1, gix-status v0.6.0, gix-submodule v0.9.0, gix-worktree-state v0.8.0, gix v0.59.0, gix-fsck v0.3.0, gitoxide-core v0.36.0, gitoxide v0.34.0, safety bump 10 crates ([`45b4470`](https://github.com/Byron/gitoxide/commit/45b447045bc826f252129c300c531acde2652c64)) - Prepare changelogs prior to release ([`f2e111f`](https://github.com/Byron/gitoxide/commit/f2e111f768fc1bc6182355261c20b63610cffec7)) - Merge branch 'dirwalk' ([`face359`](https://github.com/Byron/gitoxide/commit/face359443ba33e8985ec1525d5ec38b743ea7a9)) - Adjust gitignore files with precious declarations ([`ae86a6a`](https://github.com/Byron/gitoxide/commit/ae86a6a206074b85ff1eba32aea9c8b40c087b17)) diff --git a/gix-revision/Cargo.toml b/gix-revision/Cargo.toml index a34404e8e5e..e9acbb0a0ed 100644 --- a/gix-revision/Cargo.toml +++ b/gix-revision/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "gix-revision" -version = "0.26.1" +version = "0.27.1" repository = "https://github.com/Byron/gitoxide" license = "MIT OR Apache-2.0" description = "A crate of the gitoxide project dealing with finding names for revisions and parsing specifications" @@ -22,12 +22,12 @@ describe = [] serde = [ "dep:serde", "gix-hash/serde", "gix-object/serde" ] [dependencies] -gix-hash = { version = "^0.14.1", path = "../gix-hash" } -gix-object = { version = "^0.41.1", path = "../gix-object" } -gix-date = { version = "^0.8.4", path = "../gix-date" } -gix-hashtable = { version = "^0.5.1", path = "../gix-hashtable" } -gix-revwalk = { version = "^0.12.0", path = "../gix-revwalk" } -gix-trace = { version = "^0.1.7", path = "../gix-trace" } +gix-hash = { version = "^0.14.2", path = "../gix-hash" } +gix-object = { version = "^0.42.2", path = "../gix-object" } +gix-date = { version = "^0.8.6", path = "../gix-date" } +gix-hashtable = { version = "^0.5.2", path = "../gix-hashtable" } +gix-revwalk = { version = "^0.13.1", path = "../gix-revwalk" } +gix-trace = { version = "^0.1.8", path = "../gix-trace" } bstr = { version = "1.3.0", default-features = false, features = ["std"]} thiserror = "1.0.26" diff --git a/gix-revision/src/lib.rs b/gix-revision/src/lib.rs index 1593c25c1a8..9fcf73abfaa 100644 --- a/gix-revision/src/lib.rs +++ b/gix-revision/src/lib.rs @@ -9,12 +9,14 @@ #![deny(missing_docs, rust_2018_idioms, unsafe_code)] /// +#[allow(clippy::empty_docs)] #[cfg(feature = "describe")] pub mod describe; #[cfg(feature = "describe")] pub use describe::function::describe; /// +#[allow(clippy::empty_docs)] pub mod spec; pub use gix_revwalk::{graph, Graph, PriorityQueue}; pub use spec::types::Spec; diff --git a/gix-revision/src/spec/mod.rs b/gix-revision/src/spec/mod.rs index 616ad3a26b2..e05b1bc807a 100644 --- a/gix-revision/src/spec/mod.rs +++ b/gix-revision/src/spec/mod.rs @@ -105,5 +105,6 @@ pub(crate) mod types { } /// +#[allow(clippy::empty_docs)] pub mod parse; pub use parse::function::parse; diff --git a/gix-revision/src/spec/parse/function.rs b/gix-revision/src/spec/parse/function.rs index fad845deb18..502ca3e7c8d 100644 --- a/gix-revision/src/spec/parse/function.rs +++ b/gix-revision/src/spec/parse/function.rs @@ -1,4 +1,4 @@ -use std::{convert::TryInto, str::FromStr, time::SystemTime}; +use std::{str::FromStr, time::SystemTime}; use bstr::{BStr, BString, ByteSlice, ByteVec}; diff --git a/gix-revision/src/spec/parse/mod.rs b/gix-revision/src/spec/parse/mod.rs index 5a64012c666..bd5381fd200 100644 --- a/gix-revision/src/spec/parse/mod.rs +++ b/gix-revision/src/spec/parse/mod.rs @@ -46,6 +46,7 @@ pub enum Error { } /// +#[allow(clippy::empty_docs)] pub mod delegate; /// A delegate to be informed about parse events, with methods split into categories. diff --git a/gix-revision/tests/fixtures/generated-archives/make_repo_with_branches.tar.xz b/gix-revision/tests/fixtures/generated-archives/make_repo_with_branches.tar.xz index b9bfde43695..a10c1182691 100644 Binary files a/gix-revision/tests/fixtures/generated-archives/make_repo_with_branches.tar.xz and b/gix-revision/tests/fixtures/generated-archives/make_repo_with_branches.tar.xz differ diff --git a/gix-revision/tests/fixtures/make_repo_with_branches.sh b/gix-revision/tests/fixtures/make_repo_with_branches.sh index f53f9bf7db6..0bf4c54f3a3 100755 --- a/gix-revision/tests/fixtures/make_repo_with_branches.sh +++ b/gix-revision/tests/fixtures/make_repo_with_branches.sh @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash set -eu -o pipefail git init -q diff --git a/gix-revwalk/CHANGELOG.md b/gix-revwalk/CHANGELOG.md index 8b2adca1a66..8677c380974 100644 --- a/gix-revwalk/CHANGELOG.md +++ b/gix-revwalk/CHANGELOG.md @@ -5,6 +5,56 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## 0.13.1 (2024-05-22) + +A maintenance release without user-facing changes. + +### Commit Statistics + + + + - 2 commits contributed to the release over the course of 8 calendar days. + - 68 days passed between releases. + - 0 commits were understood as [conventional](https://www.conventionalcommits.org). + - 0 issues like '(#ID)' were seen in commit messages + +### Commit Details + + + +
view details + + * **Uncategorized** + - Adjust changelogs prior to release ([`9511416`](https://github.com/Byron/gitoxide/commit/9511416a6cd0c571233f958c165329c8705c2498)) + - Release gix-date v0.8.6 ([`d3588ca`](https://github.com/Byron/gitoxide/commit/d3588ca4fe0364c88e42cdac24ceae548355d99d)) +
+ +## 0.13.0 (2024-03-14) + +A maintenance release without user-facing changes. + +### Commit Statistics + + + + - 4 commits contributed to the release over the course of 4 calendar days. + - 54 days passed between releases. + - 0 commits were understood as [conventional](https://www.conventionalcommits.org). + - 0 issues like '(#ID)' were seen in commit messages + +### Commit Details + + + +
view details + + * **Uncategorized** + - Release gix-date v0.8.5, gix-hash v0.14.2, gix-trace v0.1.8, gix-utils v0.1.11, gix-features v0.38.1, gix-actor v0.31.0, gix-validate v0.8.4, gix-object v0.42.0, gix-path v0.10.7, gix-glob v0.16.2, gix-quote v0.4.12, gix-attributes v0.22.2, gix-command v0.3.6, gix-filter v0.11.0, gix-fs v0.10.1, gix-chunk v0.4.8, gix-commitgraph v0.24.2, gix-hashtable v0.5.2, gix-revwalk v0.13.0, gix-traverse v0.38.0, gix-worktree-stream v0.11.0, gix-archive v0.11.0, gix-config-value v0.14.6, gix-tempfile v13.1.1, gix-lock v13.1.1, gix-ref v0.43.0, gix-sec v0.10.6, gix-config v0.36.0, gix-prompt v0.8.4, gix-url v0.27.2, gix-credentials v0.24.2, gix-ignore v0.11.2, gix-bitmap v0.2.11, gix-index v0.31.0, gix-worktree v0.32.0, gix-diff v0.42.0, gix-discover v0.31.0, gix-pathspec v0.7.1, gix-dir v0.2.0, gix-macros v0.1.4, gix-mailmap v0.23.0, gix-negotiate v0.13.0, gix-pack v0.49.0, gix-odb v0.59.0, gix-packetline v0.17.4, gix-transport v0.41.2, gix-protocol v0.44.2, gix-revision v0.27.0, gix-refspec v0.23.0, gix-status v0.7.0, gix-submodule v0.10.0, gix-worktree-state v0.9.0, gix v0.60.0, safety bump 26 crates ([`b050327`](https://github.com/Byron/gitoxide/commit/b050327e76f234b19be921b78b7b28e034319fdb)) + - Prepare changelogs prior to release ([`52c3bbd`](https://github.com/Byron/gitoxide/commit/52c3bbd36b9e94a0f3a78b4ada84d0c08eba27f6)) + - Merge branch 'status' ([`3e5c974`](https://github.com/Byron/gitoxide/commit/3e5c974dd62ac134711c6c2f5a5490187a6ea55e)) + - Fix lints for nightly, and clippy ([`f8ce3d0`](https://github.com/Byron/gitoxide/commit/f8ce3d0721b6a53713a9392f2451874f520bc44c)) +
+ ## 0.12.0 (2024-01-20) A maintenance release without user-facing changes. @@ -13,7 +63,7 @@ A maintenance release without user-facing changes. - - 1 commit contributed to the release. + - 2 commits contributed to the release. - 20 days passed between releases. - 0 commits were understood as [conventional](https://www.conventionalcommits.org). - 0 issues like '(#ID)' were seen in commit messages @@ -25,6 +75,7 @@ A maintenance release without user-facing changes.
view details * **Uncategorized** + - Release gix-utils v0.1.9, gix-features v0.38.0, gix-actor v0.30.0, gix-object v0.41.0, gix-path v0.10.4, gix-glob v0.16.0, gix-attributes v0.22.0, gix-command v0.3.3, gix-packetline-blocking v0.17.3, gix-filter v0.9.0, gix-fs v0.10.0, gix-commitgraph v0.24.0, gix-revwalk v0.12.0, gix-traverse v0.37.0, gix-worktree-stream v0.9.0, gix-archive v0.9.0, gix-config-value v0.14.4, gix-tempfile v13.0.0, gix-lock v13.0.0, gix-ref v0.41.0, gix-sec v0.10.4, gix-config v0.34.0, gix-url v0.27.0, gix-credentials v0.24.0, gix-ignore v0.11.0, gix-index v0.29.0, gix-worktree v0.30.0, gix-diff v0.40.0, gix-discover v0.29.0, gix-mailmap v0.22.0, gix-negotiate v0.12.0, gix-pack v0.47.0, gix-odb v0.57.0, gix-pathspec v0.6.0, gix-packetline v0.17.3, gix-transport v0.41.0, gix-protocol v0.44.0, gix-revision v0.26.0, gix-refspec v0.22.0, gix-status v0.5.0, gix-submodule v0.8.0, gix-worktree-state v0.7.0, gix v0.58.0, safety bump 39 crates ([`eb6aa8f`](https://github.com/Byron/gitoxide/commit/eb6aa8f502314f886fc4ea3d52ab220763968208)) - Prepare changelogs prior to release ([`6a2e0be`](https://github.com/Byron/gitoxide/commit/6a2e0bebfdf012dc2ed0ff2604086081f2a0f96d))
@@ -34,12 +85,12 @@ A maintenance release without user-facing changes. ### Chore - - change `rust-version` manifest field back to 1.65. - They didn't actually need to be higher to work, and changing them - unecessarily can break downstream CI. - - Let's keep this value as low as possible, and only increase it when - more recent features are actually used. +- change `rust-version` manifest field back to 1.65. + They didn't actually need to be higher to work, and changing them + unecessarily can break downstream CI. + + Let's keep this value as low as possible, and only increase it when + more recent features are actually used. ### Commit Statistics @@ -67,8 +118,8 @@ A maintenance release without user-facing changes. ### Chore - - upgrade MSRV to v1.70 - Our MSRV follows the one of `helix`, which in turn follows Firefox. +- upgrade MSRV to v1.70 + Our MSRV follows the one of `helix`, which in turn follows Firefox. ### Commit Statistics diff --git a/gix-revwalk/Cargo.toml b/gix-revwalk/Cargo.toml index de6cc3e2a51..2f75032253b 100644 --- a/gix-revwalk/Cargo.toml +++ b/gix-revwalk/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "gix-revwalk" -version = "0.12.0" +version = "0.13.1" repository = "https://github.com/Byron/gitoxide" license = "MIT OR Apache-2.0" description = "A crate providing utilities for walking the revision graph" @@ -13,11 +13,11 @@ rust-version = "1.65" doctest = false [dependencies] -gix-hash = { version = "^0.14.1", path = "../gix-hash" } -gix-object = { version = "^0.41.0", path = "../gix-object" } -gix-date = { version = "^0.8.3", path = "../gix-date" } -gix-hashtable = { version = "^0.5.1", path = "../gix-hashtable" } -gix-commitgraph = { version = "^0.24.0", path = "../gix-commitgraph" } +gix-hash = { version = "^0.14.2", path = "../gix-hash" } +gix-object = { version = "^0.42.2", path = "../gix-object" } +gix-date = { version = "^0.8.6", path = "../gix-date" } +gix-hashtable = { version = "^0.5.2", path = "../gix-hashtable" } +gix-commitgraph = { version = "^0.24.2", path = "../gix-commitgraph" } thiserror = "1.0.26" smallvec = "1.10.0" diff --git a/gix-revwalk/src/graph/commit.rs b/gix-revwalk/src/graph/commit.rs index 8bfc8afc8a0..f7bb39109c5 100644 --- a/gix-revwalk/src/graph/commit.rs +++ b/gix-revwalk/src/graph/commit.rs @@ -123,6 +123,7 @@ impl<'graph> Iterator for Parents<'graph> { } /// +#[allow(clippy::empty_docs)] pub mod iter_parents { /// The error returned by the [`Parents`][super::Parents] iterator. #[derive(Debug, thiserror::Error)] @@ -136,6 +137,7 @@ pub mod iter_parents { } /// +#[allow(clippy::empty_docs)] pub mod to_owned { /// The error returned by [`to_owned()`][crate::graph::LazyCommit::to_owned()]. #[derive(Debug, thiserror::Error)] diff --git a/gix-revwalk/src/graph/mod.rs b/gix-revwalk/src/graph/mod.rs index fa3b16870fe..bd004826bb4 100644 --- a/gix-revwalk/src/graph/mod.rs +++ b/gix-revwalk/src/graph/mod.rs @@ -9,10 +9,12 @@ use crate::Graph; pub type IdMap = gix_hashtable::HashMap; /// +#[allow(clippy::empty_docs)] pub mod commit; mod errors { /// + #[allow(clippy::empty_docs)] pub mod insert_parents { use crate::graph::commit::iter_parents; @@ -30,6 +32,7 @@ mod errors { } /// + #[allow(clippy::empty_docs)] pub mod try_lookup_or_insert_default { use crate::graph::commit::to_owned; diff --git a/gix-revwalk/src/lib.rs b/gix-revwalk/src/lib.rs index 67bfc6dea6b..a05b1bb2868 100644 --- a/gix-revwalk/src/lib.rs +++ b/gix-revwalk/src/lib.rs @@ -39,6 +39,7 @@ pub struct Graph<'find, T> { } /// +#[allow(clippy::empty_docs)] pub mod graph; /// A utility type implementing a queue which can be used to automatically sort data by its time in ascending order. diff --git a/gix-sec/CHANGELOG.md b/gix-sec/CHANGELOG.md index 624162b1678..d363814ad30 100644 --- a/gix-sec/CHANGELOG.md +++ b/gix-sec/CHANGELOG.md @@ -5,6 +5,31 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## 0.10.6 (2024-03-14) + +A maintenance release without user-facing changes. + +### Commit Statistics + + + + - 3 commits contributed to the release over the course of 4 calendar days. + - 18 days passed between releases. + - 0 commits were understood as [conventional](https://www.conventionalcommits.org). + - 0 issues like '(#ID)' were seen in commit messages + +### Commit Details + + + +
view details + + * **Uncategorized** + - Prepare changelogs prior to release ([`52c3bbd`](https://github.com/Byron/gitoxide/commit/52c3bbd36b9e94a0f3a78b4ada84d0c08eba27f6)) + - Merge branch 'status' ([`3e5c974`](https://github.com/Byron/gitoxide/commit/3e5c974dd62ac134711c6c2f5a5490187a6ea55e)) + - Fix lints for nightly, and clippy ([`f8ce3d0`](https://github.com/Byron/gitoxide/commit/f8ce3d0721b6a53713a9392f2451874f520bc44c)) +
+ ## 0.10.5 (2024-02-25) A maintenance release without user-facing changes. @@ -13,7 +38,7 @@ A maintenance release without user-facing changes. - - 2 commits contributed to the release over the course of 30 calendar days. + - 3 commits contributed to the release over the course of 30 calendar days. - 36 days passed between releases. - 0 commits were understood as [conventional](https://www.conventionalcommits.org). - 0 issues like '(#ID)' were seen in commit messages @@ -25,6 +50,7 @@ A maintenance release without user-facing changes.
view details * **Uncategorized** + - Release gix-date v0.8.4, gix-utils v0.1.10, gix-actor v0.30.1, gix-object v0.41.1, gix-path v0.10.6, gix-glob v0.16.1, gix-quote v0.4.11, gix-attributes v0.22.1, gix-command v0.3.5, gix-filter v0.10.0, gix-commitgraph v0.24.1, gix-worktree-stream v0.10.0, gix-archive v0.10.0, gix-config-value v0.14.5, gix-ref v0.42.0, gix-sec v0.10.5, gix-config v0.35.0, gix-prompt v0.8.3, gix-url v0.27.1, gix-credentials v0.24.1, gix-ignore v0.11.1, gix-index v0.30.0, gix-worktree v0.31.0, gix-diff v0.41.0, gix-discover v0.30.0, gix-pathspec v0.7.0, gix-dir v0.1.0, gix-pack v0.48.0, gix-odb v0.58.0, gix-transport v0.41.1, gix-protocol v0.44.1, gix-revision v0.26.1, gix-refspec v0.22.1, gix-status v0.6.0, gix-submodule v0.9.0, gix-worktree-state v0.8.0, gix v0.59.0, gix-fsck v0.3.0, gitoxide-core v0.36.0, gitoxide v0.34.0, safety bump 10 crates ([`45b4470`](https://github.com/Byron/gitoxide/commit/45b447045bc826f252129c300c531acde2652c64)) - Prepare changelogs prior to release ([`f2e111f`](https://github.com/Byron/gitoxide/commit/f2e111f768fc1bc6182355261c20b63610cffec7)) - Release gix-path v0.10.5 ([`b8cba96`](https://github.com/Byron/gitoxide/commit/b8cba96ce57f8b6b0067d6a8cf3e37eaf280a238))
diff --git a/gix-sec/Cargo.toml b/gix-sec/Cargo.toml index fedfdd01a30..7ed5e61ccbb 100644 --- a/gix-sec/Cargo.toml +++ b/gix-sec/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "gix-sec" -version = "0.10.5" +version = "0.10.6" repository = "https://github.com/Byron/gitoxide" license = "MIT OR Apache-2.0" description = "A crate of the gitoxide project providing a shared trust model" @@ -29,7 +29,7 @@ document-features = { version = "0.2.1", optional = true } libc = "0.2.123" [target.'cfg(windows)'.dependencies] -gix-path = { version = "^0.10.6", path = "../gix-path" } +gix-path = { version = "^0.10.7", path = "../gix-path" } windows-sys = { version = "0.52.0", features = [ "Win32_Foundation", "Win32_Security_Authorization", diff --git a/gix-sec/src/lib.rs b/gix-sec/src/lib.rs index 35b7b34d524..309c734ed31 100644 --- a/gix-sec/src/lib.rs +++ b/gix-sec/src/lib.rs @@ -22,6 +22,7 @@ pub enum Trust { } /// +#[allow(clippy::empty_docs)] pub mod trust; /// Allow, deny or forbid using a resource or performing an action. @@ -37,6 +38,7 @@ pub enum Permission { } /// +#[allow(clippy::empty_docs)] pub mod permission; bitflags::bitflags! { diff --git a/gix-status/CHANGELOG.md b/gix-status/CHANGELOG.md index e9b1d12450a..cca6ff76a01 100644 --- a/gix-status/CHANGELOG.md +++ b/gix-status/CHANGELOG.md @@ -5,6 +5,124 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## 0.10.0 (2024-05-22) + +A maintenance release without user-facing changes. + +### Commit Statistics + + + + - 9 commits contributed to the release over the course of 38 calendar days. + - 38 days passed between releases. + - 0 commits were understood as [conventional](https://www.conventionalcommits.org). + - 0 issues like '(#ID)' were seen in commit messages + +### Commit Details + + + +
view details + + * **Uncategorized** + - Adjust changelogs prior to release ([`9511416`](https://github.com/Byron/gitoxide/commit/9511416a6cd0c571233f958c165329c8705c2498)) + - Merge branch 'various-fixes' ([`d6cd449`](https://github.com/Byron/gitoxide/commit/d6cd44930fb204b06e2b70fc6965e7705530c47a)) + - Merge pull request from GHSA-7w47-3wg8-547c ([`79dce79`](https://github.com/Byron/gitoxide/commit/79dce79c62f6072aa2653780d590dc3993dfa401)) + - Address review comments ([`fcc3b69`](https://github.com/Byron/gitoxide/commit/fcc3b69867db1628f6a44d0e0dad8f7417f566bc)) + - Adapt to changes in `gix-worktree` ([`1ca6a3c`](https://github.com/Byron/gitoxide/commit/1ca6a3ce22887c7eb42ec3e0a19f6e1202715745)) + - Merge branch 'cargo-fixes' ([`977346e`](https://github.com/Byron/gitoxide/commit/977346ee61de6207c66f3de003db6e8c722fb81c)) + - Release gix-index v0.32.1, gix-pathspec v0.7.4, gix-worktree v0.33.1, gix-dir v0.4.1 ([`54ac559`](https://github.com/Byron/gitoxide/commit/54ac55946bb04635cd74582a1ce2e4bee70f2e60)) + - Merge pull request #1345 from EliahKagan/shell-scripts ([`fe24c89`](https://github.com/Byron/gitoxide/commit/fe24c89e326670deaa3aaa643276d612d866072e)) + - Add missing +x bit on scripts that are run and not sourced ([`41bf65a`](https://github.com/Byron/gitoxide/commit/41bf65adef6f7d2cdd28fede262173ec7ba10822)) +
+ +## 0.9.0 (2024-04-13) + +A maintenance release without user-facing changes. + +### Commit Statistics + + + + - 6 commits contributed to the release over the course of 9 calendar days. + - 26 days passed between releases. + - 0 commits were understood as [conventional](https://www.conventionalcommits.org). + - 0 issues like '(#ID)' were seen in commit messages + +### Commit Details + + + +
view details + + * **Uncategorized** + - Release gix-trace v0.1.9, gix-utils v0.1.12, gix-packetline-blocking v0.17.4, gix-filter v0.11.1, gix-fs v0.10.2, gix-traverse v0.39.0, gix-worktree-stream v0.12.0, gix-archive v0.12.0, gix-config v0.36.1, gix-url v0.27.3, gix-index v0.32.0, gix-worktree v0.33.0, gix-diff v0.43.0, gix-pathspec v0.7.3, gix-dir v0.4.0, gix-pack v0.50.0, gix-odb v0.60.0, gix-transport v0.42.0, gix-protocol v0.45.0, gix-status v0.9.0, gix-worktree-state v0.10.0, gix v0.62.0, gix-fsck v0.4.0, gitoxide-core v0.37.0, gitoxide v0.35.0, safety bump 14 crates ([`095c673`](https://github.com/Byron/gitoxide/commit/095c6739b2722a8b9af90776b435ef2da454c0e6)) + - Prepare changelogs prior to release ([`5755271`](https://github.com/Byron/gitoxide/commit/57552717f46f96c35ba4ddc0a64434354ef845e9)) + - Merge pull request #1341 from szepeviktor/typos ([`55f379b`](https://github.com/Byron/gitoxide/commit/55f379bc47065822d078393d83d30c0835a89782)) + - Fix typos ([`f72ecce`](https://github.com/Byron/gitoxide/commit/f72ecce45babcad2a0c9b73c79d01ff502907a57)) + - Merge branch 'status' ([`45edd2e`](https://github.com/Byron/gitoxide/commit/45edd2ea66035adf526cb2f617873dcba60a2a9a)) + - Adapt to changes in `gix-index` ([`1e1fce1`](https://github.com/Byron/gitoxide/commit/1e1fce11a968ebbcede1135ccbd0b03e749a1267)) +
+ +## 0.8.0 (2024-03-18) + +A maintenance release without user-facing changes. + +### Commit Statistics + + + + - 4 commits contributed to the release. + - 3 days passed between releases. + - 0 commits were understood as [conventional](https://www.conventionalcommits.org). + - 0 issues like '(#ID)' were seen in commit messages + +### Commit Details + + + +
view details + + * **Uncategorized** + - Release gix-actor v0.31.1, gix-object v0.42.1, gix-index v0.31.1, gix-pathspec v0.7.2, gix-dir v0.3.0, gix-status v0.8.0, gix v0.61.0, safety bump 2 crates ([`155cc45`](https://github.com/Byron/gitoxide/commit/155cc45730b259e662d7c4be42a469a3af3750e1)) + - Prepare changelog prior to release ([`129ba3d`](https://github.com/Byron/gitoxide/commit/129ba3deccc9ada0dc571466458845939502763d)) + - Merge branch 'improvements-for-cargo' ([`41cd53e`](https://github.com/Byron/gitoxide/commit/41cd53e2af76e35e047aac4eca6324774df4cb50)) + - Adapt to changes in `gix-dir` ([`e48ba08`](https://github.com/Byron/gitoxide/commit/e48ba081ae1c76c05215725647399e0d060b7134)) +
+ +## 0.7.0 (2024-03-14) + +### New Features + + - provide `Entry::worktree_summary()` + That way it's possible to more easily and straight-forwardly understand + the status of an entry, comparing index to worktree. + - add index-to-worktree status with rename tracking + +### Commit Statistics + + + + - 6 commits contributed to the release over the course of 4 calendar days. + - 18 days passed between releases. + - 2 commits were understood as [conventional](https://www.conventionalcommits.org). + - 0 issues like '(#ID)' were seen in commit messages + +### Commit Details + + + +
view details + + * **Uncategorized** + - Release gix-date v0.8.5, gix-hash v0.14.2, gix-trace v0.1.8, gix-utils v0.1.11, gix-features v0.38.1, gix-actor v0.31.0, gix-validate v0.8.4, gix-object v0.42.0, gix-path v0.10.7, gix-glob v0.16.2, gix-quote v0.4.12, gix-attributes v0.22.2, gix-command v0.3.6, gix-filter v0.11.0, gix-fs v0.10.1, gix-chunk v0.4.8, gix-commitgraph v0.24.2, gix-hashtable v0.5.2, gix-revwalk v0.13.0, gix-traverse v0.38.0, gix-worktree-stream v0.11.0, gix-archive v0.11.0, gix-config-value v0.14.6, gix-tempfile v13.1.1, gix-lock v13.1.1, gix-ref v0.43.0, gix-sec v0.10.6, gix-config v0.36.0, gix-prompt v0.8.4, gix-url v0.27.2, gix-credentials v0.24.2, gix-ignore v0.11.2, gix-bitmap v0.2.11, gix-index v0.31.0, gix-worktree v0.32.0, gix-diff v0.42.0, gix-discover v0.31.0, gix-pathspec v0.7.1, gix-dir v0.2.0, gix-macros v0.1.4, gix-mailmap v0.23.0, gix-negotiate v0.13.0, gix-pack v0.49.0, gix-odb v0.59.0, gix-packetline v0.17.4, gix-transport v0.41.2, gix-protocol v0.44.2, gix-revision v0.27.0, gix-refspec v0.23.0, gix-status v0.7.0, gix-submodule v0.10.0, gix-worktree-state v0.9.0, gix v0.60.0, safety bump 26 crates ([`b050327`](https://github.com/Byron/gitoxide/commit/b050327e76f234b19be921b78b7b28e034319fdb)) + - Prepare changelogs prior to release ([`52c3bbd`](https://github.com/Byron/gitoxide/commit/52c3bbd36b9e94a0f3a78b4ada84d0c08eba27f6)) + - Merge branch 'status' ([`3e5c974`](https://github.com/Byron/gitoxide/commit/3e5c974dd62ac134711c6c2f5a5490187a6ea55e)) + - Provide `Entry::worktree_summary()` ([`2dc373f`](https://github.com/Byron/gitoxide/commit/2dc373f54988fb38b3a8f1e6100c46ece53b9f95)) + - Fix lints for nightly, and clippy ([`f8ce3d0`](https://github.com/Byron/gitoxide/commit/f8ce3d0721b6a53713a9392f2451874f520bc44c)) + - Add index-to-worktree status with rename tracking ([`80a6cb8`](https://github.com/Byron/gitoxide/commit/80a6cb80fdb3a49c3461d286d62baf68755aa733)) +
+ ## 0.6.0 (2024-02-25) ### New Features (BREAKING) @@ -19,7 +137,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - - 6 commits contributed to the release over the course of 30 calendar days. + - 7 commits contributed to the release over the course of 30 calendar days. - 36 days passed between releases. - 1 commit was understood as [conventional](https://www.conventionalcommits.org). - 0 issues like '(#ID)' were seen in commit messages @@ -31,6 +149,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
view details * **Uncategorized** + - Release gix-date v0.8.4, gix-utils v0.1.10, gix-actor v0.30.1, gix-object v0.41.1, gix-path v0.10.6, gix-glob v0.16.1, gix-quote v0.4.11, gix-attributes v0.22.1, gix-command v0.3.5, gix-filter v0.10.0, gix-commitgraph v0.24.1, gix-worktree-stream v0.10.0, gix-archive v0.10.0, gix-config-value v0.14.5, gix-ref v0.42.0, gix-sec v0.10.5, gix-config v0.35.0, gix-prompt v0.8.3, gix-url v0.27.1, gix-credentials v0.24.1, gix-ignore v0.11.1, gix-index v0.30.0, gix-worktree v0.31.0, gix-diff v0.41.0, gix-discover v0.30.0, gix-pathspec v0.7.0, gix-dir v0.1.0, gix-pack v0.48.0, gix-odb v0.58.0, gix-transport v0.41.1, gix-protocol v0.44.1, gix-revision v0.26.1, gix-refspec v0.22.1, gix-status v0.6.0, gix-submodule v0.9.0, gix-worktree-state v0.8.0, gix v0.59.0, gix-fsck v0.3.0, gitoxide-core v0.36.0, gitoxide v0.34.0, safety bump 10 crates ([`45b4470`](https://github.com/Byron/gitoxide/commit/45b447045bc826f252129c300c531acde2652c64)) - Prepare changelogs prior to release ([`f2e111f`](https://github.com/Byron/gitoxide/commit/f2e111f768fc1bc6182355261c20b63610cffec7)) - Merge branch 'status' ([`bb48c4c`](https://github.com/Byron/gitoxide/commit/bb48c4ce22650b8c76af3b147e252ebe7cedb205)) - Improve `index_as_worktree()` function signature. ([`0bef316`](https://github.com/Byron/gitoxide/commit/0bef316696fcf11e9f7dc1910819a60e4229f766)) diff --git a/gix-status/Cargo.toml b/gix-status/Cargo.toml index 0d75b7dc3c0..225e68eb5f5 100644 --- a/gix-status/Cargo.toml +++ b/gix-status/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "gix-status" -version = "0.6.0" +version = "0.10.0" repository = "https://github.com/Byron/gitoxide" license = "MIT OR Apache-2.0" description = "A crate of the gitoxide project dealing with 'git status'-like functionality" @@ -13,17 +13,29 @@ autotests = false [lib] doctest = false +[features] +## Add support for tracking rewrites along with checking for worktree modifications. +worktree-rewrites = ["dep:gix-dir", "dep:gix-diff"] + [dependencies] -gix-index = { version = "^0.30.0", path = "../gix-index" } -gix-fs = { version = "^0.10.0", path = "../gix-fs" } -gix-hash = { version = "^0.14.1", path = "../gix-hash" } -gix-object = { version = "^0.41.1", path = "../gix-object" } -gix-path = { version = "^0.10.6", path = "../gix-path" } -gix-features = { version = "^0.38.0", path = "../gix-features" } -gix-filter = { version = "^0.10.0", path = "../gix-filter" } -gix-worktree = { version = "^0.31.0", path = "../gix-worktree", default-features = false, features = ["attributes"] } -gix-pathspec = { version = "^0.7.0", path = "../gix-pathspec" } +gix-index = { version = "^0.33.0", path = "../gix-index" } +gix-fs = { version = "^0.11.1", path = "../gix-fs" } +gix-hash = { version = "^0.14.2", path = "../gix-hash" } +gix-object = { version = "^0.42.2", path = "../gix-object" } +gix-path = { version = "^0.10.7", path = "../gix-path" } +gix-features = { version = "^0.38.2", path = "../gix-features", features = ["progress"] } +gix-filter = { version = "^0.11.2", path = "../gix-filter" } +gix-worktree = { version = "^0.34.0", path = "../gix-worktree", default-features = false, features = ["attributes"] } +gix-pathspec = { version = "^0.7.5", path = "../gix-pathspec" } + +gix-dir = { version = "^0.5.0", path = "../gix-dir", optional = true } +gix-diff = { version = "^0.44.0", path = "../gix-diff", default-features = false, features = ["blob"], optional = true } thiserror = "1.0.26" filetime = "0.2.15" bstr = { version = "1.3.0", default-features = false } + +document-features = { version = "0.2.0", optional = true } + +[package.metadata.docs.rs] +features = ["document-features", "worktree-rewrites"] diff --git a/gix-status/src/index_as_worktree/function.rs b/gix-status/src/index_as_worktree/function.rs index e69d6629b64..cf2e8ea9e51 100644 --- a/gix-status/src/index_as_worktree/function.rs +++ b/gix-status/src/index_as_worktree/function.rs @@ -19,7 +19,7 @@ use crate::{ types::{Error, Options}, Change, Conflict, EntryStatus, Outcome, VisitEntry, }, - SymlinkCheck, + is_dir_to_mode, SymlinkCheck, }; /// Calculates the changes that need to be applied to an `index` to match the state of the `worktree` and makes them @@ -157,11 +157,11 @@ where let mut idx = 0; while let Some(entry) = chunk_entries.get(idx) { let absolute_entry_index = entry_offset + idx; - if idx == 0 && entry.stage() != 0 { + if idx == 0 && entry.stage_raw() != 0 { let offset = entry_offset.checked_sub(1).and_then(|prev_idx| { let prev_entry = &all_entries[prev_idx]; let entry_path = entry.path_in(state.path_backing); - if prev_entry.stage() == 0 || prev_entry.path_in(state.path_backing) != entry_path { + if prev_entry.stage_raw() == 0 || prev_entry.path_in(state.path_backing) != entry_path { // prev_entry (in previous chunk) does not belong to our conflict return None; } @@ -276,7 +276,7 @@ impl<'index> State<'_, 'index> { &mut |relative_path, case, is_dir, out| { self.attr_stack .set_case(case) - .at_entry(relative_path, Some(is_dir), objects) + .at_entry(relative_path, Some(is_dir_to_mode(is_dir)), objects) .map_or(false, |platform| platform.matching_attributes(out)) }, ) @@ -286,7 +286,7 @@ impl<'index> State<'_, 'index> { self.skipped_by_pathspec.fetch_add(1, Ordering::Relaxed); return None; } - let status = if entry.stage() != 0 { + let status = if entry.stage_raw() != 0 { Ok( Conflict::try_from_entry(entries, self.path_backing, entry_index, path).map(|(conflict, offset)| { *outer_entry_index += offset; // let out loop skip over entries related to the conflict @@ -541,7 +541,9 @@ where } } else { self.buf.clear(); - let platform = self.attr_stack.at_entry(self.rela_path, Some(false), &self.objects)?; + let platform = self + .attr_stack + .at_entry(self.rela_path, Some(self.entry.mode), &self.objects)?; let file = std::fs::File::open(self.path)?; let out = self .filter @@ -604,7 +606,7 @@ impl Conflict { let mut count = 0_usize; for stage in (start_index..(start_index + 3).min(entries.len())).filter_map(|idx| { let entry = &entries[idx]; - let stage = entry.stage(); + let stage = entry.stage_raw(); (stage > 0 && entry.path_in(path_backing) == entry_path).then_some(stage) }) { // This could be `1 << (stage - 1)` but let's be specific. diff --git a/gix-status/src/index_as_worktree/mod.rs b/gix-status/src/index_as_worktree/mod.rs index 96694078bb8..9f1c38f6ab1 100644 --- a/gix-status/src/index_as_worktree/mod.rs +++ b/gix-status/src/index_as_worktree/mod.rs @@ -1,11 +1,13 @@ //! Changes between an index and a worktree. /// +#[allow(clippy::empty_docs)] mod types; pub use types::{Change, Conflict, Context, EntryStatus, Error, Options, Outcome, VisitEntry}; mod recorder; pub use recorder::{Record, Recorder}; -pub(crate) mod function; +pub(super) mod function; /// +#[allow(clippy::empty_docs)] pub mod traits; diff --git a/gix-status/src/index_as_worktree/recorder.rs b/gix-status/src/index_as_worktree/recorder.rs index 0cf1aa6f367..407abfd557c 100644 --- a/gix-status/src/index_as_worktree/recorder.rs +++ b/gix-status/src/index_as_worktree/recorder.rs @@ -6,7 +6,7 @@ use crate::index_as_worktree::{EntryStatus, VisitEntry}; /// A record of a change. /// /// It's created either if there is a conflict or a change, or both. -#[derive(Debug)] +#[derive(Debug, Clone)] pub struct Record<'index, T, U> { /// The index entry that is changed. pub entry: &'index index::Entry, diff --git a/gix-status/src/index_as_worktree/traits.rs b/gix-status/src/index_as_worktree/traits.rs index 8d86b579aaa..9865d1514e2 100644 --- a/gix-status/src/index_as_worktree/traits.rs +++ b/gix-status/src/index_as_worktree/traits.rs @@ -50,6 +50,7 @@ pub trait ReadData<'a> { } /// +#[allow(clippy::empty_docs)] pub mod read_data { use std::sync::atomic::{AtomicU64, Ordering}; diff --git a/gix-status/src/index_as_worktree/types.rs b/gix-status/src/index_as_worktree/types.rs index 6d93f784f12..729171f4595 100644 --- a/gix-status/src/index_as_worktree/types.rs +++ b/gix-status/src/index_as_worktree/types.rs @@ -21,7 +21,7 @@ pub enum Error { } /// Options that control how the index status with a worktree is computed. -#[derive(Clone, Default)] +#[derive(Clone, Default, Debug, PartialEq, Eq, Hash)] pub struct Options { /// Capabilities of the file system which affect the status computation. pub fs: gix_fs::Capabilities, @@ -37,6 +37,9 @@ pub struct Options { #[derive(Clone)] pub struct Context<'a> { /// The pathspec to limit the amount of paths that are checked. Can be empty to allow all paths. + /// + /// Note that these are expected to have a [common_prefix()](gix_pathspec::Search::common_prefix()) according + /// to the prefix of the repository to efficiently limit the scope of the paths we process. pub pathspec: gix_pathspec::Search, /// A stack pre-configured to allow accessing attributes for each entry, as required for `filter` /// and possibly pathspecs. diff --git a/gix-status/src/index_as_worktree_with_renames/mod.rs b/gix-status/src/index_as_worktree_with_renames/mod.rs new file mode 100644 index 00000000000..a3953bbed78 --- /dev/null +++ b/gix-status/src/index_as_worktree_with_renames/mod.rs @@ -0,0 +1,566 @@ +//! Changes between the index and the worktree along with optional rename tracking. +mod types; +pub use types::{Context, DirwalkContext, Entry, Error, Options, Outcome, RewriteSource, Sorting, Summary, VisitEntry}; + +mod recorder; +pub use recorder::Recorder; + +pub(super) mod function { + use crate::index_as_worktree::traits::{CompareBlobs, SubmoduleStatus}; + use crate::index_as_worktree_with_renames::function::rewrite::ModificationOrDirwalkEntry; + use crate::index_as_worktree_with_renames::{Context, Entry, Error, Options, Outcome, RewriteSource, VisitEntry}; + use crate::is_dir_to_mode; + use bstr::ByteSlice; + use gix_worktree::stack::State; + use std::borrow::Cow; + use std::path::Path; + + /// Similar to [`index_as_worktree(…)`](crate::index_as_worktree()), except that it will automatically + /// track renames if enabled, while additionally providing information about untracked files + /// (or more, depending on the configuration). + /// + /// * `index` + /// - used for checking modifications, and also for knowing which files are tracked during + /// the working-dir traversal. + /// * `worktree` + /// - The root of the worktree, in a format that respects `core.precomposeUnicode`. + /// * `collector` + /// - A [`VisitEntry`] implementation that sees the results of this operation. + /// * `compare` + /// - An implementation to compare two blobs for equality, used during index modification checks. + /// * `submodule` + /// - An implementation to determine the status of a submodule when encountered during + /// index modification checks. + /// * `objects` + /// - A way to obtain objects from the git object database. + /// * `progress` + /// - A way to send progress information for the index modification checks. + /// * `ctx` + /// - Additional information that will be accessed during index modification checks and traversal. + /// * `options` + /// - a way to configure both paths of the operation. + #[allow(clippy::too_many_arguments)] + pub fn index_as_worktree_with_renames<'index, T, U, Find, E>( + index: &'index gix_index::State, + worktree: &Path, + collector: &mut impl VisitEntry<'index, ContentChange = T, SubmoduleStatus = U>, + compare: impl CompareBlobs + Send + Clone, + submodule: impl SubmoduleStatus + Send + Clone, + objects: Find, + progress: &mut dyn gix_features::progress::Progress, + mut ctx: Context<'_>, + options: Options, + ) -> Result + where + T: Send + Clone, + U: Send + Clone, + E: std::error::Error + Send + Sync + 'static, + Find: gix_object::Find + gix_object::FindHeader + Send + Clone, + { + gix_features::parallel::threads(|scope| -> Result { + let (tx, rx) = std::sync::mpsc::channel(); + let walk_outcome = options + .dirwalk + .map(|options| { + gix_features::parallel::build_thread() + .name("gix_status::dirwalk".into()) + .spawn_scoped(scope, { + let tx = tx.clone(); + let mut collect = dirwalk::Delegate { + tx, + should_interrupt: ctx.should_interrupt, + }; + let dirwalk_ctx = ctx.dirwalk; + let objects = objects.clone(); + let mut excludes = match ctx.resource_cache.attr_stack.state() { + State::CreateDirectoryAndAttributesStack { .. } | State::AttributesStack(_) => None, + State::AttributesAndIgnoreStack { .. } | State::IgnoreStack(_) => { + Some(ctx.resource_cache.attr_stack.clone()) + } + }; + let mut pathspec_attr_stack = ctx + .pathspec + .patterns() + .any(|p| !p.attributes.is_empty()) + .then(|| ctx.resource_cache.attr_stack.clone()); + let mut pathspec = ctx.pathspec.clone(); + move || -> Result<_, Error> { + gix_dir::walk( + worktree, + gix_dir::walk::Context { + should_interrupt: Some(ctx.should_interrupt), + git_dir_realpath: dirwalk_ctx.git_dir_realpath, + current_dir: dirwalk_ctx.current_dir, + index, + ignore_case_index_lookup: dirwalk_ctx.ignore_case_index_lookup, + pathspec: &mut pathspec, + pathspec_attributes: &mut |relative_path, case, is_dir, out| { + let stack = pathspec_attr_stack + .as_mut() + .expect("can only be called if attributes are used in patterns"); + stack + .set_case(case) + .at_entry(relative_path, Some(is_dir_to_mode(is_dir)), &objects) + .map_or(false, |platform| platform.matching_attributes(out)) + }, + excludes: excludes.as_mut(), + objects: &objects, + explicit_traversal_root: Some(worktree), + }, + options, + &mut collect, + ) + .map_err(Error::DirWalk) + } + }) + .map_err(Error::SpawnThread) + }) + .transpose()?; + + let entries = &index.entries()[index + .prefixed_entries_range(ctx.pathspec.common_prefix()) + .unwrap_or(0..index.entries().len())]; + + let filter = options.rewrites.is_some().then(|| { + ( + ctx.resource_cache.filter.worktree_filter.clone(), + ctx.resource_cache.attr_stack.clone(), + ) + }); + let tracked_modifications_outcome = gix_features::parallel::build_thread() + .name("gix_status::index_as_worktree".into()) + .spawn_scoped(scope, { + let mut collect = tracked_modifications::Delegate { tx }; + let objects = objects.clone(); + let stack = ctx.resource_cache.attr_stack.clone(); + let filter = ctx.resource_cache.filter.worktree_filter.clone(); + move || -> Result<_, Error> { + crate::index_as_worktree( + index, + worktree, + &mut collect, + compare, + submodule, + objects, + progress, + crate::index_as_worktree::Context { + pathspec: ctx.pathspec, + stack, + filter, + should_interrupt: ctx.should_interrupt, + }, + options.tracked_file_modifications, + ) + .map_err(Error::TrackedFileModifications) + } + }) + .map_err(Error::SpawnThread)?; + + let tracker = options + .rewrites + .map(gix_diff::rewrites::Tracker::>::new) + .zip(filter); + let rewrite_outcome = match tracker { + Some((mut tracker, (mut filter, mut attrs))) => { + let mut entries_for_sorting = options.sorting.map(|_| Vec::new()); + let mut buf = Vec::new(); + for event in rx { + let (change, location) = match event { + Event::IndexEntry(record) => { + let location = Cow::Borrowed(record.relative_path); + (rewrite::ModificationOrDirwalkEntry::Modification(record), location) + } + Event::DirEntry(entry, collapsed_directory_status) => { + let location = Cow::Owned(entry.rela_path.clone()); + ( + rewrite::ModificationOrDirwalkEntry::DirwalkEntry { + id: rewrite::calculate_worktree_id( + options.object_hash, + worktree, + entry.disk_kind, + entry.rela_path.as_bstr(), + &mut filter, + &mut attrs, + &objects, + &mut buf, + ctx.should_interrupt, + )?, + entry, + collapsed_directory_status, + }, + location, + ) + } + }; + if let Some(v) = entries_for_sorting.as_mut() { + v.push((change, location)); + } else if let Some(change) = tracker.try_push_change(change, location.as_ref()) { + collector.visit_entry(rewrite::change_to_entry(change, entries)) + } + } + + let mut entries_for_sorting = entries_for_sorting.map(|mut v| { + v.sort_by(|a, b| a.1.cmp(&b.1)); + let mut remaining = Vec::new(); + for (change, location) in v { + if let Some(change) = tracker.try_push_change(change, location.as_ref()) { + remaining.push(rewrite::change_to_entry(change, entries)); + } + } + remaining + }); + + let outcome = tracker.emit( + |dest, src| { + match src { + None => { + let entry = rewrite::change_to_entry(dest.change, entries); + if let Some(v) = entries_for_sorting.as_mut() { + v.push(entry); + } else { + collector.visit_entry(entry) + } + } + Some(src) => { + let rewrite::ModificationOrDirwalkEntry::DirwalkEntry { + id, + entry, + collapsed_directory_status, + } = dest.change + else { + unreachable!("BUG: only possible destinations are dirwalk entries (additions)"); + }; + let source = match src.change { + ModificationOrDirwalkEntry::Modification(record) => { + RewriteSource::RewriteFromIndex { + index_entries: entries, + source_entry: record.entry, + source_entry_index: record.entry_index, + source_rela_path: record.relative_path, + source_status: record.status.clone(), + } + } + ModificationOrDirwalkEntry::DirwalkEntry { + id, + entry, + collapsed_directory_status, + } => RewriteSource::CopyFromDirectoryEntry { + source_dirwalk_entry: entry.clone(), + source_dirwalk_entry_collapsed_directory_status: + *collapsed_directory_status, + source_dirwalk_entry_id: *id, + }, + }; + + let entry = Entry::Rewrite { + source, + dirwalk_entry: entry, + dirwalk_entry_collapsed_directory_status: collapsed_directory_status, + dirwalk_entry_id: id, + diff: src.diff, + copy: src.kind == gix_diff::rewrites::tracker::visit::SourceKind::Copy, + }; + if let Some(v) = entries_for_sorting.as_mut() { + v.push(entry); + } else { + collector.visit_entry(entry); + } + } + } + gix_diff::tree::visit::Action::Continue + }, + &mut ctx.resource_cache, + &objects, + |_cb| { + // NOTE: to make this work, we'd want to wait the index modification check to complete. + // Then it's possible to efficiently emit the tracked files along with what we already sent, + // i.e. untracked and ignored files. + gix_features::trace::debug!("full-tree copy tracking isn't currently supported"); + Ok::<_, std::io::Error>(()) + }, + )?; + + if let Some(mut v) = entries_for_sorting { + v.sort_by(|a, b| a.destination_rela_path().cmp(b.destination_rela_path())); + for entry in v { + collector.visit_entry(entry); + } + } + Some(outcome) + } + None => { + let mut entries_for_sorting = options.sorting.map(|_| Vec::new()); + for event in rx { + let entry = match event { + Event::IndexEntry(record) => Entry::Modification { + entries, + entry: record.entry, + entry_index: record.entry_index, + rela_path: record.relative_path, + status: record.status, + }, + Event::DirEntry(entry, collapsed_directory_status) => Entry::DirectoryContents { + entry, + collapsed_directory_status, + }, + }; + + if let Some(v) = entries_for_sorting.as_mut() { + v.push(entry); + } else { + collector.visit_entry(entry); + } + } + + if let Some(mut v) = entries_for_sorting { + v.sort_by(|a, b| a.destination_rela_path().cmp(b.destination_rela_path())); + for entry in v { + collector.visit_entry(entry); + } + } + None + } + }; + + let walk_outcome = walk_outcome + .map(|handle| handle.join().expect("no panic")) + .transpose()?; + let tracked_modifications_outcome = tracked_modifications_outcome.join().expect("no panic")?; + Ok(Outcome { + dirwalk: walk_outcome.map(|t| t.0), + tracked_file_modification: tracked_modifications_outcome, + rewrites: rewrite_outcome, + }) + }) + } + + enum Event<'index, T, U> { + IndexEntry(crate::index_as_worktree::Record<'index, T, U>), + DirEntry(gix_dir::Entry, Option), + } + + mod tracked_modifications { + use crate::index_as_worktree::{EntryStatus, Record}; + use crate::index_as_worktree_with_renames::function::Event; + use bstr::BStr; + use gix_index::Entry; + + pub(super) struct Delegate<'index, T, U> { + pub(super) tx: std::sync::mpsc::Sender>, + } + + impl<'index, T, U> crate::index_as_worktree::VisitEntry<'index> for Delegate<'index, T, U> { + type ContentChange = T; + type SubmoduleStatus = U; + + fn visit_entry( + &mut self, + _entries: &'index [Entry], + entry: &'index Entry, + entry_index: usize, + rela_path: &'index BStr, + status: EntryStatus, + ) { + self.tx + .send(Event::IndexEntry(Record { + entry, + entry_index, + relative_path: rela_path, + status, + })) + .ok(); + } + } + } + + mod dirwalk { + use super::Event; + use gix_dir::entry::Status; + use gix_dir::walk::Action; + use gix_dir::EntryRef; + use std::sync::atomic::{AtomicBool, Ordering}; + + pub(super) struct Delegate<'index, 'a, T, U> { + pub(super) tx: std::sync::mpsc::Sender>, + pub(super) should_interrupt: &'a AtomicBool, + } + + impl<'index, 'a, T, U> gix_dir::walk::Delegate for Delegate<'index, 'a, T, U> { + fn emit(&mut self, entry: EntryRef<'_>, collapsed_directory_status: Option) -> Action { + let entry = entry.to_owned(); + self.tx.send(Event::DirEntry(entry, collapsed_directory_status)).ok(); + + if self.should_interrupt.load(Ordering::Relaxed) { + Action::Cancel + } else { + Action::Continue + } + } + } + } + + mod rewrite { + use crate::index_as_worktree::{Change, EntryStatus}; + use crate::index_as_worktree_with_renames::{Entry, Error}; + use bstr::BStr; + use gix_diff::rewrites::tracker::ChangeKind; + use gix_dir::entry::Kind; + use gix_filter::pipeline::convert::ToGitOutcome; + use gix_hash::oid; + use gix_object::tree::EntryMode; + use std::io::Read; + use std::path::Path; + + #[derive(Clone)] + pub enum ModificationOrDirwalkEntry<'index, T, U> + where + T: Clone, + U: Clone, + { + Modification(crate::index_as_worktree::Record<'index, T, U>), + DirwalkEntry { + id: gix_hash::ObjectId, + entry: gix_dir::Entry, + collapsed_directory_status: Option, + }, + } + + impl<'index, T, U> gix_diff::rewrites::tracker::Change for ModificationOrDirwalkEntry<'index, T, U> + where + T: Clone, + U: Clone, + { + fn id(&self) -> &oid { + match self { + ModificationOrDirwalkEntry::Modification(m) => &m.entry.id, + ModificationOrDirwalkEntry::DirwalkEntry { id, .. } => id, + } + } + + fn kind(&self) -> ChangeKind { + match self { + ModificationOrDirwalkEntry::Modification(m) => match &m.status { + EntryStatus::Conflict(_) | EntryStatus::IntentToAdd | EntryStatus::NeedsUpdate(_) => { + ChangeKind::Modification + } + EntryStatus::Change(c) => match c { + Change::Removed => ChangeKind::Deletion, + Change::Type | Change::Modification { .. } | Change::SubmoduleModification(_) => { + ChangeKind::Modification + } + }, + }, + ModificationOrDirwalkEntry::DirwalkEntry { .. } => ChangeKind::Addition, + } + } + + fn entry_mode(&self) -> EntryMode { + match self { + ModificationOrDirwalkEntry::Modification(c) => c.entry.mode.to_tree_entry_mode(), + ModificationOrDirwalkEntry::DirwalkEntry { entry, .. } => entry.disk_kind.map(|kind| { + match kind { + Kind::File => gix_object::tree::EntryKind::Blob, + Kind::Symlink => gix_object::tree::EntryKind::Link, + Kind::Repository | Kind::Directory => gix_object::tree::EntryKind::Tree, + } + .into() + }), + } + .unwrap_or(gix_object::tree::EntryKind::Blob.into()) + } + + fn id_and_entry_mode(&self) -> (&oid, EntryMode) { + (self.id(), self.entry_mode()) + } + } + + /// Note that for non-files, we always return a null-sha and assume that the rename-tracking + /// does nothing for these anyway. + #[allow(clippy::too_many_arguments)] + pub(super) fn calculate_worktree_id( + object_hash: gix_hash::Kind, + worktree_root: &Path, + disk_kind: Option, + rela_path: &BStr, + filter: &mut gix_filter::Pipeline, + attrs: &mut gix_worktree::Stack, + objects: &dyn gix_object::Find, + buf: &mut Vec, + should_interrupt: &std::sync::atomic::AtomicBool, + ) -> Result { + let Some(kind) = disk_kind else { + return Ok(object_hash.null()); + }; + + Ok(match kind { + Kind::File => { + let platform = attrs + .at_entry(rela_path, None, objects) + .map_err(Error::SetAttributeContext)?; + let rela_path = gix_path::from_bstr(rela_path); + let file_path = worktree_root.join(rela_path.as_ref()); + let file = std::fs::File::open(&file_path).map_err(Error::OpenWorktreeFile)?; + let out = filter.convert_to_git( + file, + rela_path.as_ref(), + &mut |_path, attrs| { + platform.matching_attributes(attrs); + }, + &mut |_buf| Ok(None), + )?; + match out { + ToGitOutcome::Unchanged(mut file) => gix_object::compute_stream_hash( + object_hash, + gix_object::Kind::Blob, + &mut file, + file_path.metadata().map_err(Error::OpenWorktreeFile)?.len(), + &mut gix_features::progress::Discard, + should_interrupt, + ) + .map_err(Error::HashFile)?, + ToGitOutcome::Buffer(buf) => gix_object::compute_hash(object_hash, gix_object::Kind::Blob, buf), + ToGitOutcome::Process(mut stream) => { + buf.clear(); + stream.read_to_end(buf).map_err(Error::HashFile)?; + gix_object::compute_hash(object_hash, gix_object::Kind::Blob, buf) + } + } + } + Kind::Symlink => { + let path = worktree_root.join(gix_path::from_bstr(rela_path)); + let target = gix_path::into_bstr(std::fs::read_link(path).map_err(Error::ReadLink)?); + gix_object::compute_hash(object_hash, gix_object::Kind::Blob, &target) + } + Kind::Directory | Kind::Repository => object_hash.null(), + }) + } + + #[inline] + pub(super) fn change_to_entry<'index, T, U>( + change: ModificationOrDirwalkEntry<'index, T, U>, + entries: &'index [gix_index::Entry], + ) -> Entry<'index, T, U> + where + T: Clone, + U: Clone, + { + match change { + ModificationOrDirwalkEntry::Modification(r) => Entry::Modification { + entries, + entry: r.entry, + entry_index: r.entry_index, + rela_path: r.relative_path, + status: r.status, + }, + ModificationOrDirwalkEntry::DirwalkEntry { + id: _, + entry, + collapsed_directory_status, + } => Entry::DirectoryContents { + entry, + collapsed_directory_status, + }, + } + } + } +} diff --git a/gix-status/src/index_as_worktree_with_renames/recorder.rs b/gix-status/src/index_as_worktree_with_renames/recorder.rs new file mode 100644 index 00000000000..81d05f1b064 --- /dev/null +++ b/gix-status/src/index_as_worktree_with_renames/recorder.rs @@ -0,0 +1,17 @@ +use crate::index_as_worktree_with_renames::{Entry, VisitEntry}; + +/// Convenience implementation of [`VisitEntry`] that collects all changes into a `Vec`. +#[derive(Debug, Default)] +pub struct Recorder<'index, T = (), U = ()> { + /// The collected changes. + pub records: Vec>, +} + +impl<'index, T: Send, U: Send> VisitEntry<'index> for Recorder<'index, T, U> { + type ContentChange = T; + type SubmoduleStatus = U; + + fn visit_entry(&mut self, entry: Entry<'index, Self::ContentChange, Self::SubmoduleStatus>) { + self.records.push(entry) + } +} diff --git a/gix-status/src/index_as_worktree_with_renames/types.rs b/gix-status/src/index_as_worktree_with_renames/types.rs new file mode 100644 index 00000000000..84ba2e24ec5 --- /dev/null +++ b/gix-status/src/index_as_worktree_with_renames/types.rs @@ -0,0 +1,371 @@ +use crate::index_as_worktree::{Change, EntryStatus}; +use bstr::{BStr, ByteSlice}; +use std::sync::atomic::AtomicBool; + +/// The error returned by [index_as_worktree_with_renames()`](crate::index_as_worktree_with_renames()). +#[derive(Debug, thiserror::Error)] +#[allow(missing_docs)] +pub enum Error { + #[error(transparent)] + TrackedFileModifications(#[from] crate::index_as_worktree::Error), + #[error(transparent)] + DirWalk(gix_dir::walk::Error), + #[error(transparent)] + SpawnThread(std::io::Error), + #[error("Failed to change the context for querying gitattributes to the respective path")] + SetAttributeContext(std::io::Error), + #[error("Could not open worktree file for reading")] + OpenWorktreeFile(std::io::Error), + #[error(transparent)] + HashFile(std::io::Error), + #[error("Could not read worktree link content")] + ReadLink(std::io::Error), + #[error(transparent)] + ConvertToGit(#[from] gix_filter::pipeline::convert::to_git::Error), + #[error(transparent)] + RewriteTracker(#[from] gix_diff::rewrites::tracker::emit::Error), +} + +/// The way all output should be sorted. +#[derive(Clone, Copy, Default, Debug, Eq, PartialEq, PartialOrd, Ord, Hash)] +pub enum Sorting { + /// The entries are sorted by their path in a case-sensitive fashion. + #[default] + ByPathCaseSensitive, +} + +/// Provide additional information collected during the runtime of [`index_as_worktree_with_renames()`](crate::index_as_worktree_with_renames()). +#[derive(Clone, Debug, Default, PartialEq)] +pub struct Outcome { + /// The outcome of the modification check of tracked files. + pub tracked_file_modification: crate::index_as_worktree::Outcome, + /// The outcome of the directory walk, or `None` if its [options](Options::dirwalk) also weren't present which means + /// the dirwalk never ran. + pub dirwalk: Option, + /// The result of the rewrite operation, if [rewrites were configured](Options::rewrites). + pub rewrites: Option, +} + +/// Either an index entry for renames or another directory entry in case of copies. +#[derive(Clone, PartialEq, Debug)] +pub enum RewriteSource<'index, ContentChange, SubmoduleStatus> { + /// The source originates in the index and is detected as missing in the working tree. + /// This can also happen for copies. + RewriteFromIndex { + /// All entries in the index. + index_entries: &'index [gix_index::Entry], + /// The entry that is the source of the rewrite, which means it was removed on disk, + /// equivalent to [Change::Removed](crate::index_as_worktree::Change::Removed). + /// + /// Note that the [entry-id](gix_index::Entry::id) is the content-id of the source of the rewrite. + source_entry: &'index gix_index::Entry, + /// The index of the `source_entry` for lookup in `index_entries` - useful to look at neighbors. + source_entry_index: usize, + /// The repository-relative path of the `source_entry`. + source_rela_path: &'index BStr, + /// The computed status of the `source_entry`. + source_status: EntryStatus, + }, + /// This source originates in the directory tree and is always the source of copies. + CopyFromDirectoryEntry { + /// The source of the copy operation, which is also an entry of the directory walk. + /// + /// Note that its [`rela_path`](gix_dir::EntryRef::rela_path) is the source of the rewrite. + source_dirwalk_entry: gix_dir::Entry, + /// `collapsed_directory_status` is `Some(dir_status)` if this `source_dirwalk_entry` was part of a directory with the given + /// `dir_status` that wasn't the same as the one of `source_dirwalk_entry` and if [gix_dir::walk::Options::emit_collapsed] was + /// [CollapsedEntriesEmissionMode::OnStatusMismatch](gix_dir::walk::CollapsedEntriesEmissionMode::OnStatusMismatch). + /// It will also be `Some(dir_status)` if that option was [CollapsedEntriesEmissionMode::All](gix_dir::walk::CollapsedEntriesEmissionMode::All). + source_dirwalk_entry_collapsed_directory_status: Option, + /// The object id as it would appear if the entry was written to the object database. + /// It's the same as `dirwalk_entry_id`, or `diff` is `Some(_)` to indicate that the copy was determined by similarity. + source_dirwalk_entry_id: gix_hash::ObjectId, + }, +} + +/// An 'entry' in the sense of a merge of modified tracked files and results from a directory walk. +#[derive(Clone, PartialEq, Debug)] +pub enum Entry<'index, ContentChange, SubmoduleStatus> { + /// A tracked file was modified, and index-specific information is passed. + Modification { + /// All entries in the index. + entries: &'index [gix_index::Entry], + /// The entry with modifications. + entry: &'index gix_index::Entry, + /// The index of the `entry` for lookup in `entries` - useful to look at neighbors. + entry_index: usize, + /// The repository-relative path of the entry. + rela_path: &'index BStr, + /// The computed status of the entry. + status: EntryStatus, + }, + /// An entry returned by the directory walk, without any relation to the index. + /// + /// This can happen if ignored files are returned as well, or if rename-tracking is disabled. + DirectoryContents { + /// The entry found during the disk traversal. + entry: gix_dir::Entry, + /// `collapsed_directory_status` is `Some(dir_status)` if this `entry` was part of a directory with the given + /// `dir_status` that wasn't the same as the one of `entry` and if [gix_dir::walk::Options::emit_collapsed] was + /// [CollapsedEntriesEmissionMode::OnStatusMismatch](gix_dir::walk::CollapsedEntriesEmissionMode::OnStatusMismatch). + /// It will also be `Some(dir_status)` if that option was [CollapsedEntriesEmissionMode::All](gix_dir::walk::CollapsedEntriesEmissionMode::All). + collapsed_directory_status: Option, + }, + /// The rewrite tracking discovered a match between a deleted and added file, and considers them equal enough, + /// depending on the tracker settings. + /// + /// Note that the source of the rewrite is always the index as it detects the absence of entries, something that + /// can't be done during a directory walk. + Rewrite { + /// The source of the rewrite operation. + source: RewriteSource<'index, ContentChange, SubmoduleStatus>, + /// The untracked entry found during the disk traversal, the destination of the rewrite. + /// + /// Note that its [`rela_path`](gix_dir::EntryRef::rela_path) is the destination of the rewrite, and the current + /// location of the entry. + dirwalk_entry: gix_dir::Entry, + /// `collapsed_directory_status` is `Some(dir_status)` if this `dirwalk_entry` was part of a directory with the given + /// `dir_status` that wasn't the same as the one of `dirwalk_entry` and if [gix_dir::walk::Options::emit_collapsed] was + /// [CollapsedEntriesEmissionMode::OnStatusMismatch](gix_dir::walk::CollapsedEntriesEmissionMode::OnStatusMismatch). + /// It will also be `Some(dir_status)` if that option was [CollapsedEntriesEmissionMode::All](gix_dir::walk::CollapsedEntriesEmissionMode::All). + dirwalk_entry_collapsed_directory_status: Option, + /// The object id after the rename, specifically hashed in order to determine equality. + dirwalk_entry_id: gix_hash::ObjectId, + /// It's `None` if the 'source.id' is equal to `dirwalk_entry_id`, as identity made an actual diff computation unnecessary. + /// Otherwise, and if enabled, it's `Some(stats)` to indicate how similar both entries were. + diff: Option, + /// If true, this rewrite is created by copy, and 'source.id' is pointing to its source. + /// Otherwise, it's a rename, and 'source.id' points to a deleted object, + /// as renames are tracked as deletions and additions of the same or similar content. + copy: bool, + }, +} + +/// An easy to grasp summary of the changes of the worktree compared to the index. +#[derive(Clone, Copy, Debug, Eq, PartialEq, PartialOrd, Ord, Hash)] +pub enum Summary { + /// An entry exists in the index but doesn't in the worktree. + Removed, + /// A file exists in the worktree but doesn't have a corresponding entry in the index. + /// + /// In a `git status`, this would be an untracked file. + Added, + /// A file or submodule was modified, compared to the state recorded in the index. + /// On Unix, the change of executable bit also counts as modification. + /// + /// If the modification is a submodule, it could also stem from various other factors, like + /// having modified or untracked files, or changes in the index. + Modified, + /// The type of the entry in the worktree changed compared to the index. + /// + /// This can happen if a file in the worktree now is a directory, or a symlink, for example. + TypeChange, + /// A match between an entry in the index and a differently named file in the worktree was detected, + /// considering the index the source of a rename operation, and the worktree file the destination. + /// + /// Note that the renamed file may also have been modified, but is considered similar enough. + /// + /// To obtain this state, rewrite-tracking must have been enabled, as otherwise the source would be + /// considered `Removed` and the destination would be considered `Added`. + Renamed, + /// A match between an entry in the index and a differently named file in the worktree was detected, + /// considering the index the source of the copy of a worktree file. + /// + /// Note that the copied file may also have been modified, but is considered similar enough. + /// + /// To obtain this state, rewrite-and-copy-tracking must have been enabled, as otherwise the source would be + /// considered `Removed` and the destination would be considered `Added`. + Copied, + /// An index entry with a corresponding worktree file that corresponds to an untracked worktree + /// file marked with `git add --intent-to-add`. + /// + /// This means it's not available in the object database yet even though now an entry exists + /// that represents the worktree file. + /// The entry represents the promise of adding a new file, no matter the actual stat or content. + /// Effectively this means nothing changed. + /// This also means the file is still present, and that no detailed change checks were performed. + IntentToAdd, + /// Describes a conflicting entry in the index, which also means that + /// no further comparison to the worktree file was performed. + /// + /// As this variant only describes the state of the index, the corresponding worktree file may + /// or may not exist. + Conflict, +} + +/// Access +impl RewriteSource<'_, ContentChange, SubmoduleStatus> { + /// The repository-relative path of this source. + pub fn rela_path(&self) -> &BStr { + match self { + RewriteSource::RewriteFromIndex { source_rela_path, .. } => source_rela_path, + RewriteSource::CopyFromDirectoryEntry { + source_dirwalk_entry, .. + } => source_dirwalk_entry.rela_path.as_bstr(), + } + } +} + +/// Access +impl Entry<'_, ContentChange, SubmoduleStatus> { + /// Return a summary of the entry as digest of its status, or `None` if this entry is + /// created from the directory walk and is *not untracked*, or if it is merely to communicate + /// a needed update to the index entry. + pub fn summary(&self) -> Option { + Some(match self { + Entry::Modification { + status: EntryStatus::Conflict(_), + .. + } => Summary::Conflict, + Entry::Modification { + status: EntryStatus::IntentToAdd, + .. + } => Summary::IntentToAdd, + Entry::Modification { + status: EntryStatus::NeedsUpdate(_), + .. + } => return None, + Entry::Modification { + status: EntryStatus::Change(change), + .. + } => match change { + Change::SubmoduleModification(_) | Change::Modification { .. } => Summary::Modified, + Change::Type => Summary::TypeChange, + Change::Removed => Summary::Removed, + }, + Entry::DirectoryContents { entry, .. } => { + if matches!(entry.status, gix_dir::entry::Status::Untracked) { + Summary::Added + } else { + return None; + } + } + Entry::Rewrite { copy, .. } => { + if *copy { + Summary::Copied + } else { + Summary::Renamed + } + } + }) + } + /// The repository-relative path at which the source of a rewrite is located. + /// + /// If this isn't a rewrite, the path is the location of the entry itself. + pub fn source_rela_path(&self) -> &BStr { + match self { + Entry::Modification { rela_path, .. } => rela_path, + Entry::DirectoryContents { entry, .. } => entry.rela_path.as_bstr(), + Entry::Rewrite { source, .. } => source.rela_path(), + } + } + + /// The repository-relative path at which the destination of a rewrite is located. + /// + /// If this isn't a rewrite, the path is the location of the entry itself. + pub fn destination_rela_path(&self) -> &BStr { + match self { + Entry::Modification { rela_path, .. } => rela_path, + Entry::DirectoryContents { entry, .. } => entry.rela_path.as_bstr(), + Entry::Rewrite { dirwalk_entry, .. } => dirwalk_entry.rela_path.as_bstr(), + } + } +} + +/// Options for use in [index_as_worktree_with_renames()](crate::index_as_worktree_with_renames()). +#[derive(Clone, Default)] +pub struct Options { + /// The way all output should be sorted. + /// + /// If `None`, and depending on the `rewrites` field, output will be immediate but the output order + /// isn't determined, and may differ between two runs. `rewrites` also depend on the order of entries that + /// are presented to it, hence for deterministic results, sorting needs to be enabled. + /// + /// If `Some(_)`, all entries are collected beforehand, so they can be sorted before outputting any of them + /// to the user. + /// + /// If immediate output of entries in any order is desired, this should be `None`, + /// along with `rewrites` being `None` as well. + pub sorting: Option, + /// The kind of hash to create when hashing worktree entries. + pub object_hash: gix_hash::Kind, + /// Options to configure how modifications to tracked files should be obtained. + pub tracked_file_modifications: crate::index_as_worktree::Options, + /// Options to control the directory walk that informs about untracked files. + /// + /// Note that we forcefully disable emission of tracked files to avoid any overlap + /// between emissions to indicate modifications, and those that are obtained by + /// the directory walk. + /// + /// If `None`, the directory walk portion will not run at all, yielding data similar + /// to a bare [index_as_worktree()](crate::index_as_worktree()) call. + pub dirwalk: Option, + /// The configuration for the rewrite tracking. Note that if set, the [`dirwalk`](Self::dirwalk) should be configured + /// to *not* collapse untracked and ignored entries, as rewrite tracking is on a file-by-file basis. + /// Also note that when `Some(_)`, it will collect certain changes depending on the exact configuration, which typically increases + /// the latency until the first entries are received. Note that some entries are never candidates for renames, which means + /// they are forwarded to the caller right away. + /// + /// If `None`, no tracking will occur, which means that all output becomes visible to the delegate immediately. + pub rewrites: Option, +} + +/// The context for [index_as_worktree_with_renames()`](crate::index_as_worktree_with_renames()). +pub struct Context<'a> { + /// The pathspec to limit the amount of paths that are checked. Can be empty to allow all paths. + /// + /// Note that these are expected to have a [common_prefix()](gix_pathspec::Search::common_prefix()) according + /// to the prefix of the repository to efficiently limit the scope of the paths we process, both for the + /// index modifications as well as for the directory walk. + pub pathspec: gix_pathspec::Search, + /// A fully-configured platform capable of producing diffable buffers similar to what Git would do, for use + /// with rewrite tracking. + /// + /// Note that it contains resources that are additionally used here: + /// + /// * `attr_stack` + /// - A stack pre-configured to allow accessing attributes for each entry, as required for `filter` + /// and possibly pathspecs. + /// It *may* also allow accessing `.gitignore` information for use in the directory walk. + /// If no excludes information is present, the directory walk will identify ignored files as untracked, which + /// might be desirable under certain circumstances. + /// * `filter` + /// - A filter to be able to perform conversions from and to the worktree format. + /// It is needed to potentially refresh the index with data read from the worktree, which needs to be converted back + /// to the form stored in Git. + pub resource_cache: gix_diff::blob::Platform, + /// A flag to query to learn if cancellation is requested. + pub should_interrupt: &'a AtomicBool, + /// The context for the directory walk. + pub dirwalk: DirwalkContext<'a>, +} + +/// All information that is required to perform a [dirwalk](gix_dir::walk()). +pub struct DirwalkContext<'a> { + /// The `git_dir` of the parent repository, after a call to [`gix_path::realpath()`]. + /// + /// It's used to help us differentiate our own `.git` directory from nested unrelated repositories, + /// which is needed if `core.worktree` is used to nest the `.git` directory deeper within. + pub git_dir_realpath: &'a std::path::Path, + /// The current working directory as returned by `gix_fs::current_dir()` to assure it respects `core.precomposeUnicode`. + /// It's used to produce the realpath of the git-dir of a repository candidate to assure it's not our own repository. + pub current_dir: &'a std::path::Path, + /// A utility to lookup index entries faster, and deal with ignore-case handling. + /// + /// Must be set if [`ignore_case`](gix_dir::walk::Options::ignore_case) is `true`, or else some entries won't be found if their case is different. + /// + /// [Read more in `gix-dir`](gix_dir::walk::Context::ignore_case_index_lookup). + pub ignore_case_index_lookup: Option<&'a gix_index::AccelerateLookup<'a>>, +} + +/// Observe the status of an entry by comparing an index entry to the worktree, along +/// with potential directory walk results. +pub trait VisitEntry<'a> { + /// Data generated by comparing an entry with a file. + type ContentChange; + /// Data obtained when checking the submodule status. + type SubmoduleStatus; + /// Observe the `status` of `entry` at the repository-relative `rela_path` at `entry_index` + /// (for accessing `entry` and surrounding in the complete list of `entries`). + fn visit_entry(&mut self, entry: Entry<'a, Self::ContentChange, Self::SubmoduleStatus>); +} diff --git a/gix-status/src/lib.rs b/gix-status/src/lib.rs index 0749c5bd6cb..86532fbad16 100644 --- a/gix-status/src/lib.rs +++ b/gix-status/src/lib.rs @@ -6,11 +6,23 @@ //! * find untracked files //! //! While also being able to check check if the working tree is dirty, quickly. +//! +//! ### Feature Flags +#![cfg_attr( + all(doc, feature = "document-features"), + doc = ::document_features::document_features!() +)] +#![cfg_attr(all(doc, feature = "document-features"), feature(doc_cfg, doc_auto_cfg))] #![deny(missing_docs, rust_2018_idioms, unsafe_code)] pub mod index_as_worktree; pub use index_as_worktree::function::index_as_worktree; +#[cfg(feature = "worktree-rewrites")] +pub mod index_as_worktree_with_renames; +#[cfg(feature = "worktree-rewrites")] +pub use index_as_worktree_with_renames::function::index_as_worktree_with_renames; + /// A stack that validates we are not going through a symlink in a way that is read-only. /// /// It can efficiently validate paths when these are queried in sort-order, which leads to each component @@ -20,3 +32,11 @@ pub struct SymlinkCheck { } mod stack; + +fn is_dir_to_mode(is_dir: bool) -> gix_index::entry::Mode { + if is_dir { + gix_index::entry::Mode::DIR + } else { + gix_index::entry::Mode::FILE + } +} diff --git a/gix-status/tests/Cargo.toml b/gix-status/tests/Cargo.toml index 2e138b9a184..576a81829c4 100644 --- a/gix-status/tests/Cargo.toml +++ b/gix-status/tests/Cargo.toml @@ -17,10 +17,15 @@ path = "worktree.rs" gix-features-parallel = ["gix-features/parallel"] [dev-dependencies] -gix-status = { path = ".." } +gix-status = { path = "..", features = ["worktree-rewrites"] } gix-testtools = { path = "../../tests/tools" } gix-index = { path = "../../gix-index" } gix-fs = { path = "../../gix-fs" } +gix-diff = { path = "../../gix-diff" } +gix-filter = { path = "../../gix-filter" } +gix-path = { path = "../../gix-path" } +gix-dir = { path = "../../gix-dir" } +gix-odb = { path = "../../gix-odb" } gix-hash = { path = "../../gix-hash" } gix-object = { path = "../../gix-object" } gix-features = { path = "../../gix-features" } @@ -28,4 +33,4 @@ gix-pathspec = { path = "../../gix-pathspec" } gix-worktree = { path = "../../gix-worktree" } filetime = "0.2.15" bstr = { version = "1.3.0", default-features = false } - +pretty_assertions = "1.4.0" diff --git a/gix-status/tests/fixtures/conflicts.sh b/gix-status/tests/fixtures/conflicts.sh old mode 100644 new mode 100755 index 682a214cf99..6b5c7e5fe81 --- a/gix-status/tests/fixtures/conflicts.sh +++ b/gix-status/tests/fixtures/conflicts.sh @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash set -eu -o pipefail (git init both-deleted && cd both-deleted diff --git a/gix-status/tests/fixtures/generated-archives/conflicts.tar.xz b/gix-status/tests/fixtures/generated-archives/conflicts.tar.xz index a65d273c557..8b133639543 100644 Binary files a/gix-status/tests/fixtures/generated-archives/conflicts.tar.xz and b/gix-status/tests/fixtures/generated-archives/conflicts.tar.xz differ diff --git a/gix-status/tests/fixtures/generated-archives/racy_git.tar.xz b/gix-status/tests/fixtures/generated-archives/racy_git.tar.xz index 8dea720461b..ba629301894 100644 Binary files a/gix-status/tests/fixtures/generated-archives/racy_git.tar.xz and b/gix-status/tests/fixtures/generated-archives/racy_git.tar.xz differ diff --git a/gix-status/tests/fixtures/generated-archives/status_conflict.tar.xz b/gix-status/tests/fixtures/generated-archives/status_conflict.tar.xz index 4bd0a7a6e7e..9f05ea3d774 100644 Binary files a/gix-status/tests/fixtures/generated-archives/status_conflict.tar.xz and b/gix-status/tests/fixtures/generated-archives/status_conflict.tar.xz differ diff --git a/gix-status/tests/fixtures/generated-archives/status_intent_to_add.tar.xz b/gix-status/tests/fixtures/generated-archives/status_intent_to_add.tar.xz index cb869133b8a..4d555d42754 100644 Binary files a/gix-status/tests/fixtures/generated-archives/status_intent_to_add.tar.xz and b/gix-status/tests/fixtures/generated-archives/status_intent_to_add.tar.xz differ diff --git a/gix-status/tests/fixtures/generated-archives/status_many.tar.xz b/gix-status/tests/fixtures/generated-archives/status_many.tar.xz new file mode 100644 index 00000000000..208a5af7fa0 Binary files /dev/null and b/gix-status/tests/fixtures/generated-archives/status_many.tar.xz differ diff --git a/gix-status/tests/fixtures/generated-archives/status_removed.tar.xz b/gix-status/tests/fixtures/generated-archives/status_removed.tar.xz index 2f104bfe111..b708ee74a47 100644 Binary files a/gix-status/tests/fixtures/generated-archives/status_removed.tar.xz and b/gix-status/tests/fixtures/generated-archives/status_removed.tar.xz differ diff --git a/gix-status/tests/fixtures/generated-archives/status_submodule.tar.xz b/gix-status/tests/fixtures/generated-archives/status_submodule.tar.xz index 8ce54339f5e..ca525710f26 100644 Binary files a/gix-status/tests/fixtures/generated-archives/status_submodule.tar.xz and b/gix-status/tests/fixtures/generated-archives/status_submodule.tar.xz differ diff --git a/gix-status/tests/fixtures/racy_git.sh b/gix-status/tests/fixtures/racy_git.sh index 7fdef456f87..2b027872835 100755 --- a/gix-status/tests/fixtures/racy_git.sh +++ b/gix-status/tests/fixtures/racy_git.sh @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash set -eu -o pipefail git init -q diff --git a/gix-status/tests/fixtures/status_changed.sh b/gix-status/tests/fixtures/status_changed.sh index 033c6a8336f..06ff09ebf3f 100755 --- a/gix-status/tests/fixtures/status_changed.sh +++ b/gix-status/tests/fixtures/status_changed.sh @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash set -eu -o pipefail git init -q diff --git a/gix-status/tests/fixtures/status_conflict.sh b/gix-status/tests/fixtures/status_conflict.sh index d78e81bfe7e..9573f330f88 100755 --- a/gix-status/tests/fixtures/status_conflict.sh +++ b/gix-status/tests/fixtures/status_conflict.sh @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash set -eu -o pipefail git init -q diff --git a/gix-status/tests/fixtures/status_intent_to_add.sh b/gix-status/tests/fixtures/status_intent_to_add.sh index 7d1601385c0..bfde908cd6e 100755 --- a/gix-status/tests/fixtures/status_intent_to_add.sh +++ b/gix-status/tests/fixtures/status_intent_to_add.sh @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash set -eu -o pipefail git init -q diff --git a/gix-status/tests/fixtures/status_many.sh b/gix-status/tests/fixtures/status_many.sh new file mode 100755 index 00000000000..a3d05017d3e --- /dev/null +++ b/gix-status/tests/fixtures/status_many.sh @@ -0,0 +1,40 @@ +#!/usr/bin/env bash +set -eu -o pipefail + +git init -q changed-and-untracked +(cd changed-and-untracked + touch empty + echo "content" > executable + chmod +x executable + + mkdir dir + echo "other content" > dir/content + echo "different content" > dir/content2 + + git add -A + git commit -m "Commit" + echo "change" >> executable + + + mkdir dir/empty + >dir/untracked + >untracked + + git status +) + +cp -R changed-and-untracked changed-and-untracked-and-renamed +(cd changed-and-untracked-and-renamed + # it has a local change compared to the indexed version, hence it's rewritten + mv executable rewritten-executable + + cp dir/content content-copy + cp dir/content content-copy-with-rewrite + echo change >> content-copy-with-rewrite + + mv dir/content plainly-renamed-content + + mv dir/content2 content-with-rewrite + echo change >> content-with-rewrite + +) diff --git a/gix-status/tests/fixtures/status_removed.sh b/gix-status/tests/fixtures/status_removed.sh index 30cdfb94993..d6a06ec651b 100755 --- a/gix-status/tests/fixtures/status_removed.sh +++ b/gix-status/tests/fixtures/status_removed.sh @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash set -eu -o pipefail git init -q diff --git a/gix-status/tests/fixtures/status_submodule.sh b/gix-status/tests/fixtures/status_submodule.sh old mode 100644 new mode 100755 index a8b6634c5e0..7e73686ace6 --- a/gix-status/tests/fixtures/status_submodule.sh +++ b/gix-status/tests/fixtures/status_submodule.sh @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash set -eu -o pipefail git init -q module1 diff --git a/gix-status/tests/fixtures/status_unchanged.sh b/gix-status/tests/fixtures/status_unchanged.sh index 67684549509..14caa054cd9 100755 --- a/gix-status/tests/fixtures/status_unchanged.sh +++ b/gix-status/tests/fixtures/status_unchanged.sh @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash set -eu -o pipefail git init -q diff --git a/gix-status/tests/fixtures/symlink_stack.sh b/gix-status/tests/fixtures/symlink_stack.sh index 479460c00a9..751a8ee45b6 100755 --- a/gix-status/tests/fixtures/symlink_stack.sh +++ b/gix-status/tests/fixtures/symlink_stack.sh @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash set -eu -o pipefail mkdir base; diff --git a/gix-status/tests/status/index_as_worktree.rs b/gix-status/tests/status/index_as_worktree.rs index d6753e26943..e485f0759c7 100644 --- a/gix-status/tests/status/index_as_worktree.rs +++ b/gix-status/tests/status/index_as_worktree.rs @@ -22,7 +22,7 @@ use crate::fixture_path; // changes when extracting the data so we need to disable all advanced stat // changes and only look at mtime seconds and file size to properly // test all code paths (and to trigger racy git). -const TEST_OPTIONS: index::entry::stat::Options = index::entry::stat::Options { +pub(super) const TEST_OPTIONS: index::entry::stat::Options = index::entry::stat::Options { trust_ctime: false, check_stat: false, use_nsec: false, @@ -128,7 +128,9 @@ fn fixture_filtered_detailed( } /// Note that we also reset certain information to assure there is no flakiness - everything regarding race-detection otherwise can cause failures. -fn records_to_tuple<'index>(records: impl IntoIterator>) -> Vec> { +pub(super) fn records_to_tuple<'index>( + records: impl IntoIterator>, +) -> Vec> { records .into_iter() .filter_map(|r| deracify_status(r.status).map(|status| (r.relative_path, r.entry_index, status))) @@ -159,8 +161,8 @@ fn deracify_status(status: EntryStatus) -> Option { } #[derive(Clone)] -struct SubmoduleStatusMock { - dirty: bool, +pub(super) struct SubmoduleStatusMock { + pub(super) dirty: bool, } impl SubmoduleStatus for SubmoduleStatusMock { @@ -172,7 +174,7 @@ impl SubmoduleStatus for SubmoduleStatusMock { } } -fn to_pathspecs(input: &[&str]) -> Vec { +pub(super) fn to_pathspecs(input: &[&str]) -> Vec { input .iter() .map(|pattern| gix_pathspec::parse(pattern.as_bytes(), Default::default()).expect("known to be valid")) diff --git a/gix-status/tests/status/index_as_worktree_with_renames.rs b/gix-status/tests/status/index_as_worktree_with_renames.rs new file mode 100644 index 00000000000..0706362a8b9 --- /dev/null +++ b/gix-status/tests/status/index_as_worktree_with_renames.rs @@ -0,0 +1,384 @@ +use crate::status::fixture_path; +use bstr::ByteSlice; +use gix_diff::blob::pipeline::WorktreeRoots; +use gix_diff::rewrites::CopySource; +use gix_status::index_as_worktree::traits::FastEq; +use gix_status::index_as_worktree::{Change, EntryStatus}; +use gix_status::index_as_worktree_with_renames; +use gix_status::index_as_worktree_with_renames::{ + Context, DirwalkContext, Entry, Options, Outcome, Recorder, Sorting, Summary, +}; +use pretty_assertions::assert_eq; + +#[test] +fn changed_and_untracked_and_renamed() { + let expectations_with_dirwalk = [ + // Not always will we match the right source to destinations, there is ambiguity. + Expectation::Rewrite { + source_rela_path: "dir/content", + dest_rela_path: "content-copy", + dest_dirwalk_status: gix_dir::entry::Status::Untracked, + diff: None, + copy: false, + }, + Expectation::DirwalkEntry { + rela_path: "content-copy-with-rewrite", + status: gix_dir::entry::Status::Untracked, + disk_kind: Some(gix_dir::entry::Kind::File), + }, + Expectation::Rewrite { + source_rela_path: "dir/content2", + dest_rela_path: "content-with-rewrite", + dest_dirwalk_status: gix_dir::entry::Status::Untracked, + diff: Some(gix_diff::blob::DiffLineStats { + removals: 0, + insertions: 1, + before: 1, + after: 2, + similarity: 0.72, + }), + copy: false, + }, + Expectation::Rewrite { + source_rela_path: "empty", + dest_rela_path: "dir/untracked", + dest_dirwalk_status: gix_dir::entry::Status::Untracked, + diff: None, + copy: true, + }, + // This is just detected as untracked, related to how the rename-tracker matches pairs + Expectation::DirwalkEntry { + rela_path: "plainly-renamed-content", + status: gix_dir::entry::Status::Untracked, + disk_kind: Some(gix_dir::entry::Kind::File), + }, + Expectation::Rewrite { + source_rela_path: "executable", + dest_rela_path: "rewritten-executable", + dest_dirwalk_status: gix_dir::entry::Status::Untracked, + diff: Some(gix_diff::blob::DiffLineStats { + removals: 0, + insertions: 1, + before: 1, + after: 2, + similarity: 0.53333336, + }), + copy: false, + }, + Expectation::Rewrite { + source_rela_path: "empty", + dest_rela_path: "untracked", + dest_dirwalk_status: gix_dir::entry::Status::Untracked, + diff: None, + copy: true, + }, + ]; + let rewrites = gix_diff::Rewrites { + copies: Some(gix_diff::rewrites::Copies { + source: CopySource::FromSetOfModifiedFiles, + percentage: Some(0.3), + }), + percentage: Some(0.3), + limit: 0, + }; + let out = fixture_filtered_detailed( + "changed-and-untracked-and-renamed", + &[], + &expectations_with_dirwalk, + Some(rewrites), + Some(Default::default()), + ); + assert_eq!( + out.rewrites, + Some(gix_diff::rewrites::Outcome { + options: rewrites, + num_similarity_checks: 11, + num_similarity_checks_skipped_for_rename_tracking_due_to_limit: 0, + num_similarity_checks_skipped_for_copy_tracking_due_to_limit: 0, + }) + ) +} + +#[test] +fn changed_and_untracked() { + let out = fixture_filtered_detailed( + "changed-and-untracked", + &[], + &[Expectation::Modification { + rela_path: "executable", + status: EntryStatus::Change(Change::Modification { + executable_bit_changed: false, + content_change: Some(()), + set_entry_stat_size_zero: false, + }), + }], + None, + None, + ); + assert_eq!(out.tracked_file_modification.entries_processed, 4); + assert_eq!( + out.dirwalk, None, + "we didn't configure the dirwalk, so it's just like a modification check" + ); + assert_eq!(out.rewrites, None, "rewrite checking isn't configured either"); + + let expectations_with_dirwalk = [ + Expectation::DirwalkEntry { + rela_path: "dir/untracked", + status: gix_dir::entry::Status::Untracked, + disk_kind: Some(gix_dir::entry::Kind::File), + }, + Expectation::Modification { + rela_path: "executable", + status: EntryStatus::Change(Change::Modification { + executable_bit_changed: false, + content_change: Some(()), + set_entry_stat_size_zero: false, + }), + }, + Expectation::DirwalkEntry { + rela_path: "untracked", + status: gix_dir::entry::Status::Untracked, + disk_kind: Some(gix_dir::entry::Kind::File), + }, + ]; + let out = fixture_filtered_detailed( + "changed-and-untracked", + &[], + &expectations_with_dirwalk, + None, + Some(gix_dir::walk::Options::default()), + ); + + let dirwalk = out.dirwalk.expect("configured thus has output"); + assert_eq!( + dirwalk, + gix_dir::walk::Outcome { + read_dir_calls: 3, + returned_entries: 2, + seen_entries: 8, + } + ); + assert_eq!(out.rewrites, None, "rewrites are still not configured"); + + let out = fixture_filtered_detailed( + "changed-and-untracked", + &[], + &expectations_with_dirwalk, + Some(Default::default()), + Some(gix_dir::walk::Options::default()), + ); + + let rewrites = out.rewrites.expect("configured thus has output"); + assert_eq!( + rewrites, + gix_diff::rewrites::Outcome::default(), + "there actually is no candidates pairs as there are no deletions" + ); +} + +fn fixture_filtered_detailed( + subdir: &str, + pathspecs: &[&str], + expected: &[Expectation<'_>], + rewrites: Option, + dirwalk: Option, +) -> Outcome { + fn cleanup(mut out: Outcome) -> Outcome { + out.tracked_file_modification.worktree_bytes = 0; + out.tracked_file_modification.worktree_files_read = 0; + out.tracked_file_modification.entries_to_update = 0; + out.tracked_file_modification.racy_clean = 0; + out + } + + let worktree = fixture_path("status_many.sh").join(subdir); + let git_dir = worktree.join(".git"); + let index = gix_index::File::at(git_dir.join("index"), gix_hash::Kind::Sha1, false, Default::default()).unwrap(); + let search = gix_pathspec::Search::from_specs( + crate::status::index_as_worktree::to_pathspecs(pathspecs), + None, + std::path::Path::new(""), + ) + .expect("valid specs can be normalized"); + let stack = gix_worktree::Stack::from_state_and_ignore_case( + worktree.clone(), + false, + gix_worktree::stack::State::AttributesAndIgnoreStack { + attributes: Default::default(), + ignore: Default::default(), + }, + &index, + index.path_backing(), + ); + let capabilities = gix_fs::Capabilities::probe(&git_dir); + let resource_cache = gix_diff::blob::Platform::new( + Default::default(), + gix_diff::blob::Pipeline::new( + WorktreeRoots { + old_root: None, + new_root: Some(worktree.to_owned()), + }, + gix_filter::Pipeline::new(Default::default(), Default::default()), + vec![], + gix_diff::blob::pipeline::Options { + large_file_threshold_bytes: 0, + fs: capabilities, + }, + ), + gix_diff::blob::pipeline::Mode::ToGit, + stack, + ); + + let git_dir_real = gix_path::realpath(&git_dir).unwrap(); + let cwd = gix_fs::current_dir(capabilities.precompose_unicode).unwrap(); + let context = Context { + pathspec: search, + resource_cache, + should_interrupt: &Default::default(), + dirwalk: DirwalkContext { + git_dir_realpath: &git_dir_real, + current_dir: &cwd, + ignore_case_index_lookup: None, + }, + }; + let options = Options { + object_hash: gix_hash::Kind::Sha1, + tracked_file_modifications: gix_status::index_as_worktree::Options { + fs: capabilities, + stat: crate::status::index_as_worktree::TEST_OPTIONS, + ..Default::default() + }, + dirwalk, + sorting: Some(Sorting::ByPathCaseSensitive), + rewrites, + }; + + let mut recorder = Recorder::default(); + let objects = gix_odb::at(git_dir.join("objects")).unwrap().into_arc().unwrap(); + let outcome = index_as_worktree_with_renames( + &index, + &worktree, + &mut recorder, + FastEq, + crate::status::index_as_worktree::SubmoduleStatusMock { dirty: false }, + objects, + &mut gix_features::progress::Discard, + context, + options, + ) + .unwrap(); + + let actual = records_to_expectations(&recorder.records); + assert_eq!(actual, expected); + assert_summary(&recorder.records, expected); + cleanup(outcome) +} + +fn assert_summary(entries: &[Entry<(), ()>], expected: &[Expectation]) { + let entries: Vec<_> = entries + .iter() + .filter(|r| { + !matches!( + r, + Entry::Modification { + status: EntryStatus::NeedsUpdate(..), + .. + } + ) + }) + .collect(); + assert_eq!(entries.len(), expected.len()); + for (entry, expected) in entries.iter().zip(expected) { + assert_eq!(entry.summary(), expected.summary()); + } +} + +fn records_to_expectations<'a>(recs: &'a [Entry<'_, (), ()>]) -> Vec> { + recs.iter() + .filter(|r| { + !matches!( + r, + Entry::Modification { + status: EntryStatus::NeedsUpdate(..), + .. + } + ) + }) + .map(|r| match r { + Entry::Modification { rela_path, status, .. } => Expectation::Modification { + rela_path: rela_path.to_str().unwrap(), + status: status.clone(), + }, + Entry::DirectoryContents { entry, .. } => Expectation::DirwalkEntry { + rela_path: entry.rela_path.to_str().unwrap(), + status: entry.status, + disk_kind: entry.disk_kind, + }, + Entry::Rewrite { + source, + dirwalk_entry, + diff, + copy, + .. + } => Expectation::Rewrite { + source_rela_path: source.rela_path().to_str().unwrap(), + dest_rela_path: dirwalk_entry.rela_path.to_str().unwrap(), + dest_dirwalk_status: dirwalk_entry.status, + diff: *diff, + copy: *copy, + }, + }) + .collect() +} + +#[derive(Debug, Clone, PartialEq)] +enum Expectation<'a> { + Modification { + rela_path: &'a str, + status: EntryStatus<(), ()>, + }, + DirwalkEntry { + rela_path: &'a str, + status: gix_dir::entry::Status, + disk_kind: Option, + }, + Rewrite { + source_rela_path: &'a str, + dest_rela_path: &'a str, + dest_dirwalk_status: gix_dir::entry::Status, + diff: Option, + copy: bool, + }, +} + +impl Expectation<'_> { + pub fn summary(&self) -> Option { + Some(match self { + Expectation::Modification { status, .. } => match status { + EntryStatus::Conflict(_) => Summary::Conflict, + EntryStatus::Change(change) => match change { + Change::Removed => Summary::Removed, + Change::Type => Summary::TypeChange, + Change::Modification { .. } | Change::SubmoduleModification(_) => Summary::Modified, + }, + EntryStatus::NeedsUpdate(_) => return None, + EntryStatus::IntentToAdd => Summary::IntentToAdd, + }, + Expectation::DirwalkEntry { status, .. } => { + if matches!(status, gix_dir::entry::Status::Untracked) { + Summary::Added + } else { + return None; + } + } + Expectation::Rewrite { copy, .. } => { + if *copy { + Summary::Copied + } else { + Summary::Renamed + } + } + }) + } +} diff --git a/gix-status/tests/status/mod.rs b/gix-status/tests/status/mod.rs index e758770cf90..4da345b7a8d 100644 --- a/gix-status/tests/status/mod.rs +++ b/gix-status/tests/status/mod.rs @@ -1,4 +1,5 @@ mod index_as_worktree; +mod index_as_worktree_with_renames; pub fn fixture_path(name: &str) -> std::path::PathBuf { let dir = gix_testtools::scripted_fixture_read_only_standalone(std::path::Path::new(name).with_extension("sh")) diff --git a/gix-submodule/CHANGELOG.md b/gix-submodule/CHANGELOG.md index 512055d7c3e..28da9167cb1 100644 --- a/gix-submodule/CHANGELOG.md +++ b/gix-submodule/CHANGELOG.md @@ -5,6 +5,62 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## 0.11.0 (2024-05-22) + +A maintenance release without user-facing changes. + +### Commit Statistics + + + + - 3 commits contributed to the release over the course of 33 calendar days. + - 68 days passed between releases. + - 0 commits were understood as [conventional](https://www.conventionalcommits.org). + - 0 issues like '(#ID)' were seen in commit messages + +### Commit Details + + + +
view details + + * **Uncategorized** + - Adjust changelogs prior to release ([`9511416`](https://github.com/Byron/gitoxide/commit/9511416a6cd0c571233f958c165329c8705c2498)) + - Merge branch 'cargo-fixes' ([`977346e`](https://github.com/Byron/gitoxide/commit/977346ee61de6207c66f3de003db6e8c722fb81c)) + - Release gix-index v0.32.1, gix-pathspec v0.7.4, gix-worktree v0.33.1, gix-dir v0.4.1 ([`54ac559`](https://github.com/Byron/gitoxide/commit/54ac55946bb04635cd74582a1ce2e4bee70f2e60)) +
+ +## 0.10.0 (2024-03-14) + +### New Features + + - Add `Submodule::status()` method. + That way it's possible to obtain submodule status information, + with enough information to implement `git status`-like commands. + +### Commit Statistics + + + + - 5 commits contributed to the release over the course of 4 calendar days. + - 18 days passed between releases. + - 1 commit was understood as [conventional](https://www.conventionalcommits.org). + - 0 issues like '(#ID)' were seen in commit messages + +### Commit Details + + + +
view details + + * **Uncategorized** + - Release gix-date v0.8.5, gix-hash v0.14.2, gix-trace v0.1.8, gix-utils v0.1.11, gix-features v0.38.1, gix-actor v0.31.0, gix-validate v0.8.4, gix-object v0.42.0, gix-path v0.10.7, gix-glob v0.16.2, gix-quote v0.4.12, gix-attributes v0.22.2, gix-command v0.3.6, gix-filter v0.11.0, gix-fs v0.10.1, gix-chunk v0.4.8, gix-commitgraph v0.24.2, gix-hashtable v0.5.2, gix-revwalk v0.13.0, gix-traverse v0.38.0, gix-worktree-stream v0.11.0, gix-archive v0.11.0, gix-config-value v0.14.6, gix-tempfile v13.1.1, gix-lock v13.1.1, gix-ref v0.43.0, gix-sec v0.10.6, gix-config v0.36.0, gix-prompt v0.8.4, gix-url v0.27.2, gix-credentials v0.24.2, gix-ignore v0.11.2, gix-bitmap v0.2.11, gix-index v0.31.0, gix-worktree v0.32.0, gix-diff v0.42.0, gix-discover v0.31.0, gix-pathspec v0.7.1, gix-dir v0.2.0, gix-macros v0.1.4, gix-mailmap v0.23.0, gix-negotiate v0.13.0, gix-pack v0.49.0, gix-odb v0.59.0, gix-packetline v0.17.4, gix-transport v0.41.2, gix-protocol v0.44.2, gix-revision v0.27.0, gix-refspec v0.23.0, gix-status v0.7.0, gix-submodule v0.10.0, gix-worktree-state v0.9.0, gix v0.60.0, safety bump 26 crates ([`b050327`](https://github.com/Byron/gitoxide/commit/b050327e76f234b19be921b78b7b28e034319fdb)) + - Prepare changelogs prior to release ([`52c3bbd`](https://github.com/Byron/gitoxide/commit/52c3bbd36b9e94a0f3a78b4ada84d0c08eba27f6)) + - Merge branch 'status' ([`3e5c974`](https://github.com/Byron/gitoxide/commit/3e5c974dd62ac134711c6c2f5a5490187a6ea55e)) + - Fix lints for nightly, and clippy ([`f8ce3d0`](https://github.com/Byron/gitoxide/commit/f8ce3d0721b6a53713a9392f2451874f520bc44c)) + - Add `Submodule::status()` method. ([`a29fa00`](https://github.com/Byron/gitoxide/commit/a29fa00d0727baffcba10c8f2f09115a362a2baf)) +
+ ## 0.9.0 (2024-02-25) A maintenance release without user-facing changes. @@ -13,7 +69,7 @@ A maintenance release without user-facing changes. - - 4 commits contributed to the release over the course of 30 calendar days. + - 5 commits contributed to the release over the course of 30 calendar days. - 36 days passed between releases. - 0 commits were understood as [conventional](https://www.conventionalcommits.org). - 0 issues like '(#ID)' were seen in commit messages @@ -25,6 +81,7 @@ A maintenance release without user-facing changes.
view details * **Uncategorized** + - Release gix-date v0.8.4, gix-utils v0.1.10, gix-actor v0.30.1, gix-object v0.41.1, gix-path v0.10.6, gix-glob v0.16.1, gix-quote v0.4.11, gix-attributes v0.22.1, gix-command v0.3.5, gix-filter v0.10.0, gix-commitgraph v0.24.1, gix-worktree-stream v0.10.0, gix-archive v0.10.0, gix-config-value v0.14.5, gix-ref v0.42.0, gix-sec v0.10.5, gix-config v0.35.0, gix-prompt v0.8.3, gix-url v0.27.1, gix-credentials v0.24.1, gix-ignore v0.11.1, gix-index v0.30.0, gix-worktree v0.31.0, gix-diff v0.41.0, gix-discover v0.30.0, gix-pathspec v0.7.0, gix-dir v0.1.0, gix-pack v0.48.0, gix-odb v0.58.0, gix-transport v0.41.1, gix-protocol v0.44.1, gix-revision v0.26.1, gix-refspec v0.22.1, gix-status v0.6.0, gix-submodule v0.9.0, gix-worktree-state v0.8.0, gix v0.59.0, gix-fsck v0.3.0, gitoxide-core v0.36.0, gitoxide v0.34.0, safety bump 10 crates ([`45b4470`](https://github.com/Byron/gitoxide/commit/45b447045bc826f252129c300c531acde2652c64)) - Prepare changelogs prior to release ([`f2e111f`](https://github.com/Byron/gitoxide/commit/f2e111f768fc1bc6182355261c20b63610cffec7)) - Merge branch 'tempfile-permissions' ([`7b44c7f`](https://github.com/Byron/gitoxide/commit/7b44c7ff1dc0b8875214d2673c7f52948cf04ff0)) - Release gix-tempfile v13.1.0, gix-lock v13.1.0, safety bump 12 crates ([`8430442`](https://github.com/Byron/gitoxide/commit/84304427dfe4d170c7732161b126961719f70059)) diff --git a/gix-submodule/Cargo.toml b/gix-submodule/Cargo.toml index 667d2ce0963..be8d63bb61a 100644 --- a/gix-submodule/Cargo.toml +++ b/gix-submodule/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "gix-submodule" -version = "0.9.0" +version = "0.11.0" repository = "https://github.com/Byron/gitoxide" license = "MIT OR Apache-2.0" description = "A crate of the gitoxide project dealing git submodules" @@ -13,11 +13,11 @@ include = ["src/**/*", "LICENSE-*"] doctest = false [dependencies] -gix-pathspec = { version = "^0.7.0", path = "../gix-pathspec" } -gix-refspec = { version = "^0.22.1", path = "../gix-refspec" } -gix-config = { version = "^0.35.0", path = "../gix-config" } -gix-path = { version = "^0.10.6", path = "../gix-path" } -gix-url = { version = "^0.27.1", path = "../gix-url" } +gix-pathspec = { version = "^0.7.5", path = "../gix-pathspec" } +gix-refspec = { version = "^0.23.0", path = "../gix-refspec" } +gix-config = { version = "^0.37.0", path = "../gix-config" } +gix-path = { version = "^0.10.7", path = "../gix-path" } +gix-url = { version = "^0.27.2", path = "../gix-url" } bstr = { version = "1.5.0", default-features = false } thiserror = "1.0.44" diff --git a/gix-submodule/src/config.rs b/gix-submodule/src/config.rs index 2026966917c..98148b71cc8 100644 --- a/gix-submodule/src/config.rs +++ b/gix-submodule/src/config.rs @@ -4,8 +4,6 @@ use bstr::{BStr, BString, ByteSlice}; #[derive(Default, Debug, Clone, Copy, Ord, PartialOrd, Eq, PartialEq, Hash)] pub enum Ignore { /// Submodule changes won't be considered at all, which is the fastest option. - /// - /// Note that changes to the submodule hash in the superproject will still be observable. All, /// Ignore any changes to the submodule working tree, only show committed differences between the `HEAD` of the submodule /// compared to the recorded commit in the superproject. @@ -151,6 +149,7 @@ pub struct Error { } /// +#[allow(clippy::empty_docs)] pub mod branch { use bstr::BString; @@ -166,6 +165,7 @@ pub mod branch { } /// +#[allow(clippy::empty_docs)] pub mod update { use bstr::BString; @@ -181,6 +181,7 @@ pub mod update { } /// +#[allow(clippy::empty_docs)] pub mod url { use bstr::BString; @@ -199,6 +200,7 @@ pub mod url { } /// +#[allow(clippy::empty_docs)] pub mod path { use bstr::BString; diff --git a/gix-submodule/src/lib.rs b/gix-submodule/src/lib.rs index 639af30fae3..7b3068b7739 100644 --- a/gix-submodule/src/lib.rs +++ b/gix-submodule/src/lib.rs @@ -19,9 +19,11 @@ pub struct File { mod access; /// +#[allow(clippy::empty_docs)] pub mod config; /// +#[allow(clippy::empty_docs)] pub mod is_active_platform; /// A platform to keep the state necessary to perform repeated active checks, created by [File::is_active_platform()]. @@ -85,6 +87,7 @@ impl File { } /// +#[allow(clippy::empty_docs)] mod init { use std::path::PathBuf; diff --git a/gix-submodule/tests/fixtures/basic.sh b/gix-submodule/tests/fixtures/basic.sh index 203e6afdeea..b61169e35f5 100755 --- a/gix-submodule/tests/fixtures/basic.sh +++ b/gix-submodule/tests/fixtures/basic.sh @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash set -eu -o pipefail set -x diff --git a/gix-submodule/tests/fixtures/generated-archives/basic.tar.xz b/gix-submodule/tests/fixtures/generated-archives/basic.tar.xz index d483db5e73f..f9405c31df9 100644 Binary files a/gix-submodule/tests/fixtures/generated-archives/basic.tar.xz and b/gix-submodule/tests/fixtures/generated-archives/basic.tar.xz differ diff --git a/gix-tempfile/CHANGELOG.md b/gix-tempfile/CHANGELOG.md index ce9f1fde5cf..1b420439445 100644 --- a/gix-tempfile/CHANGELOG.md +++ b/gix-tempfile/CHANGELOG.md @@ -5,6 +5,56 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## 14.0.0 (2024-05-22) + +A maintenance release without user-facing changes. + +### Commit Statistics + + + + - 1 commit contributed to the release. + - 68 days passed between releases. + - 0 commits were understood as [conventional](https://www.conventionalcommits.org). + - 0 issues like '(#ID)' were seen in commit messages + +### Commit Details + + + +
view details + + * **Uncategorized** + - Adjust changelogs prior to release ([`9511416`](https://github.com/Byron/gitoxide/commit/9511416a6cd0c571233f958c165329c8705c2498)) +
+ +## 13.1.1 (2024-03-14) + +A maintenance release without user-facing changes. + +### Commit Statistics + + + + - 5 commits contributed to the release over the course of 37 calendar days. + - 37 days passed between releases. + - 0 commits were understood as [conventional](https://www.conventionalcommits.org). + - 0 issues like '(#ID)' were seen in commit messages + +### Commit Details + + + +
view details + + * **Uncategorized** + - Release gix-date v0.8.5, gix-hash v0.14.2, gix-trace v0.1.8, gix-utils v0.1.11, gix-features v0.38.1, gix-actor v0.31.0, gix-validate v0.8.4, gix-object v0.42.0, gix-path v0.10.7, gix-glob v0.16.2, gix-quote v0.4.12, gix-attributes v0.22.2, gix-command v0.3.6, gix-filter v0.11.0, gix-fs v0.10.1, gix-chunk v0.4.8, gix-commitgraph v0.24.2, gix-hashtable v0.5.2, gix-revwalk v0.13.0, gix-traverse v0.38.0, gix-worktree-stream v0.11.0, gix-archive v0.11.0, gix-config-value v0.14.6, gix-tempfile v13.1.1, gix-lock v13.1.1, gix-ref v0.43.0, gix-sec v0.10.6, gix-config v0.36.0, gix-prompt v0.8.4, gix-url v0.27.2, gix-credentials v0.24.2, gix-ignore v0.11.2, gix-bitmap v0.2.11, gix-index v0.31.0, gix-worktree v0.32.0, gix-diff v0.42.0, gix-discover v0.31.0, gix-pathspec v0.7.1, gix-dir v0.2.0, gix-macros v0.1.4, gix-mailmap v0.23.0, gix-negotiate v0.13.0, gix-pack v0.49.0, gix-odb v0.59.0, gix-packetline v0.17.4, gix-transport v0.41.2, gix-protocol v0.44.2, gix-revision v0.27.0, gix-refspec v0.23.0, gix-status v0.7.0, gix-submodule v0.10.0, gix-worktree-state v0.9.0, gix v0.60.0, safety bump 26 crates ([`b050327`](https://github.com/Byron/gitoxide/commit/b050327e76f234b19be921b78b7b28e034319fdb)) + - Prepare changelogs prior to release ([`52c3bbd`](https://github.com/Byron/gitoxide/commit/52c3bbd36b9e94a0f3a78b4ada84d0c08eba27f6)) + - Merge branch 'status' ([`3e5c974`](https://github.com/Byron/gitoxide/commit/3e5c974dd62ac134711c6c2f5a5490187a6ea55e)) + - Fix lints for nightly, and clippy ([`f8ce3d0`](https://github.com/Byron/gitoxide/commit/f8ce3d0721b6a53713a9392f2451874f520bc44c)) + - Merge branch 'tempfile-permissions' ([`7b44c7f`](https://github.com/Byron/gitoxide/commit/7b44c7ff1dc0b8875214d2673c7f52948cf04ff0)) +
+ ## 13.1.0 (2024-02-06) ### New Features @@ -15,7 +65,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - - 1 commit contributed to the release. + - 2 commits contributed to the release. - 17 days passed between releases. - 1 commit was understood as [conventional](https://www.conventionalcommits.org). - 0 issues like '(#ID)' were seen in commit messages @@ -27,6 +77,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
view details * **Uncategorized** + - Release gix-tempfile v13.1.0, gix-lock v13.1.0, safety bump 12 crates ([`8430442`](https://github.com/Byron/gitoxide/commit/84304427dfe4d170c7732161b126961719f70059)) - Tempfile with permission support, adding new `*_with_permissions(..., permissions)` methods. ([`45ef4ff`](https://github.com/Byron/gitoxide/commit/45ef4ffa76f10073ace6ce264198d211a74963e2))
@@ -60,12 +111,12 @@ A maintenance release without user-facing changes. ### Chore - - change `rust-version` manifest field back to 1.65. - They didn't actually need to be higher to work, and changing them - unecessarily can break downstream CI. - - Let's keep this value as low as possible, and only increase it when - more recent features are actually used. +- change `rust-version` manifest field back to 1.65. + They didn't actually need to be higher to work, and changing them + unecessarily can break downstream CI. + + Let's keep this value as low as possible, and only increase it when + more recent features are actually used. ### Commit Statistics @@ -93,8 +144,8 @@ A maintenance release without user-facing changes. ### Chore - - upgrade MSRV to v1.70 - Our MSRV follows the one of `helix`, which in turn follows Firefox. +- upgrade MSRV to v1.70 + Our MSRV follows the one of `helix`, which in turn follows Firefox. ### Commit Statistics @@ -177,7 +228,7 @@ A maintenance release without user-facing changes. ### Other - - Improve documentation to make possibility of leaks more clear +- Improve documentation to make possibility of leaks more clear ### Commit Statistics @@ -320,7 +371,7 @@ A maintenance release without user-facing changes. ### Chore - - Add `clippy::redundant-closure-for-method-calls` lint +- Add `clippy::redundant-closure-for-method-calls` lint ### Commit Statistics @@ -647,9 +698,9 @@ A maintenance release without any user-facing changes. ### Chore - - uniformize deny attributes - - remove default link to cargo doc everywhere - - upgrade dashmap to 5.1.0 (with security fix) +- uniformize deny attributes +- remove default link to cargo doc everywhere +- upgrade dashmap to 5.1.0 (with security fix) ### Documentation @@ -959,8 +1010,8 @@ Maintenance release without observable changes. ### Chore - - uniformize deny attributes - - remove default link to cargo doc everywhere +- uniformize deny attributes +- remove default link to cargo doc everywhere ## 2.0.3 (2022-08-17) @@ -997,7 +1048,7 @@ Fixes a potential deadlock in in an interrupt handler attempting to cleanup temp ### Chore - - upgrade dashmap to 5.1.0 (with security fix) +- upgrade dashmap to 5.1.0 (with security fix) ### Bug Fixes diff --git a/gix-tempfile/Cargo.toml b/gix-tempfile/Cargo.toml index 77cf5df1c25..420ecb358c1 100644 --- a/gix-tempfile/Cargo.toml +++ b/gix-tempfile/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "gix-tempfile" -version = "13.1.0" +version = "14.0.0" repository = "https://github.com/Byron/gitoxide" license = "MIT OR Apache-2.0" description = "A tempfile implementation with a global registry to assure cleanup" @@ -29,7 +29,7 @@ doctest = false test = true [dependencies] -gix-fs = { version = "^0.10.0", path = "../gix-fs" } +gix-fs = { version = "^0.11.1", path = "../gix-fs" } parking_lot = "0.12.1" dashmap = { version = "5.1.0", optional = true } once_cell = { version = "1.8.0", default-features = false, features = ["race", "std"] } diff --git a/gix-tempfile/src/handle.rs b/gix-tempfile/src/handle.rs index a4b6627c806..4365a2ef849 100644 --- a/gix-tempfile/src/handle.rs +++ b/gix-tempfile/src/handle.rs @@ -1,4 +1,5 @@ //! +#![allow(clippy::empty_docs)] use std::{io, path::Path}; use tempfile::{NamedTempFile, TempPath}; @@ -249,6 +250,7 @@ mod io_impls { } /// +#[allow(clippy::empty_docs)] pub mod persist { use std::path::Path; diff --git a/gix-tempfile/src/lib.rs b/gix-tempfile/src/lib.rs index fc1580903b5..9a5d52405b6 100644 --- a/gix-tempfile/src/lib.rs +++ b/gix-tempfile/src/lib.rs @@ -108,6 +108,7 @@ pub mod handle; use crate::handle::{Closed, Writable}; /// +#[allow(clippy::empty_docs)] pub mod registry; static NEXT_MAP_INDEX: AtomicUsize = AtomicUsize::new(0); diff --git a/gix-tempfile/src/signal.rs b/gix-tempfile/src/signal.rs index d7c0f83684b..2591dc517e8 100644 --- a/gix-tempfile/src/signal.rs +++ b/gix-tempfile/src/signal.rs @@ -16,6 +16,7 @@ pub fn setup(mode: handler::Mode) { } /// +#[allow(clippy::empty_docs)] pub mod handler { use std::sync::atomic::AtomicUsize; diff --git a/gix-trace/CHANGELOG.md b/gix-trace/CHANGELOG.md index a5d4c2f7d57..e20cd16841d 100644 --- a/gix-trace/CHANGELOG.md +++ b/gix-trace/CHANGELOG.md @@ -5,6 +5,57 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## 0.1.9 (2024-04-13) + +A maintenance release without user-facing changes. + +### Commit Statistics + + + + - 3 commits contributed to the release over the course of 2 calendar days. + - 29 days passed between releases. + - 0 commits were understood as [conventional](https://www.conventionalcommits.org). + - 0 issues like '(#ID)' were seen in commit messages + +### Commit Details + + + +
view details + + * **Uncategorized** + - Prepare changelogs prior to release ([`5755271`](https://github.com/Byron/gitoxide/commit/57552717f46f96c35ba4ddc0a64434354ef845e9)) + - Merge pull request #1341 from szepeviktor/typos ([`55f379b`](https://github.com/Byron/gitoxide/commit/55f379bc47065822d078393d83d30c0835a89782)) + - Fix typos ([`f72ecce`](https://github.com/Byron/gitoxide/commit/f72ecce45babcad2a0c9b73c79d01ff502907a57)) +
+ +## 0.1.8 (2024-03-14) + +A maintenance release without user-facing changes. + +### Commit Statistics + + + + - 4 commits contributed to the release over the course of 4 calendar days. + - 59 days passed between releases. + - 0 commits were understood as [conventional](https://www.conventionalcommits.org). + - 0 issues like '(#ID)' were seen in commit messages + +### Commit Details + + + +
view details + + * **Uncategorized** + - Release gix-date v0.8.5, gix-hash v0.14.2, gix-trace v0.1.8, gix-utils v0.1.11, gix-features v0.38.1, gix-actor v0.31.0, gix-validate v0.8.4, gix-object v0.42.0, gix-path v0.10.7, gix-glob v0.16.2, gix-quote v0.4.12, gix-attributes v0.22.2, gix-command v0.3.6, gix-filter v0.11.0, gix-fs v0.10.1, gix-chunk v0.4.8, gix-commitgraph v0.24.2, gix-hashtable v0.5.2, gix-revwalk v0.13.0, gix-traverse v0.38.0, gix-worktree-stream v0.11.0, gix-archive v0.11.0, gix-config-value v0.14.6, gix-tempfile v13.1.1, gix-lock v13.1.1, gix-ref v0.43.0, gix-sec v0.10.6, gix-config v0.36.0, gix-prompt v0.8.4, gix-url v0.27.2, gix-credentials v0.24.2, gix-ignore v0.11.2, gix-bitmap v0.2.11, gix-index v0.31.0, gix-worktree v0.32.0, gix-diff v0.42.0, gix-discover v0.31.0, gix-pathspec v0.7.1, gix-dir v0.2.0, gix-macros v0.1.4, gix-mailmap v0.23.0, gix-negotiate v0.13.0, gix-pack v0.49.0, gix-odb v0.59.0, gix-packetline v0.17.4, gix-transport v0.41.2, gix-protocol v0.44.2, gix-revision v0.27.0, gix-refspec v0.23.0, gix-status v0.7.0, gix-submodule v0.10.0, gix-worktree-state v0.9.0, gix v0.60.0, safety bump 26 crates ([`b050327`](https://github.com/Byron/gitoxide/commit/b050327e76f234b19be921b78b7b28e034319fdb)) + - Prepare changelogs prior to release ([`52c3bbd`](https://github.com/Byron/gitoxide/commit/52c3bbd36b9e94a0f3a78b4ada84d0c08eba27f6)) + - Merge branch 'status' ([`3e5c974`](https://github.com/Byron/gitoxide/commit/3e5c974dd62ac134711c6c2f5a5490187a6ea55e)) + - Fix lints for nightly, and clippy ([`f8ce3d0`](https://github.com/Byron/gitoxide/commit/f8ce3d0721b6a53713a9392f2451874f520bc44c)) +
+ ## 0.1.7 (2024-01-15) A maintenance release without user-facing changes. @@ -13,7 +64,7 @@ A maintenance release without user-facing changes. - - 3 commits contributed to the release over the course of 1 calendar day. + - 4 commits contributed to the release over the course of 1 calendar day. - 16 days passed between releases. - 0 commits were understood as [conventional](https://www.conventionalcommits.org). - 0 issues like '(#ID)' were seen in commit messages @@ -25,6 +76,7 @@ A maintenance release without user-facing changes.
view details * **Uncategorized** + - Release gix-trace v0.1.7, gix-features v0.37.2, gix-commitgraph v0.23.2, gix-traverse v0.36.2, gix-index v0.28.2 ([`b6c04c8`](https://github.com/Byron/gitoxide/commit/b6c04c87b426bf36a059df8dc52b56d384b27b79)) - Prepare changelogs prior to `gix-index` release ([`17d1aac`](https://github.com/Byron/gitoxide/commit/17d1aac91ad22291ad6d72f6e8798ebb741a8d7d)) - Merge pull request #1248 from joshtriplett/tyop ([`39f35da`](https://github.com/Byron/gitoxide/commit/39f35da390bc46005d0374b9bf4e7106fc1bd0ec)) - Typo fixes ([`3ef3bc2`](https://github.com/Byron/gitoxide/commit/3ef3bc20a1b90799e5ac26858f898bc7a7c96901)) @@ -36,12 +88,12 @@ A maintenance release without user-facing changes. ### Chore - - change `rust-version` manifest field back to 1.65. - They didn't actually need to be higher to work, and changing them - unecessarily can break downstream CI. - - Let's keep this value as low as possible, and only increase it when - more recent features are actually used. +- change `rust-version` manifest field back to 1.65. + They didn't actually need to be higher to work, and changing them + unecessarily can break downstream CI. + + Let's keep this value as low as possible, and only increase it when + more recent features are actually used. ### Commit Statistics @@ -69,8 +121,8 @@ A maintenance release without user-facing changes. ### Chore - - upgrade MSRV to v1.70 - Our MSRV follows the one of `helix`, which in turn follows Firefox. +- upgrade MSRV to v1.70 + Our MSRV follows the one of `helix`, which in turn follows Firefox. ### Commit Statistics diff --git a/gix-trace/Cargo.toml b/gix-trace/Cargo.toml index dd3f7f98392..47b389caddc 100644 --- a/gix-trace/Cargo.toml +++ b/gix-trace/Cargo.toml @@ -2,7 +2,7 @@ name = "gix-trace" description = "A crate to provide minimal `tracing` support that can be turned off to zero cost" repository = "https://github.com/Byron/gitoxide" -version = "0.1.7" +version = "0.1.9" authors = ["Sebastian Thiel "] license = "MIT OR Apache-2.0" edition = "2021" diff --git a/gix-trace/src/lib.rs b/gix-trace/src/lib.rs index 283716405d1..db90f77ad03 100644 --- a/gix-trace/src/lib.rs +++ b/gix-trace/src/lib.rs @@ -54,6 +54,7 @@ mod disabled; pub use disabled::Span; /// +#[allow(clippy::empty_docs)] pub mod event { #[cfg(feature = "tracing")] pub use tracing_core::Level; diff --git a/gix-trace/tests/trace.rs b/gix-trace/tests/trace.rs index 1559e1374d6..59ed2a19c7c 100644 --- a/gix-trace/tests/trace.rs +++ b/gix-trace/tests/trace.rs @@ -2,8 +2,8 @@ use gix_trace::{coarse, debug, detail, error, event, info, span, trace, warn}; #[test] fn span() { let _x = span!(gix_trace::Level::Coarse, "hello"); - let fourty_two = span!(gix_trace::Level::Coarse, "hello", x = "value", y = 42).into_scope(|| 42); - assert_eq!(fourty_two, 42); + let forty_two = span!(gix_trace::Level::Coarse, "hello", x = "value", y = 42).into_scope(|| 42); + assert_eq!(forty_two, 42); let span = span!(target: "other", gix_trace::Level::Coarse, "hello", x = "value", y = 42); span.record("y", "hello").record("x", 36); } diff --git a/gix-transport/CHANGELOG.md b/gix-transport/CHANGELOG.md index f815d94a605..d421dc435b2 100644 --- a/gix-transport/CHANGELOG.md +++ b/gix-transport/CHANGELOG.md @@ -5,6 +5,165 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## 0.42.1 (2024-05-22) + +A maintenance release without user-facing changes. + +### Commit Statistics + + + + - 5 commits contributed to the release over the course of 30 calendar days. + - 38 days passed between releases. + - 0 commits were understood as [conventional](https://www.conventionalcommits.org). + - 0 issues like '(#ID)' were seen in commit messages + +### Commit Details + + + +
view details + + * **Uncategorized** + - Adjust changelogs prior to release ([`9511416`](https://github.com/Byron/gitoxide/commit/9511416a6cd0c571233f958c165329c8705c2498)) + - Merge branch 'various-fixes' ([`d6cd449`](https://github.com/Byron/gitoxide/commit/d6cd44930fb204b06e2b70fc6965e7705530c47a)) + - Update dependencies ([`cd4de83`](https://github.com/Byron/gitoxide/commit/cd4de8327fc195eb862ab6e138f2315a87374f85)) + - Merge branch 'push-wwxrqxuzmolm' ([`048e43e`](https://github.com/Byron/gitoxide/commit/048e43e26908b0572852a75780a451460dc152ff)) + - Refactor - address fix suggested by `cargo check` ([`4faf10e`](https://github.com/Byron/gitoxide/commit/4faf10e27a942da3da25e659a43e97eb160e79f2)) +
+ +## 0.42.0 (2024-04-13) + +### Bug Fixes + + - Prevent usernames with leading `-` from being passed to SSH + This detects ambiguous usernames in dangerous cases where they + would be passed to external commands to form SSH connections, if + they would be misinterpreted as option arguments. + + This change is analogous to b06a0dd, hardening `gix-transport` and + applications that use it against options smuggled in URLs, but for + the non-mandatory username portion of a URL, rather than the host + and path portions that were covered there. + + For example, commands like these no longer pass `-F...` options to + `ssh`: + + gix clone 'ssh://-Fconfigfile@example.com/abc' + gix clone -- '-Fconfigfile@example.com:abc/def' + + Instead, they refuse to run `ssh`, producing the error: + + Error: Username '-Fconfigfile' could be mistaken for a command-line argument + - forward `curl` rustls feature from `gix-transport` to avoid `curl` in `gix`. + This removes the `curl` dependency just for configuring it, and removes + a hazard which became evident with reqwest. + +### Bug Fixes (BREAKING) + + - declare `reqwest` dependency update as breaking + Related to https://github.com/Byron/gitoxide/pull/1327 . + +### Commit Statistics + + + + - 21 commits contributed to the release over the course of 20 calendar days. + - 22 days passed between releases. + - 3 commits were understood as [conventional](https://www.conventionalcommits.org). + - 1 unique issue was worked on: [#1328](https://github.com/Byron/gitoxide/issues/1328) + +### Commit Details + + + +
view details + + * **[#1328](https://github.com/Byron/gitoxide/issues/1328)** + - Forward `curl` rustls feature from `gix-transport` to avoid `curl` in `gix`. ([`98cfbec`](https://github.com/Byron/gitoxide/commit/98cfbec51276bbd6caa48fd6d8942247df091c94)) + - Declare `reqwest` dependency update as breaking ([`e304369`](https://github.com/Byron/gitoxide/commit/e30436982903e82a0c635bec58dfee1fff33fed8)) + * **Uncategorized** + - Release gix-trace v0.1.9, gix-utils v0.1.12, gix-packetline-blocking v0.17.4, gix-filter v0.11.1, gix-fs v0.10.2, gix-traverse v0.39.0, gix-worktree-stream v0.12.0, gix-archive v0.12.0, gix-config v0.36.1, gix-url v0.27.3, gix-index v0.32.0, gix-worktree v0.33.0, gix-diff v0.43.0, gix-pathspec v0.7.3, gix-dir v0.4.0, gix-pack v0.50.0, gix-odb v0.60.0, gix-transport v0.42.0, gix-protocol v0.45.0, gix-status v0.9.0, gix-worktree-state v0.10.0, gix v0.62.0, gix-fsck v0.4.0, gitoxide-core v0.37.0, gitoxide v0.35.0, safety bump 14 crates ([`095c673`](https://github.com/Byron/gitoxide/commit/095c6739b2722a8b9af90776b435ef2da454c0e6)) + - Prepare changelogs prior to release ([`5755271`](https://github.com/Byron/gitoxide/commit/57552717f46f96c35ba4ddc0a64434354ef845e9)) + - Merge branch 'strange-usernames' ([`1272542`](https://github.com/Byron/gitoxide/commit/1272542e79c29302ada47324d6bb02101393563d)) + - Refactor `gix-transport` with minor edits to comments ([`996310b`](https://github.com/Byron/gitoxide/commit/996310ba1408fe746c6de43cb24dc1e809fd4d57)) + - (Re)add a short, more specific comment about user@ ([`03fb64a`](https://github.com/Byron/gitoxide/commit/03fb64ac8fca02bed9786b0e832764c1728e6e0e)) + - Use `Url::host_as_argument()` in `ssh::connect()` ([`cf59f57`](https://github.com/Byron/gitoxide/commit/cf59f574f97f9a3ca489c603674d0ec76e498e1e)) + - Test that leading-`-` host names aren't used in `-G` check ([`902367f`](https://github.com/Byron/gitoxide/commit/902367fb452787a8fd8305676fa9c5a827646490)) + - Try, so far unsuccessfully, to add missing `-G` test ([`524739b`](https://github.com/Byron/gitoxide/commit/524739b9189d007dab2f0dde38ce59dabd20d737)) + - Reallow `user@-arg...` in prepare_invocation ([`2911623`](https://github.com/Byron/gitoxide/commit/29116236de3ca0ee97f60f1ad4024f74490bb2cd)) + - Start on using {user,host}_as_argument in prepare_invocation ([`4dda375`](https://github.com/Byron/gitoxide/commit/4dda375cb1e2b4c7bc656979ac718f919c391b94)) + - Comment gix_transport::client::blocking_io::ssh::connect ([`2e7517e`](https://github.com/Byron/gitoxide/commit/2e7517e964af0a0d74e05049db6bcd2527199cb3)) + - Prevent usernames with leading `-` from being passed to SSH ([`f56ad39`](https://github.com/Byron/gitoxide/commit/f56ad390a5569d0129b7b16632991d18b9ddb4f7)) + - Add ambiguous user unit tests, and more for hostname ([`5428609`](https://github.com/Byron/gitoxide/commit/54286091ebc6e13a8f27f730fa88127e6334cf13)) + - Merge pull request #1341 from szepeviktor/typos ([`55f379b`](https://github.com/Byron/gitoxide/commit/55f379bc47065822d078393d83d30c0835a89782)) + - Fix typos ([`f72ecce`](https://github.com/Byron/gitoxide/commit/f72ecce45babcad2a0c9b73c79d01ff502907a57)) + - Merge pull request #1333 from cesfahani/fix_zombie_ssh_procs ([`16dc027`](https://github.com/Byron/gitoxide/commit/16dc027508d17a5ffb694328ec9218d4ecdaba51)) + - Fix zombie ssh processes from accumulating ([`ba93ef2`](https://github.com/Byron/gitoxide/commit/ba93ef272317c697b3eb3e2202c6d7453ce2e5f8)) + - Merge branch 'patch-1' ([`9e9c653`](https://github.com/Byron/gitoxide/commit/9e9c653a83df58f8cdfe3a7adb2d824c8a368e72)) + - Remove dep reqwest from gix ([`e3eedd8`](https://github.com/Byron/gitoxide/commit/e3eedd8b5326b8de2e6fe8941e1851bdbad673ab)) +
+ +## 0.41.3 (2024-03-22) + +A maintenance release without user-facing changes, but with a `reqwest` update that uses more recent tls and hyper versions. + +YANKED: This version requires that the used `gix` crate is also uses the most recently published `gix v0.61.1`, or else +no HTTPS connection can be established at runtime. However, depending on the dependency tree, this isn't guaranteed to happen. +Further, it will definitely not be working correctly when compiled with `gix v0.60.0`, which would also pull this release +into its dependency tree. + +### Commit Statistics + + + + - 7 commits contributed to the release over the course of 1 calendar day. + - 7 days passed between releases. + - 0 commits were understood as [conventional](https://www.conventionalcommits.org). + - 0 issues like '(#ID)' were seen in commit messages + +### Commit Details + + + +
view details + + * **Uncategorized** + - Release gix-packetline v0.17.5, gix-transport v0.41.3, gix v0.61.1 ([`57579f1`](https://github.com/Byron/gitoxide/commit/57579f1ee4ef12c214db36325a2a0b2e8b2b14fd)) + - Prepare changelogs prior to release ([`7018a92`](https://github.com/Byron/gitoxide/commit/7018a928a405ba0534442f0b538d58f520145376)) + - Merge branch 'patch-1' ([`8fde62b`](https://github.com/Byron/gitoxide/commit/8fde62b2617985f835e2e2fa07c735a5158789cf)) + - Turn`curl` into a workspace package ([`adee500`](https://github.com/Byron/gitoxide/commit/adee50016007619495c93580e845ae757377c4f0)) + - Adjust expectations to deal with reqwest Content-Length ([`3b8f39d`](https://github.com/Byron/gitoxide/commit/3b8f39d64cdf44952fea9a45dc3d40df550e60ec)) + - Make reqwest a workspace package ([`369cf1b`](https://github.com/Byron/gitoxide/commit/369cf1b03735617debe1527b3f23247685181e7d)) + - Bump dep reqwest from 0.11.12 to 0.12.0 ([`cdb128c`](https://github.com/Byron/gitoxide/commit/cdb128c95b6d6bf26fa098d7f4495f0c6113319f)) +
+ +## 0.41.2 (2024-03-14) + +A maintenance release without user-facing changes. + +### Commit Statistics + + + + - 4 commits contributed to the release over the course of 4 calendar days. + - 18 days passed between releases. + - 0 commits were understood as [conventional](https://www.conventionalcommits.org). + - 0 issues like '(#ID)' were seen in commit messages + +### Commit Details + + + +
view details + + * **Uncategorized** + - Release gix-date v0.8.5, gix-hash v0.14.2, gix-trace v0.1.8, gix-utils v0.1.11, gix-features v0.38.1, gix-actor v0.31.0, gix-validate v0.8.4, gix-object v0.42.0, gix-path v0.10.7, gix-glob v0.16.2, gix-quote v0.4.12, gix-attributes v0.22.2, gix-command v0.3.6, gix-filter v0.11.0, gix-fs v0.10.1, gix-chunk v0.4.8, gix-commitgraph v0.24.2, gix-hashtable v0.5.2, gix-revwalk v0.13.0, gix-traverse v0.38.0, gix-worktree-stream v0.11.0, gix-archive v0.11.0, gix-config-value v0.14.6, gix-tempfile v13.1.1, gix-lock v13.1.1, gix-ref v0.43.0, gix-sec v0.10.6, gix-config v0.36.0, gix-prompt v0.8.4, gix-url v0.27.2, gix-credentials v0.24.2, gix-ignore v0.11.2, gix-bitmap v0.2.11, gix-index v0.31.0, gix-worktree v0.32.0, gix-diff v0.42.0, gix-discover v0.31.0, gix-pathspec v0.7.1, gix-dir v0.2.0, gix-macros v0.1.4, gix-mailmap v0.23.0, gix-negotiate v0.13.0, gix-pack v0.49.0, gix-odb v0.59.0, gix-packetline v0.17.4, gix-transport v0.41.2, gix-protocol v0.44.2, gix-revision v0.27.0, gix-refspec v0.23.0, gix-status v0.7.0, gix-submodule v0.10.0, gix-worktree-state v0.9.0, gix v0.60.0, safety bump 26 crates ([`b050327`](https://github.com/Byron/gitoxide/commit/b050327e76f234b19be921b78b7b28e034319fdb)) + - Prepare changelogs prior to release ([`52c3bbd`](https://github.com/Byron/gitoxide/commit/52c3bbd36b9e94a0f3a78b4ada84d0c08eba27f6)) + - Merge branch 'status' ([`3e5c974`](https://github.com/Byron/gitoxide/commit/3e5c974dd62ac134711c6c2f5a5490187a6ea55e)) + - Fix lints for nightly, and clippy ([`f8ce3d0`](https://github.com/Byron/gitoxide/commit/f8ce3d0721b6a53713a9392f2451874f520bc44c)) +
+ ## 0.41.1 (2024-02-25) A maintenance release without user-facing changes. @@ -13,7 +172,7 @@ A maintenance release without user-facing changes. - - 2 commits contributed to the release over the course of 21 calendar days. + - 3 commits contributed to the release over the course of 21 calendar days. - 36 days passed between releases. - 0 commits were understood as [conventional](https://www.conventionalcommits.org). - 0 issues like '(#ID)' were seen in commit messages @@ -25,6 +184,7 @@ A maintenance release without user-facing changes.
view details * **Uncategorized** + - Release gix-date v0.8.4, gix-utils v0.1.10, gix-actor v0.30.1, gix-object v0.41.1, gix-path v0.10.6, gix-glob v0.16.1, gix-quote v0.4.11, gix-attributes v0.22.1, gix-command v0.3.5, gix-filter v0.10.0, gix-commitgraph v0.24.1, gix-worktree-stream v0.10.0, gix-archive v0.10.0, gix-config-value v0.14.5, gix-ref v0.42.0, gix-sec v0.10.5, gix-config v0.35.0, gix-prompt v0.8.3, gix-url v0.27.1, gix-credentials v0.24.1, gix-ignore v0.11.1, gix-index v0.30.0, gix-worktree v0.31.0, gix-diff v0.41.0, gix-discover v0.30.0, gix-pathspec v0.7.0, gix-dir v0.1.0, gix-pack v0.48.0, gix-odb v0.58.0, gix-transport v0.41.1, gix-protocol v0.44.1, gix-revision v0.26.1, gix-refspec v0.22.1, gix-status v0.6.0, gix-submodule v0.9.0, gix-worktree-state v0.8.0, gix v0.59.0, gix-fsck v0.3.0, gitoxide-core v0.36.0, gitoxide v0.34.0, safety bump 10 crates ([`45b4470`](https://github.com/Byron/gitoxide/commit/45b447045bc826f252129c300c531acde2652c64)) - Prepare changelogs prior to release ([`f2e111f`](https://github.com/Byron/gitoxide/commit/f2e111f768fc1bc6182355261c20b63610cffec7)) - Release gix-command v0.3.4 ([`8a62fb5`](https://github.com/Byron/gitoxide/commit/8a62fb57f7751d3d57273d9430517487e555f999))
diff --git a/gix-transport/Cargo.toml b/gix-transport/Cargo.toml index c6947b67bc6..c028fb53eaa 100644 --- a/gix-transport/Cargo.toml +++ b/gix-transport/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "gix-transport" -version = "0.41.1" +version = "0.42.1" repository = "https://github.com/Byron/gitoxide" license = "MIT OR Apache-2.0" description = "A crate of the gitoxide project dedicated to implementing the git transport layer" @@ -22,20 +22,44 @@ default = [] ## If set, blocking implementations of the typical git transports become available in `crate::client` blocking-client = ["gix-packetline/blocking-io"] ## Implies `blocking-client`, and adds support for the http and https transports. -http-client = ["base64", "gix-features/io-pipe", "blocking-client", "gix-credentials"] +http-client = [ + "base64", + "gix-features/io-pipe", + "blocking-client", + "gix-credentials", +] ## Implies `http-client`, and adds support for the http and https transports using the Rust bindings for `libcurl`. http-client-curl = ["curl", "http-client"] +## Implies `http-client-curl` and enables `rustls` for creating `https://` connections. +http-client-curl-rust-tls = ["http-client-curl", "curl/rustls"] ### Implies `http-client` and adds support for http and https transports using the blocking version of `reqwest`. http-client-reqwest = ["reqwest", "http-client"] +## Stacks with `blocking-http-transport-reqwest` and enables `https://` via the `rustls` crate. +http-client-reqwest-rust-tls = ["http-client-reqwest", "reqwest/rustls-tls"] +## Stacks with `blocking-http-transport-reqwest` and enables `https://` via the `rustls` crate. +## This also makes use of `trust-dns` to avoid `getaddrinfo`, but note it comes with its own problems. +http-client-reqwest-rust-tls-trust-dns = [ + "http-client-reqwest", + "reqwest/rustls-tls", + "reqwest/trust-dns", +] +## Stacks with `blocking-http-transport-reqwest` and enables `https://` via the `native-tls` crate. +http-client-reqwest-native-tls = ["http-client-reqwest", "reqwest/default-tls"] ## If set, an async implementations of the git transports becomes available in `crate::client`. ## Suitable for implementing your own transports while using git's way of communication, typically in conjunction with a custom server. ## **Note** that the _blocking_ client has a wide range of available transports, with the _async_ version of it supporting only the TCP based `git` transport leaving you ## with the responsibility to providing such an implementation of `futures-io::AsyncRead/AsyncWrite` yourself. -async-client = ["gix-packetline/async-io", "async-trait", "futures-lite", "futures-io", "pin-project-lite"] +async-client = [ + "gix-packetline/async-io", + "async-trait", + "futures-lite", + "futures-io", + "pin-project-lite", +] #! ### Other ## Data structures implement `serde::Serialize` and `serde::Deserialize`. -serde= ["dep:serde"] +serde = ["dep:serde"] [[test]] name = "blocking-transport" @@ -53,16 +77,22 @@ path = "tests/async-transport.rs" required-features = ["async-client"] [dependencies] -gix-command = { version = "^0.3.5", path = "../gix-command" } -gix-features = { version = "^0.38.0", path = "../gix-features" } -gix-url = { version = "^0.27.1", path = "../gix-url" } -gix-sec = { version = "^0.10.5", path = "../gix-sec" } -gix-packetline = { version = "^0.17.3", path = "../gix-packetline" } -gix-credentials = { version = "^0.24.1", path = "../gix-credentials", optional = true } -gix-quote = { version = "^0.4.11", path = "../gix-quote" } - -serde = { version = "1.0.114", optional = true, default-features = false, features = ["std", "derive"]} -bstr = { version = "1.3.0", default-features = false, features = ["std", "unicode"] } +gix-command = { version = "^0.3.7", path = "../gix-command" } +gix-features = { version = "^0.38.2", path = "../gix-features" } +gix-url = { version = "^0.27.3", path = "../gix-url" } +gix-sec = { version = "^0.10.6", path = "../gix-sec" } +gix-packetline = { version = "^0.17.5", path = "../gix-packetline" } +gix-credentials = { version = "^0.24.2", path = "../gix-credentials", optional = true } +gix-quote = { version = "^0.4.12", path = "../gix-quote" } + +serde = { version = "1.0.114", optional = true, default-features = false, features = [ + "std", + "derive", +] } +bstr = { version = "1.3.0", default-features = false, features = [ + "std", + "unicode", +] } thiserror = "1.0.26" # for async-client @@ -72,13 +102,13 @@ futures-lite = { workspace = true, optional = true } pin-project-lite = { version = "0.2.6", optional = true } # for http-client -base64 = { version = "0.21.0", optional = true } +base64 = { version = "0.22.1", optional = true } # for http-client-curl. Additional configuration should be performed on higher levels of the dependency tree. -curl = { version = "0.4", optional = true } +curl = { workspace = true, optional = true } # for http-client-reqwest -reqwest = { version = "0.11.12", optional = true, default-features = false, features = ["blocking"] } +reqwest = { workspace = true, optional = true, features = ["blocking"] } ## If used in conjunction with `async-client`, the `connect()` method will become available along with supporting the git protocol over TCP, ## where the TCP stream is created using this crate. @@ -87,7 +117,9 @@ async-std = { version = "1.12.0", optional = true } document-features = { version = "0.2.0", optional = true } [dev-dependencies] -gix-pack = { path = "../gix-pack", default-features = false, features = ["streaming-input"] } +gix-pack = { path = "../gix-pack", default-features = false, features = [ + "streaming-input", +] } gix-hash = { path = "../gix-hash" } async-std = { version = "1.9.0", features = ["attributes"] } maybe-async = "0.2.6" diff --git a/gix-transport/src/client/async_io/connect.rs b/gix-transport/src/client/async_io/connect.rs index 4d09d0e98d2..72ba2289613 100644 --- a/gix-transport/src/client/async_io/connect.rs +++ b/gix-transport/src/client/async_io/connect.rs @@ -2,8 +2,6 @@ pub use crate::client::non_io_types::connect::{Error, Options}; #[cfg(feature = "async-std")] pub(crate) mod function { - use std::convert::TryInto; - use crate::client::{git, non_io_types::connect::Error}; /// A general purpose connector connecting to a repository identified by the given `url`. diff --git a/gix-transport/src/client/async_io/mod.rs b/gix-transport/src/client/async_io/mod.rs index 1ea85cdcf58..e2e2cab99a9 100644 --- a/gix-transport/src/client/async_io/mod.rs +++ b/gix-transport/src/client/async_io/mod.rs @@ -8,6 +8,7 @@ mod traits; pub use traits::{SetServiceResponse, Transport, TransportV2Ext}; /// +#[allow(clippy::empty_docs)] pub mod connect; #[cfg(feature = "async-std")] pub use connect::function::connect; diff --git a/gix-transport/src/client/blocking_io/connect.rs b/gix-transport/src/client/blocking_io/connect.rs index 557a5914b8e..59f9e02c33c 100644 --- a/gix-transport/src/client/blocking_io/connect.rs +++ b/gix-transport/src/client/blocking_io/connect.rs @@ -1,8 +1,6 @@ pub use crate::client::non_io_types::connect::{Error, Options}; pub(crate) mod function { - use std::convert::TryInto; - use crate::client::{non_io_types::connect::Error, Transport}; /// A general purpose connector connecting to a repository identified by the given `url`. diff --git a/gix-transport/src/client/blocking_io/file.rs b/gix-transport/src/client/blocking_io/file.rs index 5106e407ad3..8f0186c59aa 100644 --- a/gix-transport/src/client/blocking_io/file.rs +++ b/gix-transport/src/client/blocking_io/file.rs @@ -126,50 +126,63 @@ impl client::TransportWithoutIO for SpawnProcessOnDemand { } } +impl Drop for SpawnProcessOnDemand { + fn drop(&mut self) { + if let Some(mut child) = self.child.take() { + // The child process (e.g. `ssh`) may still be running at this point, so kill it before joining/waiting. + // In the happy-path case, it should have already exited gracefully, but in error cases or if the user + // interrupted the operation, it will likely still be running. + child.kill().ok(); + child.wait().ok(); + } + } +} + struct ReadStdoutFailOnError { recv: std::sync::mpsc::Receiver, read: std::process::ChildStdout, } +impl std::io::Read for ReadStdoutFailOnError { + fn read(&mut self, buf: &mut [u8]) -> std::io::Result { + let res = self.read.read(buf); + self.swap_err_if_present_in_stderr(buf.len(), res) + } +} + +impl ReadStdoutFailOnError { + fn swap_err_if_present_in_stderr(&self, wanted: usize, res: std::io::Result) -> std::io::Result { + match self.recv.try_recv().ok() { + Some(err) => Err(err), + None => match res { + Ok(n) if n == wanted => Ok(n), + Ok(n) => { + // TODO: fix this + // When parsing refs this seems to happen legitimately + // (even though we read packet lines only and should always know exactly how much to read) + // Maybe this still happens in `read_exact()` as sometimes we just don't get enough bytes + // despite knowing how many. + // To prevent deadlock, we have to set a timeout which slows down legitimate parts of the protocol. + // This code was specifically written to make the `cargo` test-suite pass, and we can reduce + // the timeouts even more once there is a native ssh transport that is used by `cargo`, it will + // be able to handle these properly. + // Alternatively, one could implement something like `read2` to avoid blocking on stderr entirely. + self.recv + .recv_timeout(std::time::Duration::from_millis(5)) + .ok() + .map_or(Ok(n), Err) + } + Err(err) => Err(self.recv.recv().ok().unwrap_or(err)), + }, + } + } +} + fn supervise_stderr( ssh_kind: ssh::ProgramKind, stderr: std::process::ChildStderr, stdout: std::process::ChildStdout, ) -> ReadStdoutFailOnError { - impl ReadStdoutFailOnError { - fn swap_err_if_present_in_stderr(&self, wanted: usize, res: std::io::Result) -> std::io::Result { - match self.recv.try_recv().ok() { - Some(err) => Err(err), - None => match res { - Ok(n) if n == wanted => Ok(n), - Ok(n) => { - // TODO: fix this - // When parsing refs this seems to happen legitimately - // (even though we read packet lines only and should always know exactly how much to read) - // Maybe this still happens in `read_exact()` as sometimes we just don't get enough bytes - // despite knowing how many. - // To prevent deadlock, we have to set a timeout which slows down legitimate parts of the protocol. - // This code was specifically written to make the `cargo` test-suite pass, and we can reduce - // the timeouts even more once there is a native ssh transport that is used by `cargo`, it will - // be able to handle these properly. - // Alternatively, one could implement something like `read2` to avoid blocking on stderr entirely. - self.recv - .recv_timeout(std::time::Duration::from_millis(5)) - .ok() - .map_or(Ok(n), Err) - } - Err(err) => Err(self.recv.recv().ok().unwrap_or(err)), - }, - } - } - } - impl std::io::Read for ReadStdoutFailOnError { - fn read(&mut self, buf: &mut [u8]) -> std::io::Result { - let res = self.read.read(buf); - self.swap_err_if_present_in_stderr(buf.len(), res) - } - } - let (send, recv) = std::sync::mpsc::sync_channel(1); std::thread::Builder::new() .name("supervise ssh stderr".into()) @@ -279,7 +292,7 @@ pub fn connect( mod tests { mod ssh { mod connect { - use crate::{client::blocking_io::ssh::connect, Protocol}; + use crate::{client::blocking_io::ssh, Protocol}; #[test] fn path() { @@ -292,10 +305,29 @@ mod tests { ("user@host.xy:~/repo", "~/repo"), ] { let url = gix_url::parse((*url).into()).expect("valid url"); - let cmd = connect(url, Protocol::V1, Default::default(), false).expect("parse success"); + let cmd = ssh::connect(url, Protocol::V1, Default::default(), false).expect("parse success"); assert_eq!(cmd.path, expected, "the path will be substituted by the remote shell"); } } + + #[test] + fn ambiguous_host_disallowed() { + for url in [ + "ssh://-oProxyCommand=open$IFS-aCalculator/foo", + "user@-oProxyCommand=open$IFS-aCalculator:username/repo", + ] { + let url = gix_url::parse((*url).into()).expect("valid url"); + let options = ssh::connect::Options { + command: Some("unrecognized".into()), + disallow_shell: false, + kind: None, + }; + assert!(matches!( + ssh::connect(url, Protocol::V1, options, false), + Err(ssh::Error::AmbiguousHostName { host }) if host == "-oProxyCommand=open$IFS-aCalculator", + )); + } + } } } } diff --git a/gix-transport/src/client/blocking_io/http/mod.rs b/gix-transport/src/client/blocking_io/http/mod.rs index d42be09014f..de717006eae 100644 --- a/gix-transport/src/client/blocking_io/http/mod.rs +++ b/gix-transport/src/client/blocking_io/http/mod.rs @@ -27,6 +27,7 @@ compile_error!("Cannot set both 'http-client-reqwest' and 'http-client-curl' fea #[cfg(feature = "http-client-curl")] /// +#[allow(clippy::empty_docs)] pub mod curl; /// The experimental `reqwest` backend. @@ -39,6 +40,7 @@ pub mod reqwest; mod traits; /// +#[allow(clippy::empty_docs)] pub mod options { /// A function to authenticate a URL. pub type AuthenticateFn = diff --git a/gix-transport/src/client/blocking_io/http/reqwest/mod.rs b/gix-transport/src/client/blocking_io/http/reqwest/mod.rs index 7c68b166ea2..9f07c41f64b 100644 --- a/gix-transport/src/client/blocking_io/http/reqwest/mod.rs +++ b/gix-transport/src/client/blocking_io/http/reqwest/mod.rs @@ -25,4 +25,5 @@ pub struct Options { } /// +#[allow(clippy::empty_docs)] pub mod remote; diff --git a/gix-transport/src/client/blocking_io/http/reqwest/remote.rs b/gix-transport/src/client/blocking_io/http/reqwest/remote.rs index 7f8e8284604..1aa8bdb08e8 100644 --- a/gix-transport/src/client/blocking_io/http/reqwest/remote.rs +++ b/gix-transport/src/client/blocking_io/http/reqwest/remote.rs @@ -1,6 +1,5 @@ use std::{ any::Any, - convert::TryFrom, io::{Read, Write}, str::FromStr, sync::{atomic, Arc}, diff --git a/gix-transport/src/client/blocking_io/mod.rs b/gix-transport/src/client/blocking_io/mod.rs index dfb3752af95..087aa6cc0c4 100644 --- a/gix-transport/src/client/blocking_io/mod.rs +++ b/gix-transport/src/client/blocking_io/mod.rs @@ -1,7 +1,9 @@ /// +#[allow(clippy::empty_docs)] pub mod connect; /// +#[allow(clippy::empty_docs)] pub mod file; /// #[cfg(feature = "http-client")] @@ -14,6 +16,7 @@ mod request; pub use request::RequestWriter; /// +#[allow(clippy::empty_docs)] pub mod ssh; mod traits; diff --git a/gix-transport/src/client/blocking_io/ssh/mod.rs b/gix-transport/src/client/blocking_io/ssh/mod.rs index 3d83493ca85..6820051a5ec 100644 --- a/gix-transport/src/client/blocking_io/ssh/mod.rs +++ b/gix-transport/src/client/blocking_io/ssh/mod.rs @@ -1,5 +1,7 @@ use std::process::Stdio; +use gix_url::ArgumentSafety::*; + use crate::{client::blocking_io, Protocol}; /// The error used in [`connect()`]. @@ -34,6 +36,7 @@ pub enum ProgramKind { mod program_kind; /// +#[allow(clippy::empty_docs)] pub mod invocation { use std::ffi::OsString; @@ -41,6 +44,8 @@ pub mod invocation { #[derive(Debug, thiserror::Error)] #[allow(missing_docs)] pub enum Error { + #[error("Username '{user}' could be mistaken for a command-line argument")] + AmbiguousUserName { user: String }, #[error("Host name '{host}' could be mistaken for a command-line argument")] AmbiguousHostName { host: String }, #[error("The 'Simple' ssh variant doesn't support {function}")] @@ -54,6 +59,7 @@ pub mod invocation { } /// +#[allow(clippy::empty_docs)] pub mod connect { use std::ffi::{OsStr, OsString}; @@ -114,9 +120,11 @@ pub fn connect( .stdin(Stdio::null()) .with_shell() .arg("-G") - .arg(url.host_argument_safe().ok_or_else(|| Error::AmbiguousHostName { - host: url.host().expect("set in ssh urls").into(), - })?), + .arg(match url.host_as_argument() { + Usable(host) => host, + Dangerous(host) => Err(Error::AmbiguousHostName { host: host.into() })?, + Absent => panic!("BUG: host should always be present in SSH URLs"), + }), ); gix_features::trace::debug!(cmd = ?cmd, "invoking `ssh` for feature check"); kind = if cmd.status().ok().map_or(false, |status| status.success()) { diff --git a/gix-transport/src/client/blocking_io/ssh/program_kind.rs b/gix-transport/src/client/blocking_io/ssh/program_kind.rs index 70905829f64..d7162c09b33 100644 --- a/gix-transport/src/client/blocking_io/ssh/program_kind.rs +++ b/gix-transport/src/client/blocking_io/ssh/program_kind.rs @@ -2,6 +2,8 @@ use std::{ffi::OsStr, io::ErrorKind}; use bstr::{BString, ByteSlice, ByteVec}; +use gix_url::ArgumentSafety::*; + use crate::{ client::{ssh, ssh::ProgramKind}, Protocol, @@ -60,23 +62,21 @@ impl ProgramKind { } } }; - let host_as_ssh_arg = match url.user() { - Some(user) => { - let host = url.host().expect("present in ssh urls"); - format!("{user}@{host}") - } - None => { - let host = url - .host_argument_safe() - .ok_or_else(|| ssh::invocation::Error::AmbiguousHostName { - host: url.host().expect("ssh host always set").into(), - })?; - host.into() - } + + let host_maybe_with_user_as_ssh_arg = match (url.user_as_argument(), url.host_as_argument()) { + (Usable(user), Usable(host)) => format!("{user}@{host}"), + (Usable(user), Dangerous(host)) => format!("{user}@{host}"), // The `user@` makes it safe. + (Absent, Usable(host)) => host.into(), + (Dangerous(user), _) => Err(ssh::invocation::Error::AmbiguousUserName { user: user.into() })?, + (_, Dangerous(host)) => Err(ssh::invocation::Error::AmbiguousHostName { host: host.into() })?, + (_, Absent) => panic!("BUG: host should always be present in SSH URLs"), }; - // Try to force ssh to yield english messages (for parsing later) - Ok(prepare.arg(host_as_ssh_arg).env("LANG", "C").env("LC_ALL", "C")) + Ok(prepare + .arg(host_maybe_with_user_as_ssh_arg) + // Try to force ssh to yield English messages (for parsing later). + .env("LANG", "C") + .env("LC_ALL", "C")) } /// Note that the caller has to assure that the ssh program is launched in English by setting the locale. diff --git a/gix-transport/src/client/blocking_io/ssh/tests.rs b/gix-transport/src/client/blocking_io/ssh/tests.rs index 4e4da780703..8fa19d0bb77 100644 --- a/gix-transport/src/client/blocking_io/ssh/tests.rs +++ b/gix-transport/src/client/blocking_io/ssh/tests.rs @@ -144,8 +144,25 @@ mod program_kind { assert!(call_args(kind, "ssh://user@host:43/p", Protocol::V2).ends_with("-P 43 user@host")); } } + #[test] - fn ambiguous_host_is_allowed_with_user() { + fn ambiguous_user_is_disallowed_explicit_ssh() { + assert!(matches!( + try_call(ProgramKind::Ssh, "ssh://-arg@host/p", Protocol::V2), + Err(ssh::invocation::Error::AmbiguousUserName { user }) if user == "-arg" + )) + } + + #[test] + fn ambiguous_user_is_disallowed_implicit_ssh() { + assert!(matches!( + try_call(ProgramKind::Ssh, "-arg@host:p/q", Protocol::V2), + Err(ssh::invocation::Error::AmbiguousUserName { user }) if user == "-arg" + )) + } + + #[test] + fn ambiguous_host_is_allowed_with_user_explicit_ssh() { assert_eq!( call_args(ProgramKind::Ssh, "ssh://user@-arg/p", Protocol::V2), joined(&["ssh", "-o", "SendEnv=GIT_PROTOCOL", "user@-arg"]) @@ -153,13 +170,37 @@ mod program_kind { } #[test] - fn ambiguous_host_is_disallowed() { + fn ambiguous_host_is_allowed_with_user_implicit_ssh() { + assert_eq!( + call_args(ProgramKind::Ssh, "user@-arg:p/q", Protocol::V2), + joined(&["ssh", "-o", "SendEnv=GIT_PROTOCOL", "user@-arg"]) + ); + } + + #[test] + fn ambiguous_host_is_disallowed_without_user() { assert!(matches!( try_call(ProgramKind::Ssh, "ssh://-arg/p", Protocol::V2), Err(ssh::invocation::Error::AmbiguousHostName { host }) if host == "-arg" )); } + #[test] + fn ambiguous_user_and_host_remain_disallowed_together_explicit_ssh() { + assert!(matches!( + try_call(ProgramKind::Ssh, "ssh://-arg@host/p", Protocol::V2), + Err(ssh::invocation::Error::AmbiguousUserName { user }) if user == "-arg" + )); + } + + #[test] + fn ambiguous_user_and_host_remain_disallowed_together_implicit_ssh() { + assert!(matches!( + try_call(ProgramKind::Ssh, "-userarg@-hostarg:p/q", Protocol::V2), + Err(ssh::invocation::Error::AmbiguousUserName { user }) if user == "-userarg" + )); + } + #[test] fn simple_cannot_handle_any_arguments() { assert!(matches!( diff --git a/gix-transport/src/client/capabilities.rs b/gix-transport/src/client/capabilities.rs index 21513ace664..408bf8bddeb 100644 --- a/gix-transport/src/client/capabilities.rs +++ b/gix-transport/src/client/capabilities.rs @@ -167,6 +167,7 @@ impl Capabilities { #[cfg(feature = "blocking-client")] /// +#[allow(clippy::empty_docs)] pub mod recv { use std::io; @@ -252,6 +253,7 @@ pub mod recv { #[cfg(feature = "async-client")] #[allow(missing_docs)] /// +#[allow(clippy::empty_docs)] pub mod recv { use bstr::ByteVec; use futures_io::AsyncRead; diff --git a/gix-transport/src/client/git/blocking_io.rs b/gix-transport/src/client/git/blocking_io.rs index 42f253dfb3a..eb072c8ec5d 100644 --- a/gix-transport/src/client/git/blocking_io.rs +++ b/gix-transport/src/client/git/blocking_io.rs @@ -132,6 +132,7 @@ where } /// +#[allow(clippy::empty_docs)] pub mod connect { use std::net::{TcpStream, ToSocketAddrs}; diff --git a/gix-transport/src/client/mod.rs b/gix-transport/src/client/mod.rs index b5296543e41..427a55f4683 100644 --- a/gix-transport/src/client/mod.rs +++ b/gix-transport/src/client/mod.rs @@ -23,6 +23,7 @@ pub use blocking_io::{ pub use connect::function::connect; /// +#[allow(clippy::empty_docs)] pub mod capabilities; #[doc(inline)] pub use capabilities::Capabilities; diff --git a/gix-transport/src/lib.rs b/gix-transport/src/lib.rs index 60f2d538403..6a698741f4f 100644 --- a/gix-transport/src/lib.rs +++ b/gix-transport/src/lib.rs @@ -82,6 +82,7 @@ mod traits { pub use traits::IsSpuriousError; /// +#[allow(clippy::empty_docs)] pub mod client; #[doc(inline)] diff --git a/gix-transport/tests/client/blocking_io/http/mod.rs b/gix-transport/tests/client/blocking_io/http/mod.rs index 94dac048c2c..42ec9010eb5 100644 --- a/gix-transport/tests/client/blocking_io/http/mod.rs +++ b/gix-transport/tests/client/blocking_io/http/mod.rs @@ -160,6 +160,7 @@ fn http_authentication_error_can_be_differentiated_and_identity_is_transmitted() .received_as_string() .lines() .map(str::to_lowercase) + .filter(ignore_reqwest_content_length) .collect::>(), format!( "GET /path/not-important/info/refs?service=git-upload-pack HTTP/1.1 @@ -187,6 +188,7 @@ Authorization: Basic dXNlcjpwYXNzd29yZA== .lines() .map(str::to_lowercase) .filter(|l| !l.starts_with("expect: ")) + .filter(ignore_reqwest_content_length) .collect::>(); // On linux on CI, for some reason, it won't have this chunk id here, but // it has it whenever and where-ever I run it. @@ -337,6 +339,7 @@ fn handshake_v1() -> crate::Result { .received_as_string() .lines() .map(str::to_lowercase) + .filter(ignore_reqwest_content_length) .collect::>(), format!( "GET /path/not/important/due/to/mock/info/refs?service=git-upload-pack HTTP/1.1 @@ -550,6 +553,7 @@ fn handshake_and_lsrefs_and_fetch_v2_impl(handshake_fixture: &str) -> crate::Res .received_as_string() .lines() .map(str::to_lowercase) + .filter(ignore_reqwest_content_length) .collect::>(), format!( "GET /path/not/important/due/to/mock/info/refs?service=git-upload-pack HTTP/1.1 @@ -680,3 +684,7 @@ fn check_content_type_is_case_insensitive() -> crate::Result { assert!(result.is_ok()); Ok(()) } + +fn ignore_reqwest_content_length(header_line: &String) -> bool { + header_line != "content-length: 0" +} diff --git a/gix-traverse/CHANGELOG.md b/gix-traverse/CHANGELOG.md index b0520165c40..b69df1c44b8 100644 --- a/gix-traverse/CHANGELOG.md +++ b/gix-traverse/CHANGELOG.md @@ -5,6 +5,100 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## 0.39.1 (2024-05-22) + +A maintenance release without user-facing changes. + +### Commit Statistics + + + + - 2 commits contributed to the release over the course of 8 calendar days. + - 38 days passed between releases. + - 0 commits were understood as [conventional](https://www.conventionalcommits.org). + - 0 issues like '(#ID)' were seen in commit messages + +### Commit Details + + + +
view details + + * **Uncategorized** + - Adjust changelogs prior to release ([`9511416`](https://github.com/Byron/gitoxide/commit/9511416a6cd0c571233f958c165329c8705c2498)) + - Release gix-date v0.8.6 ([`d3588ca`](https://github.com/Byron/gitoxide/commit/d3588ca4fe0364c88e42cdac24ceae548355d99d)) +
+ +## 0.39.0 (2024-04-13) + +### Bug Fixes (BREAKING) + + - Make `topo` more similar to `Ancestors`, but also rename `Ancestors` to `Simple` + - adjust the structure of `commit::ancestors` to be more consistent with other plumbing crates. + This makes sure that each time is reached by only a single path. + - simplify ancestor module.. + We remove ability to reuse `State` as ultimately,this capability + wasn't all that useful. + +### Commit Statistics + + + + - 14 commits contributed to the release over the course of 6 calendar days. + - 29 days passed between releases. + - 3 commits were understood as [conventional](https://www.conventionalcommits.org). + - 1 unique issue was worked on: [#1336](https://github.com/Byron/gitoxide/issues/1336) + +### Commit Details + + + +
view details + + * **[#1336](https://github.com/Byron/gitoxide/issues/1336)** + - Improve documentation of Topological DateOrder sorting and add baseline test ([`1f6c8ab`](https://github.com/Byron/gitoxide/commit/1f6c8abe9a0055631369ed6e03a6ce308b42242f)) + * **Uncategorized** + - Release gix-trace v0.1.9, gix-utils v0.1.12, gix-packetline-blocking v0.17.4, gix-filter v0.11.1, gix-fs v0.10.2, gix-traverse v0.39.0, gix-worktree-stream v0.12.0, gix-archive v0.12.0, gix-config v0.36.1, gix-url v0.27.3, gix-index v0.32.0, gix-worktree v0.33.0, gix-diff v0.43.0, gix-pathspec v0.7.3, gix-dir v0.4.0, gix-pack v0.50.0, gix-odb v0.60.0, gix-transport v0.42.0, gix-protocol v0.45.0, gix-status v0.9.0, gix-worktree-state v0.10.0, gix v0.62.0, gix-fsck v0.4.0, gitoxide-core v0.37.0, gitoxide v0.35.0, safety bump 14 crates ([`095c673`](https://github.com/Byron/gitoxide/commit/095c6739b2722a8b9af90776b435ef2da454c0e6)) + - Prepare changelogs prior to release ([`5755271`](https://github.com/Byron/gitoxide/commit/57552717f46f96c35ba4ddc0a64434354ef845e9)) + - Use `u8` for `WalkFlags` in the hopes that this can save memory. ([`a5751bc`](https://github.com/Byron/gitoxide/commit/a5751bc10a414c6d7ad5f280e23962784faa4fc4)) + - Merge pull request #1341 from szepeviktor/typos ([`55f379b`](https://github.com/Byron/gitoxide/commit/55f379bc47065822d078393d83d30c0835a89782)) + - Fix typos ([`f72ecce`](https://github.com/Byron/gitoxide/commit/f72ecce45babcad2a0c9b73c79d01ff502907a57)) + - Merge branch 'add-topo-walk' ([`b590a9d`](https://github.com/Byron/gitoxide/commit/b590a9d2b6a273f76f0320d2b9fe1f679c08f549)) + - Make `topo` more similar to `Ancestors`, but also rename `Ancestors` to `Simple` ([`2a9c178`](https://github.com/Byron/gitoxide/commit/2a9c178326b7f13ba6bc1f89fc2b9d9facbecf48)) + - Adjust the structure of `commit::ancestors` to be more consistent with other plumbing crates. ([`3f938fa`](https://github.com/Byron/gitoxide/commit/3f938faf9e6323b8056ee286f299747e15e6a0d3)) + - Refactor ([`ee13bdb`](https://github.com/Byron/gitoxide/commit/ee13bdb94e574a35c43cc6b6353f39e6c2d5c786)) + - Simplify ancestor module.. ([`e0969c3`](https://github.com/Byron/gitoxide/commit/e0969c3cc928b31345c4f3a122466193697eb0fc)) + - Initial implementation of topological commit walk ([`25b3d23`](https://github.com/Byron/gitoxide/commit/25b3d23bc370825ee15c68fd869b3ee5f4f79a1f)) + - Move some utilities to super module to make them sharable ([`5b3dfbb`](https://github.com/Byron/gitoxide/commit/5b3dfbb129ae5e53fd63ce1a8637a35d7836b909)) + - Move [Aa]ncestors to under commit module ([`ecc049c`](https://github.com/Byron/gitoxide/commit/ecc049cf14187d751c4d33b4c4b5d71bcce620b9)) +
+ +## 0.38.0 (2024-03-14) + +A maintenance release without user-facing changes. + +### Commit Statistics + + + + - 4 commits contributed to the release over the course of 4 calendar days. + - 54 days passed between releases. + - 0 commits were understood as [conventional](https://www.conventionalcommits.org). + - 0 issues like '(#ID)' were seen in commit messages + +### Commit Details + + + +
view details + + * **Uncategorized** + - Release gix-date v0.8.5, gix-hash v0.14.2, gix-trace v0.1.8, gix-utils v0.1.11, gix-features v0.38.1, gix-actor v0.31.0, gix-validate v0.8.4, gix-object v0.42.0, gix-path v0.10.7, gix-glob v0.16.2, gix-quote v0.4.12, gix-attributes v0.22.2, gix-command v0.3.6, gix-filter v0.11.0, gix-fs v0.10.1, gix-chunk v0.4.8, gix-commitgraph v0.24.2, gix-hashtable v0.5.2, gix-revwalk v0.13.0, gix-traverse v0.38.0, gix-worktree-stream v0.11.0, gix-archive v0.11.0, gix-config-value v0.14.6, gix-tempfile v13.1.1, gix-lock v13.1.1, gix-ref v0.43.0, gix-sec v0.10.6, gix-config v0.36.0, gix-prompt v0.8.4, gix-url v0.27.2, gix-credentials v0.24.2, gix-ignore v0.11.2, gix-bitmap v0.2.11, gix-index v0.31.0, gix-worktree v0.32.0, gix-diff v0.42.0, gix-discover v0.31.0, gix-pathspec v0.7.1, gix-dir v0.2.0, gix-macros v0.1.4, gix-mailmap v0.23.0, gix-negotiate v0.13.0, gix-pack v0.49.0, gix-odb v0.59.0, gix-packetline v0.17.4, gix-transport v0.41.2, gix-protocol v0.44.2, gix-revision v0.27.0, gix-refspec v0.23.0, gix-status v0.7.0, gix-submodule v0.10.0, gix-worktree-state v0.9.0, gix v0.60.0, safety bump 26 crates ([`b050327`](https://github.com/Byron/gitoxide/commit/b050327e76f234b19be921b78b7b28e034319fdb)) + - Prepare changelogs prior to release ([`52c3bbd`](https://github.com/Byron/gitoxide/commit/52c3bbd36b9e94a0f3a78b4ada84d0c08eba27f6)) + - Merge branch 'status' ([`3e5c974`](https://github.com/Byron/gitoxide/commit/3e5c974dd62ac134711c6c2f5a5490187a6ea55e)) + - Fix lints for nightly, and clippy ([`f8ce3d0`](https://github.com/Byron/gitoxide/commit/f8ce3d0721b6a53713a9392f2451874f520bc44c)) +
+ ## 0.37.0 (2024-01-20) A maintenance release without user-facing changes. @@ -13,7 +107,7 @@ A maintenance release without user-facing changes. - - 1 commit contributed to the release. + - 2 commits contributed to the release. - 4 days passed between releases. - 0 commits were understood as [conventional](https://www.conventionalcommits.org). - 0 issues like '(#ID)' were seen in commit messages @@ -25,6 +119,7 @@ A maintenance release without user-facing changes.
view details * **Uncategorized** + - Release gix-utils v0.1.9, gix-features v0.38.0, gix-actor v0.30.0, gix-object v0.41.0, gix-path v0.10.4, gix-glob v0.16.0, gix-attributes v0.22.0, gix-command v0.3.3, gix-packetline-blocking v0.17.3, gix-filter v0.9.0, gix-fs v0.10.0, gix-commitgraph v0.24.0, gix-revwalk v0.12.0, gix-traverse v0.37.0, gix-worktree-stream v0.9.0, gix-archive v0.9.0, gix-config-value v0.14.4, gix-tempfile v13.0.0, gix-lock v13.0.0, gix-ref v0.41.0, gix-sec v0.10.4, gix-config v0.34.0, gix-url v0.27.0, gix-credentials v0.24.0, gix-ignore v0.11.0, gix-index v0.29.0, gix-worktree v0.30.0, gix-diff v0.40.0, gix-discover v0.29.0, gix-mailmap v0.22.0, gix-negotiate v0.12.0, gix-pack v0.47.0, gix-odb v0.57.0, gix-pathspec v0.6.0, gix-packetline v0.17.3, gix-transport v0.41.0, gix-protocol v0.44.0, gix-revision v0.26.0, gix-refspec v0.22.0, gix-status v0.5.0, gix-submodule v0.8.0, gix-worktree-state v0.7.0, gix v0.58.0, safety bump 39 crates ([`eb6aa8f`](https://github.com/Byron/gitoxide/commit/eb6aa8f502314f886fc4ea3d52ab220763968208)) - Prepare changelogs prior to release ([`6a2e0be`](https://github.com/Byron/gitoxide/commit/6a2e0bebfdf012dc2ed0ff2604086081f2a0f96d))
@@ -60,12 +155,12 @@ A maintenance release without user-facing changes. ### Chore - - change `rust-version` manifest field back to 1.65. - They didn't actually need to be higher to work, and changing them - unecessarily can break downstream CI. - - Let's keep this value as low as possible, and only increase it when - more recent features are actually used. +- change `rust-version` manifest field back to 1.65. + They didn't actually need to be higher to work, and changing them + unecessarily can break downstream CI. + + Let's keep this value as low as possible, and only increase it when + more recent features are actually used. ### Commit Statistics @@ -93,8 +188,8 @@ A maintenance release without user-facing changes. ### Chore - - upgrade MSRV to v1.70 - Our MSRV follows the one of `helix`, which in turn follows Firefox. +- upgrade MSRV to v1.70 + Our MSRV follows the one of `helix`, which in turn follows Firefox. ### Commit Statistics @@ -239,7 +334,7 @@ A maintenance release without user-facing changes. ### Chore - - don't call crate 'WIP' in manifest anymore. +- don't call crate 'WIP' in manifest anymore. ### Commit Statistics @@ -611,16 +706,16 @@ A maintenance release without user-facing changes. ### Refactor (BREAKING) - - remove pack-cache from `Find::try_find(…)` - With the new architecture this can be an implementation detail without - forcing it to be Sync. - - move git_pack::data::Object to git_object::Data, massively alter git_odb::Find trait - This will break a lot, but has to happen to prepare these traits for the - next generation of object databases. +- remove pack-cache from `Find::try_find(…)` + With the new architecture this can be an implementation detail without + forcing it to be Sync. +- move git_pack::data::Object to git_object::Data, massively alter git_odb::Find trait + This will break a lot, but has to happen to prepare these traits for the + next generation of object databases. ### Other (BREAKING) - - Avoid duplicate module paths in 'tree' and 'commit' +- Avoid duplicate module paths in 'tree' and 'commit' ### Bug Fixes (BREAKING) @@ -660,24 +755,24 @@ A maintenance release without user-facing changes. ### Test - - ensure tests use 'merge.ff false' and recreate fixtures on each run +- ensure tests use 'merge.ff false' and recreate fixtures on each run ### Other - - commit traversal along the first parent… - …which allows to traverse only the 'mainline' part of the commit - ancestry, ignoring merges entirely. - - This kind of traversal is more suited to algorithms which want - to see a linear history as well as segments of commits between - references. - - try git-cliff… - …but to no avail as it simpy won't work for us if we don't follow - conventional commits properly, especially the absence of breaking - changes support is a real issue. - - It's probably better to just implement it ourselves in smart-release. - - object_id +- commit traversal along the first parent… + …which allows to traverse only the 'mainline' part of the commit + ancestry, ignoring merges entirely. + + This kind of traversal is more suited to algorithms which want + to see a linear history as well as segments of commits between + references. +- try git-cliff… + …but to no avail as it simpy won't work for us if we don't follow + conventional commits properly, especially the absence of breaking + changes support is a real issue. + + It's probably better to just implement it ourselves in smart-release. +- object_id ### Bug Fixes @@ -704,8 +799,8 @@ A maintenance release without user-facing changes. ### Chore - - uniformize deny attributes - - remove default link to cargo doc everywhere +- uniformize deny attributes +- remove default link to cargo doc everywhere ### Documentation @@ -1085,8 +1180,8 @@ Maintenance release without user-facing changes. ### Chore - - uniformize deny attributes - - remove default link to cargo doc everywhere +- uniformize deny attributes +- remove default link to cargo doc everywhere ## 0.16.1 (2022-08-17) @@ -1140,7 +1235,7 @@ A maintenance release without user-facing changes. ### Test - - ensure tests use 'merge.ff false' and recreate fixtures on each run +- ensure tests use 'merge.ff false' and recreate fixtures on each run ### New Features @@ -1150,12 +1245,12 @@ A maintenance release without user-facing changes. - rename `commit::Ancestors::mode()` to `*::parents()` The previous name was too generic to be helpful or discoverable. - - remove pack-cache from `Find::try_find(…)` - With the new architecture this can be an implementation detail without - forcing it to be Sync. - - move gix_pack::data::Object to gix_object::Data, massively alter gix_odb::Find trait - This will break a lot, but has to happen to prepare these traits for the - next generation of object databases. +- remove pack-cache from `Find::try_find(…)` + With the new architecture this can be an implementation detail without + forcing it to be Sync. +- move gix_pack::data::Object to gix_object::Data, massively alter gix_odb::Find trait + This will break a lot, but has to happen to prepare these traits for the + next generation of object databases. ## 0.11.0 (2021-11-29) @@ -1180,24 +1275,24 @@ Some module paths have been removed to avoid path duplication, possibly leading ### Other - - try git-cliff… - …but to no avail as it simpy won't work for us if we don't follow - conventional commits properly, especially the absence of breaking - changes support is a real issue. - - It's probably better to just implement it ourselves in smart-release. - - object_id - - commit traversal along the first parent… - …which allows to traverse only the 'mainline' part of the commit - ancestry, ignoring merges entirely. - - This kind of traversal is more suited to algorithms which want - to see a linear history as well as segments of commits between - references. +- try git-cliff… + …but to no avail as it simpy won't work for us if we don't follow + conventional commits properly, especially the absence of breaking + changes support is a real issue. + + It's probably better to just implement it ourselves in smart-release. +- object_id +- commit traversal along the first parent… + …which allows to traverse only the 'mainline' part of the commit + ancestry, ignoring merges entirely. + + This kind of traversal is more suited to algorithms which want + to see a linear history as well as segments of commits between + references. ### BREAKING - - Avoid duplicate module paths in `tree` and `commit` +- Avoid duplicate module paths in `tree` and `commit` ## v0.8.2 (2021-09-08) diff --git a/gix-traverse/Cargo.toml b/gix-traverse/Cargo.toml index 3245a97192c..1832115e9d8 100644 --- a/gix-traverse/Cargo.toml +++ b/gix-traverse/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "gix-traverse" -version = "0.37.0" +version = "0.39.1" repository = "https://github.com/Byron/gitoxide" license = "MIT OR Apache-2.0" description = "A crate of the gitoxide project" @@ -14,11 +14,12 @@ autotests = false doctest = false [dependencies] -gix-hash = { version = "^0.14.1", path = "../gix-hash" } -gix-object = { version = "^0.41.0", path = "../gix-object" } -gix-date = { version = "^0.8.3", path = "../gix-date" } -gix-hashtable = { version = "^0.5.1", path = "../gix-hashtable" } -gix-revwalk = { version = "^0.12.0", path = "../gix-revwalk" } -gix-commitgraph = { version = "^0.24.0", path = "../gix-commitgraph" } +gix-hash = { version = "^0.14.2", path = "../gix-hash" } +gix-object = { version = "^0.42.2", path = "../gix-object" } +gix-date = { version = "^0.8.6", path = "../gix-date" } +gix-hashtable = { version = "^0.5.2", path = "../gix-hashtable" } +gix-revwalk = { version = "^0.13.1", path = "../gix-revwalk" } +gix-commitgraph = { version = "^0.24.2", path = "../gix-commitgraph" } smallvec = "1.10.0" thiserror = "1.0.32" +bitflags = "2" diff --git a/gix-traverse/src/commit/mod.rs b/gix-traverse/src/commit/mod.rs new file mode 100644 index 00000000000..26e9287ae70 --- /dev/null +++ b/gix-traverse/src/commit/mod.rs @@ -0,0 +1,88 @@ +//! Provide multiple traversal implementations with different performance envelopes. +//! +//! Use [`Simple`] for fast walks that maintain minimal state, or [`Topo`] for a more elaborate traversal. +use gix_hash::ObjectId; +use gix_object::FindExt; +use gix_revwalk::graph::IdMap; +use gix_revwalk::PriorityQueue; +use smallvec::SmallVec; + +/// A fast iterator over the ancestors of one or more starting commits. +pub struct Simple { + objects: Find, + cache: Option, + predicate: Predicate, + state: simple::State, + parents: Parents, + sorting: simple::Sorting, +} + +/// Simple ancestors traversal, without the need to keep track of graph-state. +pub mod simple; + +/// A commit walker that walks in topographical order, like `git rev-list +/// --topo-order` or `--date-order` depending on the chosen [`topo::Sorting`]. +/// +/// Instantiate with [`topo::Builder`]. +pub struct Topo { + commit_graph: Option, + find: Find, + predicate: Predicate, + indegrees: IdMap, + states: IdMap, + explore_queue: PriorityQueue, + indegree_queue: PriorityQueue, + topo_queue: topo::iter::Queue, + parents: Parents, + min_gen: u32, + buf: Vec, +} + +pub mod topo; + +/// Specify how to handle commit parents during traversal. +#[derive(Default, Copy, Clone)] +pub enum Parents { + /// Traverse all parents, useful for traversing the entire ancestry. + #[default] + All, + /// Only traverse along the first parent, which commonly ignores all branches. + First, +} + +/// The collection of parent ids we saw as part of the iteration. +/// +/// Note that this list is truncated if [`Parents::First`] was used. +pub type ParentIds = SmallVec<[gix_hash::ObjectId; 1]>; + +/// Information about a commit that we obtained naturally as part of the iteration. +#[derive(Debug, Clone, PartialEq, Eq, Ord, PartialOrd, Hash)] +pub struct Info { + /// The id of the commit. + pub id: gix_hash::ObjectId, + /// All parent ids we have encountered. Note that these will be at most one if [`Parents::First`] is enabled. + pub parent_ids: ParentIds, + /// The time at which the commit was created. It will only be `Some(_)` if the chosen traversal was + /// taking dates into consideration. + pub commit_time: Option, +} + +enum Either<'buf, 'cache> { + CommitRefIter(gix_object::CommitRefIter<'buf>), + CachedCommit(gix_commitgraph::file::Commit<'cache>), +} + +fn find<'cache, 'buf, Find>( + cache: Option<&'cache gix_commitgraph::Graph>, + objects: Find, + id: &gix_hash::oid, + buf: &'buf mut Vec, +) -> Result, gix_object::find::existing_iter::Error> +where + Find: gix_object::Find, +{ + match cache.and_then(|cache| cache.commit_by_id(id).map(Either::CachedCommit)) { + Some(c) => Ok(c), + None => objects.find_commit_iter(id, buf).map(Either::CommitRefIter), + } +} diff --git a/gix-traverse/src/commit.rs b/gix-traverse/src/commit/simple.rs similarity index 74% rename from gix-traverse/src/commit.rs rename to gix-traverse/src/commit/simple.rs index 4bf49d2a0b4..10e5080a70e 100644 --- a/gix-traverse/src/commit.rs +++ b/gix-traverse/src/commit/simple.rs @@ -1,27 +1,10 @@ -use gix_object::FindExt; +use gix_date::SecondsSinceUnixEpoch; +use gix_hash::ObjectId; +use gix_hashtable::HashSet; use smallvec::SmallVec; +use std::collections::VecDeque; -/// An iterator over the ancestors one or more starting commits -pub struct Ancestors { - objects: Find, - cache: Option, - predicate: Predicate, - state: StateMut, - parents: Parents, - sorting: Sorting, -} - -/// Specify how to handle commit parents during traversal. -#[derive(Default, Copy, Clone)] -pub enum Parents { - /// Traverse all parents, useful for traversing the entire ancestry. - #[default] - All, - /// Only traverse along the first parent, which commonly ignores all branches. - First, -} - -/// Specify how to sort commits during traversal. +/// Specify how to sort commits during a [simple](super::Simple) traversal. /// /// ### Sample History /// @@ -33,7 +16,6 @@ pub enum Parents { /// \ \ /// 3----5----6----8--- /// ``` - #[derive(Default, Debug, Copy, Clone)] pub enum Sorting { /// Commits are sorted as they are mentioned in the commit graph. @@ -69,58 +51,38 @@ pub enum Sorting { }, } -/// The collection of parent ids we saw as part of the iteration. -/// -/// Note that this list is truncated if [`Parents::First`] was used. -pub type ParentIds = SmallVec<[gix_hash::ObjectId; 1]>; +/// The error is part of the item returned by the [Ancestors](super::Simple) iterator. +#[derive(Debug, thiserror::Error)] +#[allow(missing_docs)] +pub enum Error { + #[error(transparent)] + Find(#[from] gix_object::find::existing_iter::Error), + #[error(transparent)] + ObjectDecode(#[from] gix_object::decode::Error), +} -/// Information about a commit that we obtained naturally as part of the iteration. -#[derive(Debug, Clone, PartialEq, Eq, Ord, PartialOrd, Hash)] -pub struct Info { - /// The id of the commit. - pub id: gix_hash::ObjectId, - /// All parent ids we have encountered. Note that these will be at most one if [`Parents::First`] is enabled. - pub parent_ids: ParentIds, - /// The time at which the commit was created. It's only `Some(_)` if sorting is not [`Sorting::BreadthFirst`], as the walk - /// needs to require the commit-date. - pub commit_time: Option, +/// The state used and potentially shared by multiple graph traversals. +#[derive(Clone)] +pub(super) struct State { + next: VecDeque, + queue: gix_revwalk::PriorityQueue, + buf: Vec, + seen: HashSet, + parents_buf: Vec, + parent_ids: SmallVec<[(ObjectId, SecondsSinceUnixEpoch); 2]>, } /// -pub mod ancestors { - use std::{ - borrow::{Borrow, BorrowMut}, - collections::VecDeque, - }; - +#[allow(clippy::empty_docs)] +mod init { use gix_date::SecondsSinceUnixEpoch; use gix_hash::{oid, ObjectId}; - use gix_hashtable::HashSet; use gix_object::{CommitRefIter, FindExt}; - use smallvec::SmallVec; - - use crate::commit::{collect_parents, Ancestors, Either, Info, ParentIds, Parents, Sorting}; - /// The error is part of the item returned by the [Ancestors] iterator. - #[derive(Debug, thiserror::Error)] - #[allow(missing_docs)] - pub enum Error { - #[error(transparent)] - Find(#[from] gix_object::find::existing_iter::Error), - #[error(transparent)] - ObjectDecode(#[from] gix_object::decode::Error), - } - - /// The state used and potentially shared by multiple graph traversals. - #[derive(Clone)] - pub struct State { - next: VecDeque, - queue: gix_revwalk::PriorityQueue, - buf: Vec, - seen: HashSet, - parents_buf: Vec, - parent_ids: SmallVec<[(ObjectId, SecondsSinceUnixEpoch); 2]>, - } + use super::{ + super::{simple::Sorting, Either, Info, ParentIds, Parents, Simple}, + collect_parents, Error, State, + }; impl Default for State { fn default() -> Self { @@ -145,12 +107,11 @@ pub mod ancestors { } /// Builder - impl Ancestors + impl Simple where Find: gix_object::Find, - StateMut: BorrowMut, { - /// Set the sorting method, either topological or by author date + /// Set the `sorting` method. pub fn sorting(mut self, sorting: Sorting) -> Result { self.sorting = sorting; match self.sorting { @@ -159,7 +120,7 @@ pub mod ancestors { } Sorting::ByCommitTimeNewestFirst | Sorting::ByCommitTimeNewestFirstCutoffOlderThan { .. } => { let cutoff_time = self.sorting.cutoff_time(); - let state = self.state.borrow_mut(); + let state = &mut self.state; for commit_id in state.next.drain(..) { let commit_iter = self.objects.find_commit_iter(&commit_id, &mut state.buf)?; let time = commit_iter.committer()?.time.seconds; @@ -197,7 +158,7 @@ pub mod ancestors { } fn queue_to_vecdeque(&mut self) { - let state = self.state.borrow_mut(); + let state = &mut self.state; state.next.extend( std::mem::replace(&mut state.queue, gix_revwalk::PriorityQueue::new()) .into_iter_unordered() @@ -206,41 +167,35 @@ pub mod ancestors { } } - /// Initialization - impl Ancestors bool, StateMut> + /// Lifecycle + impl Simple bool> where Find: gix_object::Find, - StateMut: BorrowMut, { /// Create a new instance. /// /// * `find` - a way to lookup new object data during traversal by their `ObjectId`, writing their data into buffer and returning /// an iterator over commit tokens if the object is present and is a commit. Caching should be implemented within this function /// as needed. - /// * `state` - all state used for the traversal. If multiple traversals are performed, allocations can be minimized by reusing - /// this state. /// * `tips` /// * the starting points of the iteration, usually commits /// * each commit they lead to will only be returned once, including the tip that started it - pub fn new(tips: impl IntoIterator>, state: StateMut, find: Find) -> Self { - Self::filtered(tips, state, find, |_| true) + pub fn new(tips: impl IntoIterator>, find: Find) -> Self { + Self::filtered(tips, find, |_| true) } } - /// Initialization - impl Ancestors + /// Lifecycle + impl Simple where Find: gix_object::Find, Predicate: FnMut(&oid) -> bool, - StateMut: BorrowMut, { /// Create a new instance with commit filtering enabled. /// /// * `find` - a way to lookup new object data during traversal by their `ObjectId`, writing their data into buffer and returning /// an iterator over commit tokens if the object is present and is a commit. Caching should be implemented within this function /// as needed. - /// * `state` - all state used for the traversal. If multiple traversals are performed, allocations can be minimized by reusing - /// this state. /// * `tips` /// * the starting points of the iteration, usually commits /// * each commit they lead to will only be returned once, including the tip that started it @@ -248,13 +203,12 @@ pub mod ancestors { /// as whether its parent commits should be traversed. pub fn filtered( tips: impl IntoIterator>, - mut state: StateMut, find: Find, mut predicate: Predicate, ) -> Self { let tips = tips.into_iter(); + let mut state = State::default(); { - let state = state.borrow_mut(); state.clear(); state.next.reserve(tips.size_hint().0); for tip in tips.map(Into::into) { @@ -274,27 +228,24 @@ pub mod ancestors { } } } + /// Access - impl Ancestors - where - StateMut: Borrow, - { - /// Return an iterator for accessing more of the current commits data. + impl Simple { + /// Return an iterator for accessing data of the current commit, parsed lazily. pub fn commit_iter(&self) -> CommitRefIter<'_> { - CommitRefIter::from_bytes(&self.state.borrow().buf) + CommitRefIter::from_bytes(&self.state.buf) } - /// Return the current commits data. + /// Return the current commits' raw data, which can be parsed using [`gix_object::CommitRef::from_bytes()`]. pub fn commit_data(&self) -> &[u8] { - &self.state.borrow().buf + &self.state.buf } } - impl Iterator for Ancestors + impl Iterator for Simple where Find: gix_object::Find, Predicate: FnMut(&oid) -> bool, - StateMut: BorrowMut, { type Item = Result; @@ -324,21 +275,20 @@ pub mod ancestors { } /// Utilities - impl Ancestors + impl Simple where Find: gix_object::Find, Predicate: FnMut(&oid) -> bool, - StateMut: BorrowMut, { fn next_by_commit_date( &mut self, cutoff_older_than: Option, ) -> Option> { - let state = self.state.borrow_mut(); + let state = &mut self.state; let (commit_time, oid) = state.queue.pop()?; let mut parents: ParentIds = Default::default(); - match super::find(self.cache.as_ref(), &self.objects, &oid, &mut state.buf) { + match super::super::find(self.cache.as_ref(), &self.objects, &oid, &mut state.buf) { Ok(Either::CachedCommit(commit)) => { if !collect_parents(&mut state.parent_ids, self.cache.as_ref(), commit.iter_parents()) { // drop corrupt caches and try again with ODB @@ -395,17 +345,16 @@ pub mod ancestors { } /// Utilities - impl Ancestors + impl Simple where Find: gix_object::Find, Predicate: FnMut(&oid) -> bool, - StateMut: BorrowMut, { fn next_by_topology(&mut self) -> Option> { - let state = self.state.borrow_mut(); + let state = &mut self.state; let oid = state.next.pop_front()?; let mut parents: ParentIds = Default::default(); - match super::find(self.cache.as_ref(), &self.objects, &oid, &mut state.buf) { + match super::super::find(self.cache.as_ref(), &self.objects, &oid, &mut state.buf) { Ok(Either::CachedCommit(commit)) => { if !collect_parents(&mut state.parent_ids, self.cache.as_ref(), commit.iter_parents()) { // drop corrupt caches and try again with ODB @@ -454,11 +403,6 @@ pub mod ancestors { } } -enum Either<'buf, 'cache> { - CommitRefIter(gix_object::CommitRefIter<'buf>), - CachedCommit(gix_commitgraph::file::Commit<'cache>), -} - fn collect_parents( dest: &mut SmallVec<[(gix_hash::ObjectId, gix_date::SecondsSinceUnixEpoch); 2]>, cache: Option<&gix_commitgraph::Graph>, @@ -480,18 +424,3 @@ fn collect_parents( } true } - -fn find<'cache, 'buf, Find>( - cache: Option<&'cache gix_commitgraph::Graph>, - objects: Find, - id: &gix_hash::oid, - buf: &'buf mut Vec, -) -> Result, gix_object::find::existing_iter::Error> -where - Find: gix_object::Find, -{ - match cache.and_then(|cache| cache.commit_by_id(id).map(Either::CachedCommit)) { - Some(c) => Ok(c), - None => objects.find_commit_iter(id, buf).map(Either::CommitRefIter), - } -} diff --git a/gix-traverse/src/commit/topo/init.rs b/gix-traverse/src/commit/topo/init.rs new file mode 100644 index 00000000000..86fb45b7321 --- /dev/null +++ b/gix-traverse/src/commit/topo/init.rs @@ -0,0 +1,174 @@ +use crate::commit::topo::iter::gen_and_commit_time; +use crate::commit::topo::{Error, Sorting, WalkFlags}; +use crate::commit::{find, Info, Parents, Topo}; +use gix_hash::{oid, ObjectId}; +use gix_revwalk::graph::IdMap; +use gix_revwalk::PriorityQueue; + +/// Builder for [`Topo`]. +pub struct Builder { + commit_graph: Option, + find: Find, + predicate: Predicate, + sorting: Sorting, + parents: Parents, + tips: Vec, + ends: Vec, +} + +impl Builder bool> +where + Find: gix_object::Find, +{ + /// Create a new `Builder` for a [`Topo`] that reads commits from a repository with `find`. + /// starting at the `tips` and ending at the `ends`. Like `git rev-list + /// --topo-order ^ends tips`. + pub fn from_iters( + find: Find, + tips: impl IntoIterator>, + ends: Option>>, + ) -> Self { + let tips = tips.into_iter().map(Into::into).collect::>(); + let ends = ends + .map(|e| e.into_iter().map(Into::into).collect::>()) + .unwrap_or_default(); + + Self { + commit_graph: Default::default(), + find, + sorting: Default::default(), + parents: Default::default(), + tips, + ends, + predicate: |_| true, + } + } + + /// Set a `predicate` to filter out revisions from the walk. Can be used to + /// implement e.g. filtering on paths or time. This does *not* exclude the + /// parent(s) of a revision that is excluded. Specify a revision as an 'end' + /// if you want that behavior. + pub fn with_predicate(self, predicate: Predicate) -> Builder + where + Predicate: FnMut(&oid) -> bool, + { + Builder { + commit_graph: self.commit_graph, + find: self.find, + sorting: self.sorting, + parents: self.parents, + tips: self.tips, + ends: self.ends, + predicate, + } + } +} + +impl Builder +where + Find: gix_object::Find, + Predicate: FnMut(&oid) -> bool, +{ + /// Set the `sorting` to use for the topological walk. + pub fn sorting(mut self, sorting: Sorting) -> Self { + self.sorting = sorting; + self + } + + /// Specify how to handle commit `parents` during traversal. + pub fn parents(mut self, parents: Parents) -> Self { + self.parents = parents; + self + } + + /// Set or unset the `commit_graph` to use for the iteration. + pub fn with_commit_graph(mut self, commit_graph: Option) -> Self { + self.commit_graph = commit_graph; + self + } + + /// Build a new [`Topo`] instance. + /// + /// Note that merely building an instance is currently expensive. + pub fn build(self) -> Result, Error> { + let mut w = Topo { + commit_graph: self.commit_graph, + find: self.find, + predicate: self.predicate, + indegrees: IdMap::default(), + states: IdMap::default(), + explore_queue: PriorityQueue::new(), + indegree_queue: PriorityQueue::new(), + topo_queue: super::iter::Queue::new(self.sorting), + parents: self.parents, + min_gen: gix_commitgraph::GENERATION_NUMBER_INFINITY, + buf: vec![], + }; + + // Initial flags for the states of the tips and ends. All of them are + // seen and added to the explore and indegree queues. The ends are by + // definition (?) uninteresting and bottom. + let tip_flags = WalkFlags::Seen | WalkFlags::Explored | WalkFlags::InDegree; + let end_flags = tip_flags | WalkFlags::Uninteresting | WalkFlags::Bottom; + + for (id, flags) in self + .tips + .iter() + .map(|id| (id, tip_flags)) + .chain(self.ends.iter().map(|id| (id, end_flags))) + { + *w.indegrees.entry(*id).or_default() = 1; + let commit = find(w.commit_graph.as_ref(), &w.find, id, &mut w.buf)?; + let (gen, time) = gen_and_commit_time(commit)?; + + if gen < w.min_gen { + w.min_gen = gen; + } + + w.states.insert(*id, flags); + w.explore_queue.insert((gen, time), *id); + w.indegree_queue.insert((gen, time), *id); + } + + // NOTE: Parents of the ends must also be marked uninteresting for some + // reason. See handle_commit() + for id in &self.ends { + let parents = w.collect_all_parents(id)?; + for (id, _) in parents { + w.states + .entry(id) + .and_modify(|s| *s |= WalkFlags::Uninteresting) + .or_insert(WalkFlags::Uninteresting | WalkFlags::Seen); + } + } + + w.compute_indegrees_to_depth(w.min_gen)?; + + // NOTE: in Git the ends are also added to the topo_queue in addition to + // the tips, but then in simplify_commit() Git is told to ignore it. For + // now the tests pass. + for id in self.tips.iter() { + let i = w.indegrees.get(id).ok_or(Error::MissingIndegreeUnexpected)?; + + if *i != 1 { + continue; + } + + let commit = find(w.commit_graph.as_ref(), &w.find, id, &mut w.buf)?; + let (_, time) = gen_and_commit_time(commit)?; + let parent_ids = w.collect_all_parents(id)?.into_iter().map(|e| e.0).collect(); + + w.topo_queue.push( + time, + Info { + id: *id, + parent_ids, + commit_time: Some(time), + }, + ); + } + + w.topo_queue.initial_sort(); + Ok(w) + } +} diff --git a/gix-traverse/src/commit/topo/iter.rs b/gix-traverse/src/commit/topo/iter.rs new file mode 100644 index 00000000000..09f38eb7e7a --- /dev/null +++ b/gix-traverse/src/commit/topo/iter.rs @@ -0,0 +1,312 @@ +use crate::commit::topo::{Error, Sorting, WalkFlags}; +use crate::commit::{find, Either, Info, Parents, Topo}; +use gix_hash::{oid, ObjectId}; +use gix_revwalk::PriorityQueue; +use smallvec::SmallVec; + +pub(in crate::commit) type GenAndCommitTime = (u32, i64); + +// Git's priority queue works as a LIFO stack if no compare function is set, +// which is the case for `--topo-order.` However, even in that case the initial +// items of the queue are sorted according to the commit time before beginning +// the walk. +#[derive(Debug)] +pub(in crate::commit) enum Queue { + Date(PriorityQueue), + Topo(Vec<(i64, Info)>), +} + +impl Queue { + pub(super) fn new(s: Sorting) -> Self { + match s { + Sorting::DateOrder => Self::Date(PriorityQueue::new()), + Sorting::TopoOrder => Self::Topo(vec![]), + } + } + + pub(super) fn push(&mut self, commit_time: i64, info: Info) { + match self { + Self::Date(q) => q.insert(commit_time, info), + Self::Topo(q) => q.push((commit_time, info)), + } + } + + fn pop(&mut self) -> Option { + match self { + Self::Date(q) => q.pop().map(|(_, info)| info), + Self::Topo(q) => q.pop().map(|(_, info)| info), + } + } + + pub(super) fn initial_sort(&mut self) { + if let Self::Topo(ref mut inner_vec) = self { + inner_vec.sort_by(|a, b| a.0.cmp(&b.0)); + } + } +} + +impl Topo +where + Find: gix_object::Find, +{ + pub(super) fn compute_indegrees_to_depth(&mut self, gen_cutoff: u32) -> Result<(), Error> { + while let Some(((gen, _), _)) = self.indegree_queue.peek() { + if *gen >= gen_cutoff { + self.indegree_walk_step()?; + } else { + break; + } + } + + Ok(()) + } + + fn indegree_walk_step(&mut self) -> Result<(), Error> { + if let Some(((gen, _), id)) = self.indegree_queue.pop() { + self.explore_to_depth(gen)?; + + let parents = self.collect_parents(&id)?; + for (id, gen_time) in parents { + self.indegrees.entry(id).and_modify(|e| *e += 1).or_insert(2); + + let state = self.states.get_mut(&id).ok_or(Error::MissingStateUnexpected)?; + if !state.contains(WalkFlags::InDegree) { + *state |= WalkFlags::InDegree; + self.indegree_queue.insert(gen_time, id); + } + } + } + Ok(()) + } + + fn explore_to_depth(&mut self, gen_cutoff: u32) -> Result<(), Error> { + while let Some(((gen, _), _)) = self.explore_queue.peek() { + if *gen >= gen_cutoff { + self.explore_walk_step()?; + } else { + break; + } + } + Ok(()) + } + + fn explore_walk_step(&mut self) -> Result<(), Error> { + if let Some((_, id)) = self.explore_queue.pop() { + let parents = self.collect_parents(&id)?; + self.process_parents(&id, &parents)?; + + for (id, gen_time) in parents { + let state = self.states.get_mut(&id).ok_or(Error::MissingStateUnexpected)?; + + if !state.contains(WalkFlags::Explored) { + *state |= WalkFlags::Explored; + self.explore_queue.insert(gen_time, id); + } + } + } + Ok(()) + } + + fn expand_topo_walk(&mut self, id: &oid) -> Result<(), Error> { + let parents = self.collect_parents(id)?; + self.process_parents(id, &parents)?; + + for (pid, (parent_gen, parent_commit_time)) in parents { + let parent_state = self.states.get(&pid).ok_or(Error::MissingStateUnexpected)?; + if parent_state.contains(WalkFlags::Uninteresting) { + continue; + } + + if parent_gen < self.min_gen { + self.min_gen = parent_gen; + self.compute_indegrees_to_depth(self.min_gen)?; + } + + let i = self.indegrees.get_mut(&pid).ok_or(Error::MissingIndegreeUnexpected)?; + *i -= 1; + if *i != 1 { + continue; + } + + let parent_ids = self.collect_all_parents(&pid)?.into_iter().map(|e| e.0).collect(); + self.topo_queue.push( + parent_commit_time, + Info { + id: pid, + parent_ids, + commit_time: Some(parent_commit_time), + }, + ); + } + + Ok(()) + } + + fn process_parents(&mut self, id: &oid, parents: &[(ObjectId, GenAndCommitTime)]) -> Result<(), Error> { + let state = self.states.get_mut(id).ok_or(Error::MissingStateUnexpected)?; + if state.contains(WalkFlags::Added) { + return Ok(()); + } + + *state |= WalkFlags::Added; + + // If the current commit is uninteresting we pass that on to ALL + // parents, otherwise we set the Seen flag. + let (pass, insert) = if state.contains(WalkFlags::Uninteresting) { + let flags = WalkFlags::Uninteresting; + for (id, _) in parents { + let grand_parents = self.collect_all_parents(id)?; + + for (id, _) in &grand_parents { + self.states + .entry(*id) + .and_modify(|s| *s |= WalkFlags::Uninteresting) + .or_insert(WalkFlags::Uninteresting | WalkFlags::Seen); + } + } + (flags, flags) + } else { + // NOTE: git sets SEEN like we do but keeps the SYMMETRIC_LEFT and + // ANCENSTRY_PATH if they are set, but they have no purpose here. + let flags = WalkFlags::empty(); + (flags, WalkFlags::Seen) + }; + + for (id, _) in parents { + self.states.entry(*id).and_modify(|s| *s |= pass).or_insert(insert); + } + Ok(()) + } + + fn collect_parents(&mut self, id: &oid) -> Result, Error> { + collect_parents( + self.commit_graph.as_ref(), + &self.find, + id, + matches!(self.parents, Parents::First), + &mut self.buf, + ) + } + + // Same as collect_parents but disregards the first_parent flag + pub(super) fn collect_all_parents( + &mut self, + id: &oid, + ) -> Result, Error> { + collect_parents(self.commit_graph.as_ref(), &self.find, id, false, &mut self.buf) + } + + fn pop_commit(&mut self) -> Option> { + let commit = self.topo_queue.pop()?; + let i = match self.indegrees.get_mut(&commit.id) { + Some(i) => i, + None => { + return Some(Err(Error::MissingIndegreeUnexpected)); + } + }; + + *i = 0; + if let Err(e) = self.expand_topo_walk(&commit.id) { + return Some(Err(e)); + }; + + Some(Ok(commit)) + } +} + +impl Iterator for Topo +where + Find: gix_object::Find, + Predicate: FnMut(&oid) -> bool, +{ + type Item = Result; + + fn next(&mut self) -> Option { + loop { + match self.pop_commit()? { + Ok(id) => { + if (self.predicate)(&id.id) { + return Some(Ok(id)); + } + } + Err(e) => return Some(Err(e)), + } + } + } +} + +fn collect_parents( + cache: Option<&gix_commitgraph::Graph>, + f: Find, + id: &oid, + first_only: bool, + buf: &mut Vec, +) -> Result, Error> +where + Find: gix_object::Find, +{ + let mut parents = SmallVec::<[(ObjectId, GenAndCommitTime); 1]>::new(); + match find(cache, &f, id, buf)? { + Either::CommitRefIter(c) => { + for token in c { + use gix_object::commit::ref_iter::Token as T; + match token { + Ok(T::Tree { .. }) => continue, + Ok(T::Parent { id }) => { + parents.push((id, (0, 0))); // Dummy numbers to be filled in + if first_only { + break; + } + } + Ok(_past_parents) => break, + Err(err) => return Err(err.into()), + } + } + // Need to check the cache again. That a commit is not in the cache + // doesn't mean a parent is not. + for (id, gen_time) in parents.iter_mut() { + let commit = find(cache, &f, id, buf)?; + *gen_time = gen_and_commit_time(commit)?; + } + } + Either::CachedCommit(c) => { + for pos in c.iter_parents() { + let parent_commit = cache + .expect("cache exists if CachedCommit was returned") + .commit_at(pos?); + parents.push(( + parent_commit.id().into(), + (parent_commit.generation(), parent_commit.committer_timestamp() as i64), + )); + if first_only { + break; + } + } + } + }; + Ok(parents) +} + +pub(super) fn gen_and_commit_time(c: Either<'_, '_>) -> Result { + match c { + Either::CommitRefIter(c) => { + let mut commit_time = 0; + for token in c { + use gix_object::commit::ref_iter::Token as T; + match token { + Ok(T::Tree { .. }) => continue, + Ok(T::Parent { .. }) => continue, + Ok(T::Author { .. }) => continue, + Ok(T::Committer { signature }) => { + commit_time = signature.time.seconds; + break; + } + Ok(_unused_token) => break, + Err(err) => return Err(err.into()), + } + } + Ok((gix_commitgraph::GENERATION_NUMBER_INFINITY, commit_time)) + } + Either::CachedCommit(c) => Ok((c.generation(), c.committer_timestamp() as i64)), + } +} diff --git a/gix-traverse/src/commit/topo/mod.rs b/gix-traverse/src/commit/topo/mod.rs new file mode 100644 index 00000000000..3b81cdebf26 --- /dev/null +++ b/gix-traverse/src/commit/topo/mod.rs @@ -0,0 +1,73 @@ +//! Topological commit traversal, similar to `git log --topo-order`, which keeps track of graph state. + +use bitflags::bitflags; + +/// The errors that can occur during creation and iteration. +#[derive(thiserror::Error, Debug)] +#[allow(missing_docs)] +pub enum Error { + #[error("Indegree information is missing")] + MissingIndegreeUnexpected, + #[error("Internal state (bitflags) not found")] + MissingStateUnexpected, + #[error(transparent)] + CommitGraphFile(#[from] gix_commitgraph::file::commit::Error), + #[error(transparent)] + ObjectDecode(#[from] gix_object::decode::Error), + #[error(transparent)] + Find(#[from] gix_object::find::existing_iter::Error), +} + +bitflags! { + /// Set of flags to describe the state of a particular commit while iterating. + // NOTE: The names correspond to the names of the flags in revision.h + #[repr(transparent)] + #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] + pub(super) struct WalkFlags: u8 { + /// Commit has been seen + const Seen = 0b000001; + /// Commit has been processed by the Explore walk + const Explored = 0b000010; + /// Commit has been processed by the Indegree walk + const InDegree = 0b000100; + /// Commit is deemed uninteresting for whatever reason + const Uninteresting = 0b001000; + /// Commit marks the end of a walk, like `foo` in `git rev-list foo..bar` + const Bottom = 0b010000; + /// Parents have been processed + const Added = 0b100000; + } +} + +/// Sorting to use for the topological walk. +/// +/// ### Sample History +/// +/// The following history will be referred to for explaining how the sort order works, with the number denoting the commit timestamp +/// (*their X-alignment doesn't matter*). +/// +/// ```text +/// ---1----2----4----7 <- second parent of 8 +/// \ \ +/// 3----5----6----8--- +/// ``` +#[derive(Clone, Copy, Debug, Default)] +pub enum Sorting { + /// Show no parents before all of its children are shown, but otherwise show + /// commits in the commit timestamp order. + /// + /// This is equivalent to `git rev-list --date-order`. + #[default] + DateOrder, + /// Show no parents before all of its children are shown, and avoid + /// showing commits on multiple lines of history intermixed. + /// + /// In the *sample history* the order would be `8, 6, 5, 3, 7, 4, 2, 1`. + /// This is equivalent to `git rev-list --topo-order`. + TopoOrder, +} + +mod init; +pub use init::Builder; + +pub(super) mod iter; diff --git a/gix-traverse/src/lib.rs b/gix-traverse/src/lib.rs index 3cf6d2b3af7..49776318332 100644 --- a/gix-traverse/src/lib.rs +++ b/gix-traverse/src/lib.rs @@ -2,7 +2,6 @@ #![deny(missing_docs, rust_2018_idioms)] #![forbid(unsafe_code)] -/// Commit traversal pub mod commit; /// Tree traversal diff --git a/gix-traverse/src/tree/mod.rs b/gix-traverse/src/tree/mod.rs index 6a74ada28f3..2e41a1cfd50 100644 --- a/gix-traverse/src/tree/mod.rs +++ b/gix-traverse/src/tree/mod.rs @@ -41,6 +41,7 @@ pub struct Recorder { } /// +#[allow(clippy::empty_docs)] pub mod visit { /// What to do after an entry was [recorded][super::Visit::visit_tree()]. #[derive(Clone, Copy, PartialOrd, PartialEq, Ord, Eq, Hash)] @@ -62,8 +63,10 @@ pub mod visit { } /// +#[allow(clippy::empty_docs)] pub mod recorder; /// +#[allow(clippy::empty_docs)] pub mod breadthfirst; pub use breadthfirst::impl_::traverse as breadthfirst; diff --git a/gix-traverse/tests/commit/mod.rs b/gix-traverse/tests/commit/mod.rs index 6d9fac3e9c8..7aa327bfa53 100644 --- a/gix-traverse/tests/commit/mod.rs +++ b/gix-traverse/tests/commit/mod.rs @@ -1,430 +1,2 @@ -mod ancestor { - use gix_hash::{oid, ObjectId}; - use gix_traverse::commit; - - use crate::hex_to_id; - - struct TraversalAssertion<'a> { - init_script: &'a str, - repo_name: &'a str, - tips: &'a [&'a str], - expected: &'a [&'a str], - mode: commit::Parents, - sorting: commit::Sorting, - } - - impl<'a> TraversalAssertion<'a> { - fn new(init_script: &'a str, tips: &'a [&'a str], expected: &'a [&'a str]) -> Self { - Self::new_at(init_script, "", tips, expected) - } - - fn new_at(init_script: &'a str, repo_name: &'a str, tips: &'a [&'a str], expected: &'a [&'a str]) -> Self { - TraversalAssertion { - init_script, - repo_name, - tips, - expected, - mode: Default::default(), - sorting: Default::default(), - } - } - - fn with_parents(&mut self, mode: commit::Parents) -> &mut Self { - self.mode = mode; - self - } - - fn with_sorting(&mut self, sorting: commit::Sorting) -> &mut Self { - self.sorting = sorting; - self - } - } - - impl TraversalAssertion<'_> { - fn setup(&self) -> crate::Result<(gix_odb::Handle, Vec, Vec)> { - let dir = gix_testtools::scripted_fixture_read_only_standalone(self.init_script)?; - let store = gix_odb::at(dir.join(self.repo_name).join(".git").join("objects"))?; - let tips: Vec<_> = self.tips.iter().copied().map(hex_to_id).collect(); - let expected: Vec = tips - .clone() - .into_iter() - .chain(self.expected.iter().map(|hex_id| hex_to_id(hex_id))) - .collect(); - Ok((store, tips, expected)) - } - - fn setup_commitgraph(&self, store: &gix_odb::Store, use_graph: bool) -> Option { - use_graph - .then(|| gix_commitgraph::at(store.path().join("info"))) - .transpose() - .expect("graph can be loaded if it exists") - } - - fn check_with_predicate(&mut self, predicate: impl FnMut(&oid) -> bool + Clone) -> crate::Result<()> { - let (store, tips, expected) = self.setup()?; - - for use_commitgraph in [false, true] { - let oids = commit::Ancestors::filtered( - tips.clone(), - commit::ancestors::State::default(), - &store, - predicate.clone(), - ) - .sorting(self.sorting)? - .parents(self.mode) - .commit_graph(self.setup_commitgraph(store.store_ref(), use_commitgraph)) - .map(|res| res.map(|info| info.id)) - .collect::, _>>()?; - - assert_eq!(oids, expected); - } - Ok(()) - } - - fn check(&self) -> crate::Result { - let (store, tips, expected) = self.setup()?; - - for use_commitgraph in [false, true] { - let oids = commit::Ancestors::new(tips.clone(), commit::ancestors::State::default(), &store) - .sorting(self.sorting)? - .parents(self.mode) - .commit_graph(self.setup_commitgraph(store.store_ref(), use_commitgraph)) - .map(|res| res.map(|info| info.id)) - .collect::, _>>()?; - assert_eq!(oids, expected); - } - Ok(()) - } - } - - mod different_date_intermixed { - use gix_traverse::commit::Sorting; - - use crate::commit::ancestor::TraversalAssertion; - - #[test] - fn head_breadth_first() -> crate::Result { - TraversalAssertion::new_at( - "make_repos.sh", - "intermixed", - &["58912d92944087dcb09dca79cdd2a937cc158bed"], /* merge */ - // This is very different from what git does as it keeps commits together, - // whereas we spread them out breadth-first. - &[ - "2dce37be587e07caef8c4a5ab60b423b13a8536a", /* c3 */ - "0f6632a5a7d81417488b86692b729e49c1b73056", /* b1c2 */ - "a9c28710e058af4e5163699960234adb9fb2abc7", /* b2c2 */ - "ad33ff2d0c4fc77d56b5fbff6f86f332fe792d83", /* c2 */ - "77fd3c6832c0cd542f7a39f3af9250c3268db979", /* b1c1 */ - "b648f955b930ca95352fae6f22cb593ee0244b27", /* b2c1 */ - "65d6af66f60b8e39fd1ba6a1423178831e764ec5", /* c1 */ - ], - ) - .check() - } - - #[test] - fn head_date_order() -> crate::Result { - TraversalAssertion::new_at( - "make_repos.sh", - "intermixed", - &["58912d92944087dcb09dca79cdd2a937cc158bed"], /* merge */ - // This is exactly what git shows. - &[ - "2dce37be587e07caef8c4a5ab60b423b13a8536a", /* c3 */ - "0f6632a5a7d81417488b86692b729e49c1b73056", /* b1c2 */ - "a9c28710e058af4e5163699960234adb9fb2abc7", /* b2c2 */ - "77fd3c6832c0cd542f7a39f3af9250c3268db979", /* b1c1 */ - "b648f955b930ca95352fae6f22cb593ee0244b27", /* b2c1 */ - "ad33ff2d0c4fc77d56b5fbff6f86f332fe792d83", /* c2 */ - "65d6af66f60b8e39fd1ba6a1423178831e764ec5", /* c1 */ - ], - ) - .with_sorting(Sorting::ByCommitTimeNewestFirst) - .check() - } - } - - mod different_date { - use gix_traverse::commit::Sorting; - - use crate::commit::ancestor::TraversalAssertion; - - #[test] - fn head_breadth_first() -> crate::Result { - TraversalAssertion::new_at( - "make_repos.sh", - "simple", - &["f49838d84281c3988eeadd988d97dd358c9f9dc4"], /* merge */ - // This is very different from what git does as it keeps commits together, - // whereas we spread them out breadth-first. - &[ - "0edb95c0c0d9933d88f532ec08fcd405d0eee882", /* c5 */ - "66a309480201c4157b0eae86da69f2d606aadbe7", /* b1c2 */ - "48e8dac19508f4238f06c8de2b10301ce64a641c", /* b2c2 */ - "8cb5f13b66ce52a49399a2c49f537ee2b812369c", /* c4 */ - "80947acb398362d8236fcb8bf0f8a9dac640583f", /* b1c1 */ - "cb6a6befc0a852ac74d74e0354e0f004af29cb79", /* b2c1 */ - "33aa07785dd667c0196064e3be3c51dd9b4744ef", /* c3 */ - "ad33ff2d0c4fc77d56b5fbff6f86f332fe792d83", /* c2 */ - "65d6af66f60b8e39fd1ba6a1423178831e764ec5", /* c1 */ - ], - ) - .check() - } - - #[test] - fn head_date_order() -> crate::Result { - TraversalAssertion::new_at( - "make_repos.sh", - "simple", - &["f49838d84281c3988eeadd988d97dd358c9f9dc4"], /* merge */ - // This is exactly what git shows. - &[ - "0edb95c0c0d9933d88f532ec08fcd405d0eee882", /* c5 */ - "66a309480201c4157b0eae86da69f2d606aadbe7", /* b1c2 */ - "80947acb398362d8236fcb8bf0f8a9dac640583f", /* b1c1 */ - "48e8dac19508f4238f06c8de2b10301ce64a641c", /* b2c2 */ - "cb6a6befc0a852ac74d74e0354e0f004af29cb79", /* b2c1 */ - "8cb5f13b66ce52a49399a2c49f537ee2b812369c", /* c4 */ - "33aa07785dd667c0196064e3be3c51dd9b4744ef", /* c3 */ - "ad33ff2d0c4fc77d56b5fbff6f86f332fe792d83", /* c2 */ - "65d6af66f60b8e39fd1ba6a1423178831e764ec5", /* c1 */ - ], - ) - .with_sorting(Sorting::ByCommitTimeNewestFirst) - .check() - } - } - - /// Same dates are somewhat special as they show how sorting-details on priority queues affects ordering - mod same_date { - use gix_traverse::commit::{Parents, Sorting}; - - use crate::{commit::ancestor::TraversalAssertion, hex_to_id}; - - #[test] - fn c4_breadth_first() -> crate::Result { - TraversalAssertion::new( - "make_traversal_repo_for_commits_same_date.sh", - &["9556057aee5abb06912922e9f26c46386a816822"], /* c4 */ - &[ - "17d78c64cef6c33a10a604573fd2c429e477fd63", /* c3 */ - "9902e3c3e8f0c569b4ab295ddf473e6de763e1e7", /* c2 */ - "134385f6d781b7e97062102c6a483440bfda2a03", /* c1 */ - ], - ) - .check() - } - - #[test] - fn head_breadth_first() -> crate::Result { - TraversalAssertion::new( - "make_traversal_repo_for_commits_same_date.sh", - &["01ec18a3ebf2855708ad3c9d244306bc1fae3e9b"], /* m1b1 */ - // We always take the first parent first, then the second, and so on. - // Deviation: git for some reason displays b1c2 *before* c5, but I think it's better - // to have a strict parent order. - &[ - "efd9a841189668f1bab5b8ebade9cd0a1b139a37", /* c5 */ - "ce2e8ffaa9608a26f7b21afc1db89cadb54fd353", /* b1c2 */ - "9556057aee5abb06912922e9f26c46386a816822", /* c4 */ - "9152eeee2328073cf23dcf8e90c949170b711659", /* b1c1 */ - "17d78c64cef6c33a10a604573fd2c429e477fd63", /* c3 */ - "9902e3c3e8f0c569b4ab295ddf473e6de763e1e7", /* c2 */ - "134385f6d781b7e97062102c6a483440bfda2a03", /* c1 */ - ], - ) - .check() - } - - #[test] - fn head_date_order() -> crate::Result { - TraversalAssertion::new( - "make_traversal_repo_for_commits_same_date.sh", - &["01ec18a3ebf2855708ad3c9d244306bc1fae3e9b"], /* m1b1 */ - &[ - "efd9a841189668f1bab5b8ebade9cd0a1b139a37", /* c5 */ - "ce2e8ffaa9608a26f7b21afc1db89cadb54fd353", /* b1c2 */ - "9556057aee5abb06912922e9f26c46386a816822", /* c4 */ - "9152eeee2328073cf23dcf8e90c949170b711659", /* b1c1 */ - "17d78c64cef6c33a10a604573fd2c429e477fd63", /* c3 */ - "9902e3c3e8f0c569b4ab295ddf473e6de763e1e7", /* c2 */ - "134385f6d781b7e97062102c6a483440bfda2a03", /* c1 */ - ], - ) - .with_sorting(Sorting::ByCommitTimeNewestFirst) - .check() - } - - #[test] - fn head_first_parent_only_breadth_first() -> crate::Result { - TraversalAssertion::new( - "make_traversal_repo_for_commits_same_date.sh", - &["01ec18a3ebf2855708ad3c9d244306bc1fae3e9b"], /* m1b1 */ - &[ - "efd9a841189668f1bab5b8ebade9cd0a1b139a37", /* c5 */ - "9556057aee5abb06912922e9f26c46386a816822", /* c4 */ - "17d78c64cef6c33a10a604573fd2c429e477fd63", /* c3 */ - "9902e3c3e8f0c569b4ab295ddf473e6de763e1e7", /* c2 */ - "134385f6d781b7e97062102c6a483440bfda2a03", /* c1 */ - ], - ) - .with_parents(Parents::First) - .check() - } - - #[test] - fn head_c4_breadth_first() -> crate::Result { - TraversalAssertion::new( - "make_traversal_repo_for_commits_same_date.sh", - &[ - "01ec18a3ebf2855708ad3c9d244306bc1fae3e9b", /* m1b1 */ - "9556057aee5abb06912922e9f26c46386a816822", /* c4 */ - ], - &[ - "efd9a841189668f1bab5b8ebade9cd0a1b139a37", /* c5 */ - "ce2e8ffaa9608a26f7b21afc1db89cadb54fd353", /* b1c2 */ - "17d78c64cef6c33a10a604573fd2c429e477fd63", /* c3 */ - "9152eeee2328073cf23dcf8e90c949170b711659", /* b1c1 */ - "9902e3c3e8f0c569b4ab295ddf473e6de763e1e7", /* c2 */ - "134385f6d781b7e97062102c6a483440bfda2a03", /* c1 */ - ], - ) - .check() - } - - #[test] - fn filtered_commit_does_not_block_ancestors_reachable_from_another_commit() -> crate::Result { - // I don't see a use case for the predicate returning false for a commit but return true for - // at least one of its ancestors, so this test is kind of dubious. But we do want - // `Ancestors` to not eagerly blacklist all of a commit's ancestors when blacklisting that - // one commit, and this test happens to check that. - TraversalAssertion::new( - "make_traversal_repo_for_commits_same_date.sh", - &["01ec18a3ebf2855708ad3c9d244306bc1fae3e9b"], /* m1b1 */ - &[ - "efd9a841189668f1bab5b8ebade9cd0a1b139a37", /* c5 */ - "ce2e8ffaa9608a26f7b21afc1db89cadb54fd353", /* b1c2 */ - "9556057aee5abb06912922e9f26c46386a816822", /* c4 */ - "17d78c64cef6c33a10a604573fd2c429e477fd63", /* c3 */ - "9902e3c3e8f0c569b4ab295ddf473e6de763e1e7", /* c2 */ - "134385f6d781b7e97062102c6a483440bfda2a03", /* c1 */ - ], - ) - .check_with_predicate(|id| id != hex_to_id("9152eeee2328073cf23dcf8e90c949170b711659")) - } - - #[test] - fn predicate_only_called_once_even_if_fork_point() -> crate::Result { - // The `self.seen` check should come before the `self.predicate` check, as we don't know how - // expensive calling `self.predicate` may be. - let mut seen = false; - TraversalAssertion::new( - "make_traversal_repo_for_commits_same_date.sh", - &["01ec18a3ebf2855708ad3c9d244306bc1fae3e9b"], /* m1b1 */ - &[ - "efd9a841189668f1bab5b8ebade9cd0a1b139a37", /* c5 */ - "ce2e8ffaa9608a26f7b21afc1db89cadb54fd353", /* b1c2 */ - "9152eeee2328073cf23dcf8e90c949170b711659", /* b1c1 */ - ], - ) - .check_with_predicate(move |id| { - if id == hex_to_id("9556057aee5abb06912922e9f26c46386a816822") { - assert!(!seen); - seen = true; - false - } else { - true - } - }) - } - } - - /// Some dates adjusted to be a year apart, but still 'c1' and 'c2' with the same date. - mod adjusted_dates { - use gix_traverse::commit::{ancestors, Ancestors, Parents, Sorting}; - - use crate::{commit::ancestor::TraversalAssertion, hex_to_id}; - - #[test] - fn head_breadth_first() -> crate::Result { - TraversalAssertion::new( - "make_traversal_repo_for_commits_with_dates.sh", - &["288e509293165cb5630d08f4185bdf2445bf6170"], /* m1b1 */ - // Here `git` also shows `b1c1` first, making topo-order similar to date order for some reason, - // even though c2 *is* the first parent. - &[ - "9902e3c3e8f0c569b4ab295ddf473e6de763e1e7", /* c2 */ - "bcb05040a6925f2ff5e10d3ae1f9264f2e8c43ac", /* b1c1 */ - "134385f6d781b7e97062102c6a483440bfda2a03", /* c1 */ - ], - ) - .check() - } - - #[test] - fn head_date_order() -> crate::Result { - TraversalAssertion::new( - "make_traversal_repo_for_commits_with_dates.sh", - &["288e509293165cb5630d08f4185bdf2445bf6170"], /* m1b1 */ - &[ - "bcb05040a6925f2ff5e10d3ae1f9264f2e8c43ac", /* b1c1 */ - "9902e3c3e8f0c569b4ab295ddf473e6de763e1e7", /* c2 */ - "134385f6d781b7e97062102c6a483440bfda2a03", /* c1 */ - ], - ) - .with_sorting(Sorting::ByCommitTimeNewestFirst) - .check() - } - - #[test] - fn head_date_order_with_cutoff() -> crate::Result { - TraversalAssertion::new( - "make_traversal_repo_for_commits_with_dates.sh", - &["288e509293165cb5630d08f4185bdf2445bf6170"], /* m1b1 */ - &["bcb05040a6925f2ff5e10d3ae1f9264f2e8c43ac"], /* b1c1 */ - ) - .with_sorting(Sorting::ByCommitTimeNewestFirstCutoffOlderThan { - seconds: 978393600, // =2001-01-02 00:00:00 +0000 - }) - .check() - } - - #[test] - fn date_order_with_cutoff_is_applied_to_starting_position() -> crate::Result { - let dir = - gix_testtools::scripted_fixture_read_only_standalone("make_traversal_repo_for_commits_with_dates.sh")?; - let store = gix_odb::at(dir.join(".git").join("objects"))?; - let iter = Ancestors::new( - Some(hex_to_id("9902e3c3e8f0c569b4ab295ddf473e6de763e1e7" /* c2 */)), - ancestors::State::default(), - &store, - ) - .sorting(Sorting::ByCommitTimeNewestFirstCutoffOlderThan { - seconds: 978393600, // =2001-01-02 00:00:00 +0000 - })?; - assert_eq!( - iter.count(), - 0, - "initial tips that don't pass cutoff value are not returned either" - ); - Ok(()) - } - - #[test] - fn head_date_order_first_parent_only() -> crate::Result { - TraversalAssertion::new( - "make_traversal_repo_for_commits_with_dates.sh", - &["288e509293165cb5630d08f4185bdf2445bf6170"], /* m1b1 */ - &[ - "9902e3c3e8f0c569b4ab295ddf473e6de763e1e7", /* c2 */ - "134385f6d781b7e97062102c6a483440bfda2a03", /* c1 */ - ], - ) - .with_sorting(Sorting::ByCommitTimeNewestFirst) - .with_parents(Parents::First) - .check() - } - } -} +mod simple; +mod topo; diff --git a/gix-traverse/tests/commit/simple.rs b/gix-traverse/tests/commit/simple.rs new file mode 100644 index 00000000000..983941f35de --- /dev/null +++ b/gix-traverse/tests/commit/simple.rs @@ -0,0 +1,422 @@ +use gix_hash::{oid, ObjectId}; +use gix_traverse::commit; + +use crate::hex_to_id; + +struct TraversalAssertion<'a> { + init_script: &'a str, + repo_name: &'a str, + tips: &'a [&'a str], + expected: &'a [&'a str], + mode: commit::Parents, + sorting: commit::simple::Sorting, +} + +impl<'a> TraversalAssertion<'a> { + fn new(init_script: &'a str, tips: &'a [&'a str], expected: &'a [&'a str]) -> Self { + Self::new_at(init_script, "", tips, expected) + } + + fn new_at(init_script: &'a str, repo_name: &'a str, tips: &'a [&'a str], expected: &'a [&'a str]) -> Self { + TraversalAssertion { + init_script, + repo_name, + tips, + expected, + mode: Default::default(), + sorting: Default::default(), + } + } + + fn with_parents(&mut self, mode: commit::Parents) -> &mut Self { + self.mode = mode; + self + } + + fn with_sorting(&mut self, sorting: commit::simple::Sorting) -> &mut Self { + self.sorting = sorting; + self + } +} + +impl TraversalAssertion<'_> { + fn setup(&self) -> crate::Result<(gix_odb::Handle, Vec, Vec)> { + let dir = gix_testtools::scripted_fixture_read_only_standalone(self.init_script)?; + let store = gix_odb::at(dir.join(self.repo_name).join(".git").join("objects"))?; + let tips: Vec<_> = self.tips.iter().copied().map(hex_to_id).collect(); + let expected: Vec = tips + .clone() + .into_iter() + .chain(self.expected.iter().map(|hex_id| hex_to_id(hex_id))) + .collect(); + Ok((store, tips, expected)) + } + + fn setup_commitgraph(&self, store: &gix_odb::Store, use_graph: bool) -> Option { + use_graph + .then(|| gix_commitgraph::at(store.path().join("info"))) + .transpose() + .expect("graph can be loaded if it exists") + } + + fn check_with_predicate(&mut self, predicate: impl FnMut(&oid) -> bool + Clone) -> crate::Result<()> { + let (store, tips, expected) = self.setup()?; + + for use_commitgraph in [false, true] { + let oids = commit::Simple::filtered(tips.clone(), &store, predicate.clone()) + .sorting(self.sorting)? + .parents(self.mode) + .commit_graph(self.setup_commitgraph(store.store_ref(), use_commitgraph)) + .map(|res| res.map(|info| info.id)) + .collect::, _>>()?; + + assert_eq!(oids, expected); + } + Ok(()) + } + + fn check(&self) -> crate::Result { + let (store, tips, expected) = self.setup()?; + + for use_commitgraph in [false, true] { + let oids = commit::Simple::new(tips.clone(), &store) + .sorting(self.sorting)? + .parents(self.mode) + .commit_graph(self.setup_commitgraph(store.store_ref(), use_commitgraph)) + .map(|res| res.map(|info| info.id)) + .collect::, _>>()?; + assert_eq!(oids, expected); + } + Ok(()) + } +} + +mod different_date_intermixed { + use gix_traverse::commit::simple::Sorting; + + use crate::commit::simple::TraversalAssertion; + + #[test] + fn head_breadth_first() -> crate::Result { + TraversalAssertion::new_at( + "make_repos.sh", + "intermixed", + &["58912d92944087dcb09dca79cdd2a937cc158bed"], /* merge */ + // This is very different from what git does as it keeps commits together, + // whereas we spread them out breadth-first. + &[ + "2dce37be587e07caef8c4a5ab60b423b13a8536a", /* c3 */ + "0f6632a5a7d81417488b86692b729e49c1b73056", /* b1c2 */ + "a9c28710e058af4e5163699960234adb9fb2abc7", /* b2c2 */ + "ad33ff2d0c4fc77d56b5fbff6f86f332fe792d83", /* c2 */ + "77fd3c6832c0cd542f7a39f3af9250c3268db979", /* b1c1 */ + "b648f955b930ca95352fae6f22cb593ee0244b27", /* b2c1 */ + "65d6af66f60b8e39fd1ba6a1423178831e764ec5", /* c1 */ + ], + ) + .check() + } + + #[test] + fn head_date_order() -> crate::Result { + TraversalAssertion::new_at( + "make_repos.sh", + "intermixed", + &["58912d92944087dcb09dca79cdd2a937cc158bed"], /* merge */ + // This is exactly what git shows. + &[ + "2dce37be587e07caef8c4a5ab60b423b13a8536a", /* c3 */ + "0f6632a5a7d81417488b86692b729e49c1b73056", /* b1c2 */ + "a9c28710e058af4e5163699960234adb9fb2abc7", /* b2c2 */ + "77fd3c6832c0cd542f7a39f3af9250c3268db979", /* b1c1 */ + "b648f955b930ca95352fae6f22cb593ee0244b27", /* b2c1 */ + "ad33ff2d0c4fc77d56b5fbff6f86f332fe792d83", /* c2 */ + "65d6af66f60b8e39fd1ba6a1423178831e764ec5", /* c1 */ + ], + ) + .with_sorting(Sorting::ByCommitTimeNewestFirst) + .check() + } +} + +mod different_date { + use gix_traverse::commit::simple::Sorting; + + use crate::commit::simple::TraversalAssertion; + + #[test] + fn head_breadth_first() -> crate::Result { + TraversalAssertion::new_at( + "make_repos.sh", + "simple", + &["f49838d84281c3988eeadd988d97dd358c9f9dc4"], /* merge */ + // This is very different from what git does as it keeps commits together, + // whereas we spread them out breadth-first. + &[ + "0edb95c0c0d9933d88f532ec08fcd405d0eee882", /* c5 */ + "66a309480201c4157b0eae86da69f2d606aadbe7", /* b1c2 */ + "48e8dac19508f4238f06c8de2b10301ce64a641c", /* b2c2 */ + "8cb5f13b66ce52a49399a2c49f537ee2b812369c", /* c4 */ + "80947acb398362d8236fcb8bf0f8a9dac640583f", /* b1c1 */ + "cb6a6befc0a852ac74d74e0354e0f004af29cb79", /* b2c1 */ + "33aa07785dd667c0196064e3be3c51dd9b4744ef", /* c3 */ + "ad33ff2d0c4fc77d56b5fbff6f86f332fe792d83", /* c2 */ + "65d6af66f60b8e39fd1ba6a1423178831e764ec5", /* c1 */ + ], + ) + .check() + } + + #[test] + fn head_date_order() -> crate::Result { + TraversalAssertion::new_at( + "make_repos.sh", + "simple", + &["f49838d84281c3988eeadd988d97dd358c9f9dc4"], /* merge */ + // This is exactly what git shows. + &[ + "0edb95c0c0d9933d88f532ec08fcd405d0eee882", /* c5 */ + "66a309480201c4157b0eae86da69f2d606aadbe7", /* b1c2 */ + "80947acb398362d8236fcb8bf0f8a9dac640583f", /* b1c1 */ + "48e8dac19508f4238f06c8de2b10301ce64a641c", /* b2c2 */ + "cb6a6befc0a852ac74d74e0354e0f004af29cb79", /* b2c1 */ + "8cb5f13b66ce52a49399a2c49f537ee2b812369c", /* c4 */ + "33aa07785dd667c0196064e3be3c51dd9b4744ef", /* c3 */ + "ad33ff2d0c4fc77d56b5fbff6f86f332fe792d83", /* c2 */ + "65d6af66f60b8e39fd1ba6a1423178831e764ec5", /* c1 */ + ], + ) + .with_sorting(Sorting::ByCommitTimeNewestFirst) + .check() + } +} + +/// Same dates are somewhat special as they show how sorting-details on priority queues affects ordering +mod same_date { + use gix_traverse::commit::{simple::Sorting, Parents}; + + use crate::{commit::simple::TraversalAssertion, hex_to_id}; + + #[test] + fn c4_breadth_first() -> crate::Result { + TraversalAssertion::new( + "make_traversal_repo_for_commits_same_date.sh", + &["9556057aee5abb06912922e9f26c46386a816822"], /* c4 */ + &[ + "17d78c64cef6c33a10a604573fd2c429e477fd63", /* c3 */ + "9902e3c3e8f0c569b4ab295ddf473e6de763e1e7", /* c2 */ + "134385f6d781b7e97062102c6a483440bfda2a03", /* c1 */ + ], + ) + .check() + } + + #[test] + fn head_breadth_first() -> crate::Result { + TraversalAssertion::new( + "make_traversal_repo_for_commits_same_date.sh", + &["01ec18a3ebf2855708ad3c9d244306bc1fae3e9b"], /* m1b1 */ + // We always take the first parent first, then the second, and so on. + // Deviation: git for some reason displays b1c2 *before* c5, but I think it's better + // to have a strict parent order. + &[ + "efd9a841189668f1bab5b8ebade9cd0a1b139a37", /* c5 */ + "ce2e8ffaa9608a26f7b21afc1db89cadb54fd353", /* b1c2 */ + "9556057aee5abb06912922e9f26c46386a816822", /* c4 */ + "9152eeee2328073cf23dcf8e90c949170b711659", /* b1c1 */ + "17d78c64cef6c33a10a604573fd2c429e477fd63", /* c3 */ + "9902e3c3e8f0c569b4ab295ddf473e6de763e1e7", /* c2 */ + "134385f6d781b7e97062102c6a483440bfda2a03", /* c1 */ + ], + ) + .check() + } + + #[test] + fn head_date_order() -> crate::Result { + TraversalAssertion::new( + "make_traversal_repo_for_commits_same_date.sh", + &["01ec18a3ebf2855708ad3c9d244306bc1fae3e9b"], /* m1b1 */ + &[ + "efd9a841189668f1bab5b8ebade9cd0a1b139a37", /* c5 */ + "ce2e8ffaa9608a26f7b21afc1db89cadb54fd353", /* b1c2 */ + "9556057aee5abb06912922e9f26c46386a816822", /* c4 */ + "9152eeee2328073cf23dcf8e90c949170b711659", /* b1c1 */ + "17d78c64cef6c33a10a604573fd2c429e477fd63", /* c3 */ + "9902e3c3e8f0c569b4ab295ddf473e6de763e1e7", /* c2 */ + "134385f6d781b7e97062102c6a483440bfda2a03", /* c1 */ + ], + ) + .with_sorting(Sorting::ByCommitTimeNewestFirst) + .check() + } + + #[test] + fn head_first_parent_only_breadth_first() -> crate::Result { + TraversalAssertion::new( + "make_traversal_repo_for_commits_same_date.sh", + &["01ec18a3ebf2855708ad3c9d244306bc1fae3e9b"], /* m1b1 */ + &[ + "efd9a841189668f1bab5b8ebade9cd0a1b139a37", /* c5 */ + "9556057aee5abb06912922e9f26c46386a816822", /* c4 */ + "17d78c64cef6c33a10a604573fd2c429e477fd63", /* c3 */ + "9902e3c3e8f0c569b4ab295ddf473e6de763e1e7", /* c2 */ + "134385f6d781b7e97062102c6a483440bfda2a03", /* c1 */ + ], + ) + .with_parents(Parents::First) + .check() + } + + #[test] + fn head_c4_breadth_first() -> crate::Result { + TraversalAssertion::new( + "make_traversal_repo_for_commits_same_date.sh", + &[ + "01ec18a3ebf2855708ad3c9d244306bc1fae3e9b", /* m1b1 */ + "9556057aee5abb06912922e9f26c46386a816822", /* c4 */ + ], + &[ + "efd9a841189668f1bab5b8ebade9cd0a1b139a37", /* c5 */ + "ce2e8ffaa9608a26f7b21afc1db89cadb54fd353", /* b1c2 */ + "17d78c64cef6c33a10a604573fd2c429e477fd63", /* c3 */ + "9152eeee2328073cf23dcf8e90c949170b711659", /* b1c1 */ + "9902e3c3e8f0c569b4ab295ddf473e6de763e1e7", /* c2 */ + "134385f6d781b7e97062102c6a483440bfda2a03", /* c1 */ + ], + ) + .check() + } + + #[test] + fn filtered_commit_does_not_block_ancestors_reachable_from_another_commit() -> crate::Result { + // I don't see a use case for the predicate returning false for a commit but return true for + // at least one of its ancestors, so this test is kind of dubious. But we do want + // `Ancestors` to not eagerly blacklist all of a commit's ancestors when blacklisting that + // one commit, and this test happens to check that. + TraversalAssertion::new( + "make_traversal_repo_for_commits_same_date.sh", + &["01ec18a3ebf2855708ad3c9d244306bc1fae3e9b"], /* m1b1 */ + &[ + "efd9a841189668f1bab5b8ebade9cd0a1b139a37", /* c5 */ + "ce2e8ffaa9608a26f7b21afc1db89cadb54fd353", /* b1c2 */ + "9556057aee5abb06912922e9f26c46386a816822", /* c4 */ + "17d78c64cef6c33a10a604573fd2c429e477fd63", /* c3 */ + "9902e3c3e8f0c569b4ab295ddf473e6de763e1e7", /* c2 */ + "134385f6d781b7e97062102c6a483440bfda2a03", /* c1 */ + ], + ) + .check_with_predicate(|id| id != hex_to_id("9152eeee2328073cf23dcf8e90c949170b711659")) + } + + #[test] + fn predicate_only_called_once_even_if_fork_point() -> crate::Result { + // The `self.seen` check should come before the `self.predicate` check, as we don't know how + // expensive calling `self.predicate` may be. + let mut seen = false; + TraversalAssertion::new( + "make_traversal_repo_for_commits_same_date.sh", + &["01ec18a3ebf2855708ad3c9d244306bc1fae3e9b"], /* m1b1 */ + &[ + "efd9a841189668f1bab5b8ebade9cd0a1b139a37", /* c5 */ + "ce2e8ffaa9608a26f7b21afc1db89cadb54fd353", /* b1c2 */ + "9152eeee2328073cf23dcf8e90c949170b711659", /* b1c1 */ + ], + ) + .check_with_predicate(move |id| { + if id == hex_to_id("9556057aee5abb06912922e9f26c46386a816822") { + assert!(!seen); + seen = true; + false + } else { + true + } + }) + } +} + +/// Some dates adjusted to be a year apart, but still 'c1' and 'c2' with the same date. +mod adjusted_dates { + use gix_traverse::commit::{simple::Sorting, Parents, Simple}; + + use crate::{commit::simple::TraversalAssertion, hex_to_id}; + + #[test] + fn head_breadth_first() -> crate::Result { + TraversalAssertion::new( + "make_traversal_repo_for_commits_with_dates.sh", + &["288e509293165cb5630d08f4185bdf2445bf6170"], /* m1b1 */ + // Here `git` also shows `b1c1` first, making topo-order similar to date order for some reason, + // even though c2 *is* the first parent. + &[ + "9902e3c3e8f0c569b4ab295ddf473e6de763e1e7", /* c2 */ + "bcb05040a6925f2ff5e10d3ae1f9264f2e8c43ac", /* b1c1 */ + "134385f6d781b7e97062102c6a483440bfda2a03", /* c1 */ + ], + ) + .check() + } + + #[test] + fn head_date_order() -> crate::Result { + TraversalAssertion::new( + "make_traversal_repo_for_commits_with_dates.sh", + &["288e509293165cb5630d08f4185bdf2445bf6170"], /* m1b1 */ + &[ + "bcb05040a6925f2ff5e10d3ae1f9264f2e8c43ac", /* b1c1 */ + "9902e3c3e8f0c569b4ab295ddf473e6de763e1e7", /* c2 */ + "134385f6d781b7e97062102c6a483440bfda2a03", /* c1 */ + ], + ) + .with_sorting(Sorting::ByCommitTimeNewestFirst) + .check() + } + + #[test] + fn head_date_order_with_cutoff() -> crate::Result { + TraversalAssertion::new( + "make_traversal_repo_for_commits_with_dates.sh", + &["288e509293165cb5630d08f4185bdf2445bf6170"], /* m1b1 */ + &["bcb05040a6925f2ff5e10d3ae1f9264f2e8c43ac"], /* b1c1 */ + ) + .with_sorting(Sorting::ByCommitTimeNewestFirstCutoffOlderThan { + seconds: 978393600, // =2001-01-02 00:00:00 +0000 + }) + .check() + } + + #[test] + fn date_order_with_cutoff_is_applied_to_starting_position() -> crate::Result { + let dir = + gix_testtools::scripted_fixture_read_only_standalone("make_traversal_repo_for_commits_with_dates.sh")?; + let store = gix_odb::at(dir.join(".git").join("objects"))?; + let iter = Simple::new( + Some(hex_to_id("9902e3c3e8f0c569b4ab295ddf473e6de763e1e7" /* c2 */)), + &store, + ) + .sorting(Sorting::ByCommitTimeNewestFirstCutoffOlderThan { + seconds: 978393600, // =2001-01-02 00:00:00 +0000 + })?; + assert_eq!( + iter.count(), + 0, + "initial tips that don't pass cutoff value are not returned either" + ); + Ok(()) + } + + #[test] + fn head_date_order_first_parent_only() -> crate::Result { + TraversalAssertion::new( + "make_traversal_repo_for_commits_with_dates.sh", + &["288e509293165cb5630d08f4185bdf2445bf6170"], /* m1b1 */ + &[ + "9902e3c3e8f0c569b4ab295ddf473e6de763e1e7", /* c2 */ + "134385f6d781b7e97062102c6a483440bfda2a03", /* c1 */ + ], + ) + .with_sorting(Sorting::ByCommitTimeNewestFirst) + .with_parents(Parents::First) + .check() + } +} diff --git a/gix-traverse/tests/commit/topo.rs b/gix-traverse/tests/commit/topo.rs new file mode 100644 index 00000000000..4486a53023f --- /dev/null +++ b/gix-traverse/tests/commit/topo.rs @@ -0,0 +1,373 @@ +use gix_hash::{oid, ObjectId}; +use gix_object::bstr::ByteSlice; +use gix_traverse::commit::{topo, Parents}; +use std::path::PathBuf; + +use crate::hex_to_id; + +struct TraversalAssertion<'a> { + init_script: &'a str, + worktree_dir: PathBuf, + repo_name: &'a str, + tips: &'a [&'a str], + ends: &'a [&'a str], + expected: &'a [&'a str], + mode: Parents, + sorting: topo::Sorting, +} + +/// API +impl<'a> TraversalAssertion<'a> { + fn new(tips: &'a [&'a str], ends: &'a [&'a str], expected: &'a [&'a str]) -> Self { + Self::new_at("make_repo_for_topo.sh", "", tips, ends, expected) + } + + fn new_at( + init_script: &'a str, + repo_name: &'a str, + tips: &'a [&'a str], + ends: &'a [&'a str], + expected: &'a [&'a str], + ) -> Self { + TraversalAssertion { + init_script, + worktree_dir: Default::default(), + repo_name, + tips, + ends, + expected, + mode: Default::default(), + sorting: Default::default(), + } + } + + fn with_parents(&mut self, mode: Parents) -> &mut Self { + self.mode = mode; + self + } + + fn with_sorting(&mut self, sorting: topo::Sorting) -> &mut Self { + self.sorting = sorting; + self + } + + fn check_with_predicate(&mut self, predicate: impl FnMut(&oid) -> bool + Clone) -> crate::Result<()> { + let (store, tips, ends, expected) = self.setup()?; + + for use_commitgraph in [false, true] { + let oids = topo::Builder::from_iters(&store, tips.iter().copied(), Some(ends.iter().copied())) + .sorting(self.sorting) + .with_commit_graph(self.setup_commitgraph(store.store_ref(), use_commitgraph)) + .parents(self.mode) + .with_predicate(predicate.clone()) + .build()? + .map(|res| res.map(|info| info.id)) + .collect::, _>>()?; + + assert_eq!(oids, expected); + } + Ok(()) + } + + fn assert_baseline(&self, name: &str) { + let buf = std::fs::read(self.worktree_dir.join(format!("{name}.baseline"))) + .expect("a baseline must be set for each repo"); + let expected: Vec<_> = buf.lines().map(|s| s.to_str().unwrap()).collect(); + assert_eq!( + self.expected, expected, + "Baseline must match the expectation we provide here" + ); + } + + fn check(&mut self) -> crate::Result { + let (store, tips, ends, expected) = self.setup()?; + + for use_commitgraph in [false, true] { + let oids = topo::Builder::from_iters(&store, tips.iter().copied(), Some(ends.iter().copied())) + .sorting(self.sorting) + .with_commit_graph(self.setup_commitgraph(store.store_ref(), use_commitgraph)) + .parents(self.mode) + .build()? + .map(|res| res.map(|info| info.id)) + .collect::, _>>()?; + + assert_eq!(oids, expected); + } + Ok(()) + } +} + +impl TraversalAssertion<'_> { + #[allow(clippy::type_complexity)] + fn setup(&mut self) -> crate::Result<(gix_odb::Handle, Vec, Vec, Vec)> { + let dir = gix_testtools::scripted_fixture_read_only_standalone(self.init_script)?; + let worktree_dir = dir.join(self.repo_name); + let store = gix_odb::at(worktree_dir.join(".git").join("objects"))?; + self.worktree_dir = worktree_dir; + + let tips: Vec<_> = self.tips.iter().copied().map(hex_to_id).collect(); + let ends: Vec<_> = self.ends.iter().copied().map(hex_to_id).collect(); + // `tips` is not chained with expected unlike in `commit`'s + // TraversalAssertion since it's not given that all the tips are + // shown first. + let expected: Vec = self.expected.iter().map(|hex_id| hex_to_id(hex_id)).collect(); + + Ok((store, tips, ends, expected)) + } + + fn setup_commitgraph(&self, store: &gix_odb::Store, use_graph: bool) -> Option { + use_graph + .then(|| gix_commitgraph::at(store.path().join("info"))) + .transpose() + .expect("graph can be loaded if it exists") + } +} + +mod basic { + use gix_traverse::commit::topo; + + use super::TraversalAssertion; + + use crate::hex_to_id; + + #[test] + fn simple() -> crate::Result { + let mut assertion = TraversalAssertion::new( + &["62ed296d9986f50477e9f7b7e81cd0258939a43d"], + &[], + &[ + "62ed296d9986f50477e9f7b7e81cd0258939a43d", + "722bf6b8c3d9e3a11fa5100a02ed9b140e1d209c", + "3be0c4c793c634c8fd95054345d4935d10a0879a", + "2083b02a78e88b747e305b6ed3d5a861cf9fb73f", + "302a5d0530ec688c241f32c2f2b61b964dd17bee", + "d09384f312b03e4a1413160739805ff25e8fe99d", + "22fbc169eeca3c9678fc7028aa80fad5ef49019f", + "eeab3243aad67bc838fc4425f759453bf0b47785", + "693c775700cf90bd158ee6e7f14dd1b7bd83a4ce", + "33eb18340e4eaae3e3dcf80222b02f161cd3f966", + "1a27cb1a26c9faed9f0d1975326fe51123ab01ed", + "f1cce1b5c7efcdfa106e95caa6c45a2cae48a481", + "945d8a360915631ad545e0cf04630d86d3d4eaa1", + "a863c02247a6c5ba32dff5224459f52aa7f77f7b", + "2f291881edfb0597493a52d26ea09dd7340ce507", + "9c46b8765703273feb10a2ebd810e70b8e2ca44a", + "fb3e21cf45b04b617011d2b30973f3e5ce60d0cd", + ], + ); + assertion.with_sorting(topo::Sorting::TopoOrder).check()?; + assertion.assert_baseline("all-commits"); + Ok(()) + } + + #[test] + fn one_end() -> crate::Result { + TraversalAssertion::new( + &["62ed296d9986f50477e9f7b7e81cd0258939a43d"], + &["f1cce1b5c7efcdfa106e95caa6c45a2cae48a481"], + &[ + "62ed296d9986f50477e9f7b7e81cd0258939a43d", + "722bf6b8c3d9e3a11fa5100a02ed9b140e1d209c", + "3be0c4c793c634c8fd95054345d4935d10a0879a", + "2083b02a78e88b747e305b6ed3d5a861cf9fb73f", + "302a5d0530ec688c241f32c2f2b61b964dd17bee", + "d09384f312b03e4a1413160739805ff25e8fe99d", + "22fbc169eeca3c9678fc7028aa80fad5ef49019f", + "eeab3243aad67bc838fc4425f759453bf0b47785", + "693c775700cf90bd158ee6e7f14dd1b7bd83a4ce", + "33eb18340e4eaae3e3dcf80222b02f161cd3f966", + "1a27cb1a26c9faed9f0d1975326fe51123ab01ed", + ], + ) + .with_sorting(topo::Sorting::TopoOrder) + .check() + } + + #[test] + fn empty_range() -> crate::Result { + TraversalAssertion::new( + &["f1cce1b5c7efcdfa106e95caa6c45a2cae48a481"], + &["eeab3243aad67bc838fc4425f759453bf0b47785"], + &[], + ) + .with_sorting(topo::Sorting::TopoOrder) + .check() + } + + #[test] + fn two_tips_two_ends() -> crate::Result { + TraversalAssertion::new( + &[ + "d09384f312b03e4a1413160739805ff25e8fe99d", + "3be0c4c793c634c8fd95054345d4935d10a0879a", + ], + &[ + "1a27cb1a26c9faed9f0d1975326fe51123ab01ed", + "22fbc169eeca3c9678fc7028aa80fad5ef49019f", + ], + &[ + "3be0c4c793c634c8fd95054345d4935d10a0879a", + "2083b02a78e88b747e305b6ed3d5a861cf9fb73f", + "302a5d0530ec688c241f32c2f2b61b964dd17bee", + "d09384f312b03e4a1413160739805ff25e8fe99d", + "eeab3243aad67bc838fc4425f759453bf0b47785", + "693c775700cf90bd158ee6e7f14dd1b7bd83a4ce", + "33eb18340e4eaae3e3dcf80222b02f161cd3f966", + ], + ) + .with_sorting(topo::Sorting::TopoOrder) + .check() + } + + #[test] + fn with_dummy_predicate() -> crate::Result { + TraversalAssertion::new( + &["62ed296d9986f50477e9f7b7e81cd0258939a43d"], + &[], + &[ + "62ed296d9986f50477e9f7b7e81cd0258939a43d", + "722bf6b8c3d9e3a11fa5100a02ed9b140e1d209c", + "3be0c4c793c634c8fd95054345d4935d10a0879a", + "2083b02a78e88b747e305b6ed3d5a861cf9fb73f", + "302a5d0530ec688c241f32c2f2b61b964dd17bee", + "d09384f312b03e4a1413160739805ff25e8fe99d", + "22fbc169eeca3c9678fc7028aa80fad5ef49019f", + "693c775700cf90bd158ee6e7f14dd1b7bd83a4ce", + "33eb18340e4eaae3e3dcf80222b02f161cd3f966", + "1a27cb1a26c9faed9f0d1975326fe51123ab01ed", + "f1cce1b5c7efcdfa106e95caa6c45a2cae48a481", + "945d8a360915631ad545e0cf04630d86d3d4eaa1", + "a863c02247a6c5ba32dff5224459f52aa7f77f7b", + "2f291881edfb0597493a52d26ea09dd7340ce507", + "9c46b8765703273feb10a2ebd810e70b8e2ca44a", + "fb3e21cf45b04b617011d2b30973f3e5ce60d0cd", + ], + ) + .with_sorting(topo::Sorting::TopoOrder) + .check_with_predicate(|oid| oid != hex_to_id("eeab3243aad67bc838fc4425f759453bf0b47785")) + } + + #[test] + fn end_along_first_parent() -> crate::Result { + TraversalAssertion::new( + &["d09384f312b03e4a1413160739805ff25e8fe99d"], + &["33eb18340e4eaae3e3dcf80222b02f161cd3f966"], + &[ + "d09384f312b03e4a1413160739805ff25e8fe99d", + "22fbc169eeca3c9678fc7028aa80fad5ef49019f", + "eeab3243aad67bc838fc4425f759453bf0b47785", + "693c775700cf90bd158ee6e7f14dd1b7bd83a4ce", + ], + ) + .with_sorting(topo::Sorting::TopoOrder) + .check() + } +} + +mod first_parent { + use gix_traverse::commit::{topo, Parents}; + + use super::TraversalAssertion; + + #[test] + fn basic() -> crate::Result { + let mut assertion = TraversalAssertion::new( + &["62ed296d9986f50477e9f7b7e81cd0258939a43d"], + &[], + &[ + "62ed296d9986f50477e9f7b7e81cd0258939a43d", + "722bf6b8c3d9e3a11fa5100a02ed9b140e1d209c", + "d09384f312b03e4a1413160739805ff25e8fe99d", + "eeab3243aad67bc838fc4425f759453bf0b47785", + "693c775700cf90bd158ee6e7f14dd1b7bd83a4ce", + "33eb18340e4eaae3e3dcf80222b02f161cd3f966", + "1a27cb1a26c9faed9f0d1975326fe51123ab01ed", + "f1cce1b5c7efcdfa106e95caa6c45a2cae48a481", + "945d8a360915631ad545e0cf04630d86d3d4eaa1", + "a863c02247a6c5ba32dff5224459f52aa7f77f7b", + "2f291881edfb0597493a52d26ea09dd7340ce507", + "9c46b8765703273feb10a2ebd810e70b8e2ca44a", + "fb3e21cf45b04b617011d2b30973f3e5ce60d0cd", + ], + ); + assertion + .with_parents(Parents::First) + .with_sorting(topo::Sorting::TopoOrder) + .check()?; + + assertion.assert_baseline("first-parent"); + Ok(()) + } + + #[test] + fn with_end() -> crate::Result { + TraversalAssertion::new( + &["62ed296d9986f50477e9f7b7e81cd0258939a43d"], + &["f1cce1b5c7efcdfa106e95caa6c45a2cae48a481"], + &[ + "62ed296d9986f50477e9f7b7e81cd0258939a43d", + "722bf6b8c3d9e3a11fa5100a02ed9b140e1d209c", + "d09384f312b03e4a1413160739805ff25e8fe99d", + "eeab3243aad67bc838fc4425f759453bf0b47785", + "693c775700cf90bd158ee6e7f14dd1b7bd83a4ce", + "33eb18340e4eaae3e3dcf80222b02f161cd3f966", + "1a27cb1a26c9faed9f0d1975326fe51123ab01ed", + ], + ) + .with_parents(Parents::First) + .with_sorting(topo::Sorting::TopoOrder) + .check() + } + + #[test] + fn end_is_second_parent() -> crate::Result { + TraversalAssertion::new( + &["62ed296d9986f50477e9f7b7e81cd0258939a43d"], + &["3be0c4c793c634c8fd95054345d4935d10a0879a"], + &[ + "62ed296d9986f50477e9f7b7e81cd0258939a43d", + "722bf6b8c3d9e3a11fa5100a02ed9b140e1d209c", + "d09384f312b03e4a1413160739805ff25e8fe99d", + "eeab3243aad67bc838fc4425f759453bf0b47785", + "693c775700cf90bd158ee6e7f14dd1b7bd83a4ce", + "33eb18340e4eaae3e3dcf80222b02f161cd3f966", + "1a27cb1a26c9faed9f0d1975326fe51123ab01ed", + ], + ) + .with_parents(Parents::First) + .with_sorting(topo::Sorting::TopoOrder) + .check() + } +} + +mod date_order { + use gix_traverse::commit::topo; + + use super::TraversalAssertion; + + #[test] + fn with_ends() -> crate::Result { + let mut assertion = TraversalAssertion::new( + // Same tip and end as basic::one_end() but the order should be + // different. + &["62ed296d9986f50477e9f7b7e81cd0258939a43d"], + &["f1cce1b5c7efcdfa106e95caa6c45a2cae48a481"], + &[ + "62ed296d9986f50477e9f7b7e81cd0258939a43d", + "722bf6b8c3d9e3a11fa5100a02ed9b140e1d209c", + "3be0c4c793c634c8fd95054345d4935d10a0879a", + "2083b02a78e88b747e305b6ed3d5a861cf9fb73f", + "302a5d0530ec688c241f32c2f2b61b964dd17bee", + "d09384f312b03e4a1413160739805ff25e8fe99d", + "eeab3243aad67bc838fc4425f759453bf0b47785", + "22fbc169eeca3c9678fc7028aa80fad5ef49019f", + "693c775700cf90bd158ee6e7f14dd1b7bd83a4ce", + "33eb18340e4eaae3e3dcf80222b02f161cd3f966", + "1a27cb1a26c9faed9f0d1975326fe51123ab01ed", + ], + ); + assertion.with_sorting(topo::Sorting::DateOrder).check()?; + assertion.assert_baseline("date-order"); + Ok(()) + } +} diff --git a/gix-traverse/tests/fixtures/generated-archives/make_repo_for_topo.tar.xz b/gix-traverse/tests/fixtures/generated-archives/make_repo_for_topo.tar.xz new file mode 100644 index 00000000000..0eef69ea66b Binary files /dev/null and b/gix-traverse/tests/fixtures/generated-archives/make_repo_for_topo.tar.xz differ diff --git a/gix-traverse/tests/fixtures/generated-archives/make_repos.tar.xz b/gix-traverse/tests/fixtures/generated-archives/make_repos.tar.xz index e249ab76a8d..78cb114dd15 100644 Binary files a/gix-traverse/tests/fixtures/generated-archives/make_repos.tar.xz and b/gix-traverse/tests/fixtures/generated-archives/make_repos.tar.xz differ diff --git a/gix-traverse/tests/fixtures/generated-archives/make_traversal_repo_for_commits_same_date.tar.xz b/gix-traverse/tests/fixtures/generated-archives/make_traversal_repo_for_commits_same_date.tar.xz index dc3c16ccd79..56e225f21a3 100644 Binary files a/gix-traverse/tests/fixtures/generated-archives/make_traversal_repo_for_commits_same_date.tar.xz and b/gix-traverse/tests/fixtures/generated-archives/make_traversal_repo_for_commits_same_date.tar.xz differ diff --git a/gix-traverse/tests/fixtures/generated-archives/make_traversal_repo_for_commits_with_dates.tar.xz b/gix-traverse/tests/fixtures/generated-archives/make_traversal_repo_for_commits_with_dates.tar.xz index 2829202cb3b..b70153746a0 100644 Binary files a/gix-traverse/tests/fixtures/generated-archives/make_traversal_repo_for_commits_with_dates.tar.xz and b/gix-traverse/tests/fixtures/generated-archives/make_traversal_repo_for_commits_with_dates.tar.xz differ diff --git a/gix-traverse/tests/fixtures/generated-archives/make_traversal_repo_for_trees.tar.xz b/gix-traverse/tests/fixtures/generated-archives/make_traversal_repo_for_trees.tar.xz index 1f4c631ac35..95689b4c983 100644 Binary files a/gix-traverse/tests/fixtures/generated-archives/make_traversal_repo_for_trees.tar.xz and b/gix-traverse/tests/fixtures/generated-archives/make_traversal_repo_for_trees.tar.xz differ diff --git a/gix-traverse/tests/fixtures/make_repo_for_topo.sh b/gix-traverse/tests/fixtures/make_repo_for_topo.sh new file mode 100755 index 00000000000..da05d56e485 --- /dev/null +++ b/gix-traverse/tests/fixtures/make_repo_for_topo.sh @@ -0,0 +1,67 @@ +#!/usr/bin/env bash +set -eu -o pipefail + +function tick () { + if test -z "${tick+set}" + then + tick=1112911993 + else + tick=$(($tick + 60)) + fi + GIT_COMMITTER_DATE="$tick -0700" + GIT_AUTHOR_DATE="$tick -0700" + export GIT_COMMITTER_DATE GIT_AUTHOR_DATE +} + +tick +function commit() { + local message=${1:?first argument is the commit message} + tick + git commit --allow-empty -m "$message" +} + +function optimize() { + git commit-graph write --no-progress --reachable + git repack -adq +} + +function collect_baselines() { + git rev-list --topo-order HEAD > all-commits.baseline + git rev-list --topo-order --first-parent HEAD > first-parent.baseline + git rev-list --date-order ^f1cce1b5c7efcdfa106e95caa6c45a2cae48a481 HEAD > date-order.baseline +} + +git init +git config merge.ff false + +git checkout -q -b main +for i in {0..5}; do + commit c$i +done + +git branch branch1 +for i in {6..8}; do + commit c$i +done + +git checkout -q branch1 +commit b1c1 + +git checkout -q main +commit c9 + +git merge branch1 -m merge + +git checkout -q branch1 +commit c10 +commit c11 + +git checkout -q branch1 +commit b1c2 + +git checkout -q main +git merge branch1 -m merge +commit c12 + +optimize +collect_baselines diff --git a/gix-traverse/tests/fixtures/make_repos.sh b/gix-traverse/tests/fixtures/make_repos.sh index ec35266c2e5..c7d29e06b8a 100755 --- a/gix-traverse/tests/fixtures/make_repos.sh +++ b/gix-traverse/tests/fixtures/make_repos.sh @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash set -eu -o pipefail function tick () { diff --git a/gix-traverse/tests/fixtures/make_traversal_repo_for_commits_same_date.sh b/gix-traverse/tests/fixtures/make_traversal_repo_for_commits_same_date.sh index d9e6e60e601..f952cc12a24 100755 --- a/gix-traverse/tests/fixtures/make_traversal_repo_for_commits_same_date.sh +++ b/gix-traverse/tests/fixtures/make_traversal_repo_for_commits_same_date.sh @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash set -eu -o pipefail # all commits have the same date as it's set by `gix-testtools` to a fixed value. diff --git a/gix-traverse/tests/fixtures/make_traversal_repo_for_commits_with_dates.sh b/gix-traverse/tests/fixtures/make_traversal_repo_for_commits_with_dates.sh index f80a769f43d..96b6497f5a1 100755 --- a/gix-traverse/tests/fixtures/make_traversal_repo_for_commits_with_dates.sh +++ b/gix-traverse/tests/fixtures/make_traversal_repo_for_commits_with_dates.sh @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash set -eu -o pipefail git init -q diff --git a/gix-traverse/tests/fixtures/make_traversal_repo_for_trees.sh b/gix-traverse/tests/fixtures/make_traversal_repo_for_trees.sh index 263ecf27f47..90b4895bb6a 100755 --- a/gix-traverse/tests/fixtures/make_traversal_repo_for_trees.sh +++ b/gix-traverse/tests/fixtures/make_traversal_repo_for_trees.sh @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash set -eu -o pipefail git init -q diff --git a/gix-url/CHANGELOG.md b/gix-url/CHANGELOG.md index a255ad3b304..5adba3f646b 100644 --- a/gix-url/CHANGELOG.md +++ b/gix-url/CHANGELOG.md @@ -5,6 +5,107 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## 0.27.3 (2024-04-13) + +### New Features + + + + - Add `ArgumentSafety` and `Url::*_as_argument()` methods + This adds three methods to `Url`: + + - `Url::user_as_argument` +- `Url::host_as_argument` +- `Url::path_as_argument` +1. There is no wrapped value (`Absent`). +2. The wrapped value is usable and presumably safe (`Usable`). +3. The wrapped value is dangerous and should not be passed as a + command-line argument because it could be interpreted as an + option due to starting with `-`. The value itself may still be + useful and safe to include in error messages. + +### Bug Fixes + + - Prevent usernames with leading `-` from being passed to SSH + This detects ambiguous usernames in dangerous cases where they + would be passed to external commands to form SSH connections, if + they would be misinterpreted as option arguments. + + This change is analogous to b06a0dd, hardening `gix-transport` and + applications that use it against options smuggled in URLs, but for + the non-mandatory username portion of a URL, rather than the host + and path portions that were covered there. + + For example, commands like these no longer pass `-F...` options to + `ssh`: + + gix clone 'ssh://-Fconfigfile@example.com/abc' + gix clone -- '-Fconfigfile@example.com:abc/def' + + Instead, they refuse to run `ssh`, producing the error: + + Error: Username '-Fconfigfile' could be mistaken for a command-line argument + +### Commit Statistics + + + + - 13 commits contributed to the release over the course of 1 calendar day. + - 29 days passed between releases. + - 3 commits were understood as [conventional](https://www.conventionalcommits.org). + - 0 issues like '(#ID)' were seen in commit messages + +### Commit Details + + + +
view details + + * **Uncategorized** + - Prepare changelogs prior to release ([`5755271`](https://github.com/Byron/gitoxide/commit/57552717f46f96c35ba4ddc0a64434354ef845e9)) + - Merge branch 'strange-usernames' ([`1272542`](https://github.com/Byron/gitoxide/commit/1272542e79c29302ada47324d6bb02101393563d)) + - Refactor `gix-url` ([`09311b0`](https://github.com/Byron/gitoxide/commit/09311b0e0039c5a82c871047ff24336ab1741d47)) + - Give `ArgumentSafety` traits; test `*_as_argument` methods ([`1b0af07`](https://github.com/Byron/gitoxide/commit/1b0af07ffa122a2199d444b84512300ef578abb5)) + - Add `ArgumentSafety` and `Url::*_as_argument()` methods ([`5457998`](https://github.com/Byron/gitoxide/commit/545799882643532dd0b3e8ab436efd7722c74e3c)) + - Sketch *_as_argument methods and supporting enum ([`29941e6`](https://github.com/Byron/gitoxide/commit/29941e66b367ff1eac448ec4024b1a17246ffaac)) + - Give `looks_like_argument` a more accurate name ([`beef8d2`](https://github.com/Byron/gitoxide/commit/beef8d2ce20af8b97fcd85c185cf9373d4897cdb)) + - Prevent usernames with leading `-` from being passed to SSH ([`f56ad39`](https://github.com/Byron/gitoxide/commit/f56ad390a5569d0129b7b16632991d18b9ddb4f7)) + - Clarify circumstances when *_argument_safe should be used ([`e8e2463`](https://github.com/Byron/gitoxide/commit/e8e2463ad1e2c3b9f604b744d554cfa6684eca2f)) + - Expand and introduce tests of more combinations ([`169a698`](https://github.com/Byron/gitoxide/commit/169a698ce82df559253c0389a6fa6921a136bc9e)) + - Add `Url::user_argument_safe()` ([`db40382`](https://github.com/Byron/gitoxide/commit/db40382328c373258aa3bd5f9551511a42af6be5)) + - Prepare for adding Url::user_argument_safe ([`1bdfdd9`](https://github.com/Byron/gitoxide/commit/1bdfdd98c9a82eee7ac40b38fe05d54f1f93237e)) + - Reorder tests and add username assertion placeholders ([`6cbe65d`](https://github.com/Byron/gitoxide/commit/6cbe65d3f25c5c00fb9248a5558ac74f6e77ff06)) +
+ + +They return ArgumentSafety values that distingiush three cases:user_as_argument and host_as_argument are the most useful ones,as they serve as alternatives to user_argument_safe andhost_argument_safe whose return values are unambiguous. Theuser_argument_safe and host_argument_safe methods don’tdistinguish between the absence of a username or host, and thepresence of a username or host that is unsafe to pass as anargument.path_as_argument included for parity, in case it is useful towrite code that handles the three cases similarly. However, thereis no underlying ambiguity in the return value of the correspondingpath_argument_safe method, since unlike user and host, thepath is not an option type. Add Url::user_argument_safe()This returns None if the username begins with a -, which wouldconfuse command-line applications.It is analogous to the Url::host_argument_safe() andUrl::path_argument_safe() methods (introduced in d80b5f6), butfor usernames rather than hosts or paths. + +## 0.27.2 (2024-03-14) + +A maintenance release without user-facing changes. + +### Commit Statistics + + + + - 4 commits contributed to the release over the course of 4 calendar days. + - 18 days passed between releases. + - 0 commits were understood as [conventional](https://www.conventionalcommits.org). + - 0 issues like '(#ID)' were seen in commit messages + +### Commit Details + + + +
view details + + * **Uncategorized** + - Release gix-date v0.8.5, gix-hash v0.14.2, gix-trace v0.1.8, gix-utils v0.1.11, gix-features v0.38.1, gix-actor v0.31.0, gix-validate v0.8.4, gix-object v0.42.0, gix-path v0.10.7, gix-glob v0.16.2, gix-quote v0.4.12, gix-attributes v0.22.2, gix-command v0.3.6, gix-filter v0.11.0, gix-fs v0.10.1, gix-chunk v0.4.8, gix-commitgraph v0.24.2, gix-hashtable v0.5.2, gix-revwalk v0.13.0, gix-traverse v0.38.0, gix-worktree-stream v0.11.0, gix-archive v0.11.0, gix-config-value v0.14.6, gix-tempfile v13.1.1, gix-lock v13.1.1, gix-ref v0.43.0, gix-sec v0.10.6, gix-config v0.36.0, gix-prompt v0.8.4, gix-url v0.27.2, gix-credentials v0.24.2, gix-ignore v0.11.2, gix-bitmap v0.2.11, gix-index v0.31.0, gix-worktree v0.32.0, gix-diff v0.42.0, gix-discover v0.31.0, gix-pathspec v0.7.1, gix-dir v0.2.0, gix-macros v0.1.4, gix-mailmap v0.23.0, gix-negotiate v0.13.0, gix-pack v0.49.0, gix-odb v0.59.0, gix-packetline v0.17.4, gix-transport v0.41.2, gix-protocol v0.44.2, gix-revision v0.27.0, gix-refspec v0.23.0, gix-status v0.7.0, gix-submodule v0.10.0, gix-worktree-state v0.9.0, gix v0.60.0, safety bump 26 crates ([`b050327`](https://github.com/Byron/gitoxide/commit/b050327e76f234b19be921b78b7b28e034319fdb)) + - Prepare changelogs prior to release ([`52c3bbd`](https://github.com/Byron/gitoxide/commit/52c3bbd36b9e94a0f3a78b4ada84d0c08eba27f6)) + - Merge branch 'status' ([`3e5c974`](https://github.com/Byron/gitoxide/commit/3e5c974dd62ac134711c6c2f5a5490187a6ea55e)) + - Fix lints for nightly, and clippy ([`f8ce3d0`](https://github.com/Byron/gitoxide/commit/f8ce3d0721b6a53713a9392f2451874f520bc44c)) +
+ ## 0.27.1 (2024-02-25) A maintenance release without user-facing changes. @@ -13,7 +114,7 @@ A maintenance release without user-facing changes. - - 4 commits contributed to the release over the course of 30 calendar days. + - 5 commits contributed to the release over the course of 30 calendar days. - 36 days passed between releases. - 0 commits were understood as [conventional](https://www.conventionalcommits.org). - 0 issues like '(#ID)' were seen in commit messages @@ -25,6 +126,7 @@ A maintenance release without user-facing changes.
view details * **Uncategorized** + - Release gix-date v0.8.4, gix-utils v0.1.10, gix-actor v0.30.1, gix-object v0.41.1, gix-path v0.10.6, gix-glob v0.16.1, gix-quote v0.4.11, gix-attributes v0.22.1, gix-command v0.3.5, gix-filter v0.10.0, gix-commitgraph v0.24.1, gix-worktree-stream v0.10.0, gix-archive v0.10.0, gix-config-value v0.14.5, gix-ref v0.42.0, gix-sec v0.10.5, gix-config v0.35.0, gix-prompt v0.8.3, gix-url v0.27.1, gix-credentials v0.24.1, gix-ignore v0.11.1, gix-index v0.30.0, gix-worktree v0.31.0, gix-diff v0.41.0, gix-discover v0.30.0, gix-pathspec v0.7.0, gix-dir v0.1.0, gix-pack v0.48.0, gix-odb v0.58.0, gix-transport v0.41.1, gix-protocol v0.44.1, gix-revision v0.26.1, gix-refspec v0.22.1, gix-status v0.6.0, gix-submodule v0.9.0, gix-worktree-state v0.8.0, gix v0.59.0, gix-fsck v0.3.0, gitoxide-core v0.36.0, gitoxide v0.34.0, safety bump 10 crates ([`45b4470`](https://github.com/Byron/gitoxide/commit/45b447045bc826f252129c300c531acde2652c64)) - Prepare changelogs prior to release ([`f2e111f`](https://github.com/Byron/gitoxide/commit/f2e111f768fc1bc6182355261c20b63610cffec7)) - Merge branch 'dirwalk' ([`face359`](https://github.com/Byron/gitoxide/commit/face359443ba33e8985ec1525d5ec38b743ea7a9)) - Adjust gitignore files with precious declarations ([`ae86a6a`](https://github.com/Byron/gitoxide/commit/ae86a6a206074b85ff1eba32aea9c8b40c087b17)) diff --git a/gix-url/Cargo.toml b/gix-url/Cargo.toml index fe8384d51c9..704cf182607 100644 --- a/gix-url/Cargo.toml +++ b/gix-url/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "gix-url" -version = "0.27.1" +version = "0.27.3" repository = "https://github.com/Byron/gitoxide" license = "MIT OR Apache-2.0" description = "A crate of the gitoxide project implementing parsing and serialization of gix-url" @@ -17,12 +17,12 @@ doctest = false serde = ["dep:serde", "bstr/serde"] [dependencies] -gix-features = { version = "^0.38.0", path = "../gix-features" } -gix-path = { version = "^0.10.6", path = "../gix-path" } +gix-features = { version = "^0.38.1", path = "../gix-features" } +gix-path = { version = "^0.10.7", path = "../gix-path" } -serde = { version = "1.0.114", optional = true, default-features = false, features = ["std", "derive"]} +serde = { version = "1.0.114", optional = true, default-features = false, features = ["std", "derive"] } thiserror = "1.0.32" -url = "2.5.0" +url = "2.5.1" bstr = { version = "1.3.0", default-features = false, features = ["std"] } home = "0.5.5" diff --git a/gix-url/src/impls.rs b/gix-url/src/impls.rs index a579121cb65..d153d707fde 100644 --- a/gix-url/src/impls.rs +++ b/gix-url/src/impls.rs @@ -1,7 +1,4 @@ -use std::{ - convert::TryFrom, - path::{Path, PathBuf}, -}; +use std::path::{Path, PathBuf}; use bstr::BStr; diff --git a/gix-url/src/lib.rs b/gix-url/src/lib.rs index a5b438901ec..8468ec80eff 100644 --- a/gix-url/src/lib.rs +++ b/gix-url/src/lib.rs @@ -13,6 +13,7 @@ use std::{borrow::Cow, path::PathBuf}; use bstr::{BStr, BString}; /// +#[allow(clippy::empty_docs)] pub mod expand_path; mod scheme; @@ -20,6 +21,7 @@ pub use scheme::Scheme; mod impls; /// +#[allow(clippy::empty_docs)] pub mod parse; /// Parse the given `bytes` as a [git url](Url). @@ -53,6 +55,27 @@ pub fn expand_path(user: Option<&expand_path::ForUser>, path: &BStr) -> Result

for details. +/// +/// # Security Warning +/// +/// This type only expresses known *syntactic* risk. It does not cover other risks, such as passing a personal access +/// token as a username rather than a password in an application that logs usernames. +#[derive(Debug, PartialEq, Eq, Copy, Clone)] +pub enum ArgumentSafety<'a> { + /// May be safe. There is nothing to pass, so there is nothing dangerous. + Absent, + /// May be safe. The argument does not begin with a `-` and so will not be confused as an option. + Usable(&'a str), + /// Dangerous! Begins with `-` and could be treated as an option. Use the value in error messages only. + Dangerous(&'a str), +} + /// A URL with support for specialized git related capabilities. /// /// Additionally there is support for [deserialization](Url::from_bytes()) and [serialization](Url::to_bstring()). @@ -83,12 +106,12 @@ pub struct Url { pub port: Option, /// The path portion of the URL, usually the location of the git repository. /// - /// # Security-Warning + /// # Security Warning /// /// URLs allow paths to start with `-` which makes it possible to mask command-line arguments as path which then leads to /// the invocation of programs from an attacker controlled URL. See for details. /// - /// If this value is going to be used in a command-line application, call [Self::path_argument_safe()] instead. + /// If this value is ever going to be passed to a command-line application, call [Self::path_argument_safe()] instead. pub path: BString, } @@ -162,48 +185,101 @@ impl Url { /// Access impl Url { - /// Returns the user mentioned in the url, if present. + /// Return the username mentioned in the URL, if present. + /// + /// # Security Warning + /// + /// URLs allow usernames to start with `-` which makes it possible to mask command-line arguments as username which then leads to + /// the invocation of programs from an attacker controlled URL. See for details. + /// + /// If this value is ever going to be passed to a command-line application, call [Self::user_argument_safe()] instead. pub fn user(&self) -> Option<&str> { self.user.as_deref() } - /// Returns the password mentioned in the url, if present. + + /// Classify the username of this URL by whether it is safe to pass as a command-line argument. + /// + /// Use this method instead of [Self::user()] if the host is going to be passed to a command-line application. + /// If the unsafe and absent cases need not be distinguished, [Self::user_argument_safe()] may also be used. + pub fn user_as_argument(&self) -> ArgumentSafety<'_> { + match self.user() { + Some(user) if looks_like_command_line_option(user.as_bytes()) => ArgumentSafety::Dangerous(user), + Some(user) => ArgumentSafety::Usable(user), + None => ArgumentSafety::Absent, + } + } + + /// Return the username of this URL if present *and* if it can't be mistaken for a command-line argument. + /// + /// Use this method or [Self::user_as_argument()] instead of [Self::user()] if the host is going to be + /// passed to a command-line application. Prefer [Self::user_as_argument()] unless the unsafe and absent + /// cases need not be distinguished from each other. + pub fn user_argument_safe(&self) -> Option<&str> { + match self.user_as_argument() { + ArgumentSafety::Usable(user) => Some(user), + _ => None, + } + } + + /// Return the password mentioned in the url, if present. pub fn password(&self) -> Option<&str> { self.password.as_deref() } - /// Returns the host mentioned in the url, if present. + + /// Return the host mentioned in the URL, if present. /// - /// # Security-Warning + /// # Security Warning /// /// URLs allow hosts to start with `-` which makes it possible to mask command-line arguments as host which then leads to /// the invocation of programs from an attacker controlled URL. See for details. /// - /// If this value is going to be used in a command-line application, call [Self::host_argument_safe()] instead. + /// If this value is ever going to be passed to a command-line application, call [Self::host_as_argument()] + /// or [Self::host_argument_safe()] instead. pub fn host(&self) -> Option<&str> { self.host.as_deref() } + /// Classify the host of this URL by whether it is safe to pass as a command-line argument. + /// + /// Use this method instead of [Self::host()] if the host is going to be passed to a command-line application. + /// If the unsafe and absent cases need not be distinguished, [Self::host_argument_safe()] may also be used. + pub fn host_as_argument(&self) -> ArgumentSafety<'_> { + match self.host() { + Some(host) if looks_like_command_line_option(host.as_bytes()) => ArgumentSafety::Dangerous(host), + Some(host) => ArgumentSafety::Usable(host), + None => ArgumentSafety::Absent, + } + } + /// Return the host of this URL if present *and* if it can't be mistaken for a command-line argument. /// - /// Use this method if the host is going to be passed to a command-line application. + /// Use this method or [Self::host_as_argument()] instead of [Self::host()] if the host is going to be + /// passed to a command-line application. Prefer [Self::host_as_argument()] unless the unsafe and absent + /// cases need not be distinguished from each other. pub fn host_argument_safe(&self) -> Option<&str> { - self.host().filter(|host| !looks_like_argument(host.as_bytes())) + match self.host_as_argument() { + ArgumentSafety::Usable(host) => Some(host), + _ => None, + } } - /// Return the path of this URL *and* if it can't be mistaken for a command-line argument. + /// Return the path of this URL *if* it can't be mistaken for a command-line argument. /// Note that it always begins with a slash, which is ignored for this comparison. /// - /// Use this method if the path is going to be passed to a command-line application. + /// Use this method instead of accessing [Self::path] directly if the path is going to be passed to a + /// command-line application, unless it is certain that the leading `/` will always be included. pub fn path_argument_safe(&self) -> Option<&BStr> { self.path .get(1..) - .and_then(|truncated| (!looks_like_argument(truncated)).then_some(self.path.as_ref())) + .and_then(|truncated| (!looks_like_command_line_option(truncated)).then_some(self.path.as_ref())) } - /// Returns true if the path portion of the url is `/`. + /// Return true if the path portion of the URL is `/`. pub fn path_is_root(&self) -> bool { self.path == "/" } - /// Returns the actual or default port for use according to the url scheme. + + /// Return the actual or default port for use according to the URL scheme. /// Note that there may be no default port either. pub fn port_or_default(&self) -> Option { self.port.or_else(|| { @@ -219,13 +295,13 @@ impl Url { } } -fn looks_like_argument(b: &[u8]) -> bool { +fn looks_like_command_line_option(b: &[u8]) -> bool { b.first() == Some(&b'-') } /// Transformation impl Url { - /// Turn a file url like `file://relative` into `file:///root/relative`, hence it assures the url's path component is absolute, using + /// Turn a file URL like `file://relative` into `file:///root/relative`, hence it assures the URL's path component is absolute, using /// `current_dir` if necessary. pub fn canonicalized(&self, current_dir: &std::path::Path) -> Result { let mut res = self.clone(); @@ -285,7 +361,7 @@ impl Url { /// Deserialization impl Url { - /// Parse a URL from `bytes` + /// Parse a URL from `bytes`. pub fn from_bytes(bytes: &BStr) -> Result { parse(bytes) } diff --git a/gix-url/src/parse.rs b/gix-url/src/parse.rs index 1a064660b99..3a2b2bfe7c2 100644 --- a/gix-url/src/parse.rs +++ b/gix-url/src/parse.rs @@ -183,7 +183,7 @@ pub(crate) fn file_url(input: &BStr, protocol_colon: usize) -> Result crate::Result { + let mut url = gix_url::parse("https://user:password@host/path".into())?; + + assert_eq!(url.user(), Some("user")); + assert_eq!(url.set_user(Some("new-user".into())), Some("user".into())); + assert_eq!(url.user(), Some("new-user")); + + Ok(()) +} + #[test] fn password() -> crate::Result { let mut url = gix_url::parse("https://user:password@host/path".into())?; @@ -42,33 +55,107 @@ fn password() -> crate::Result { } #[test] -fn user() -> crate::Result { - let mut url = gix_url::parse("https://user:password@host/path".into())?; +fn user_argument_safety() -> crate::Result { + let url = gix_url::parse("ssh://-Fconfigfile@foo/bar".into())?; - assert_eq!(url.user(), Some("user")); - assert_eq!(url.set_user(Some("new-user".into())), Some("user".into())); - assert_eq!(url.user(), Some("new-user")); + assert_eq!(url.user(), Some("-Fconfigfile")); + assert_eq!(url.user_as_argument(), ArgumentSafety::Dangerous("-Fconfigfile")); + assert_eq!(url.user_argument_safe(), None, "An unsafe username is blocked."); + + assert_eq!(url.host(), Some("foo")); + assert_eq!(url.host_as_argument(), ArgumentSafety::Usable("foo")); + assert_eq!(url.host_argument_safe(), Some("foo")); + + assert_eq!(url.path, "/bar"); + assert_eq!(url.path_argument_safe(), Some("/bar".into())); Ok(()) } #[test] -fn host_argument_safe() -> crate::Result { +fn host_argument_safety() -> crate::Result { let url = gix_url::parse("ssh://-oProxyCommand=open$IFS-aCalculator/foo".into())?; + + assert_eq!(url.user(), None); + assert_eq!(url.user_as_argument(), ArgumentSafety::Absent); + assert_eq!( + url.user_argument_safe(), + None, + "As there is no user. See all_argument_safe_valid()" + ); + assert_eq!(url.host(), Some("-oProxyCommand=open$IFS-aCalculator")); - assert_eq!(url.host_argument_safe(), None); + assert_eq!( + url.host_as_argument(), + ArgumentSafety::Dangerous("-oProxyCommand=open$IFS-aCalculator") + ); + assert_eq!(url.host_argument_safe(), None, "An unsafe host string is blocked"); + assert_eq!(url.path, "/foo"); assert_eq!(url.path_argument_safe(), Some("/foo".into())); + Ok(()) } #[test] -fn path_argument_safe() -> crate::Result { +fn path_argument_safety() -> crate::Result { let url = gix_url::parse("ssh://foo/-oProxyCommand=open$IFS-aCalculator".into())?; + + assert_eq!(url.user(), None); + assert_eq!(url.user_as_argument(), ArgumentSafety::Absent); + assert_eq!( + url.user_argument_safe(), + None, + "As there is no user. See all_argument_safe_valid()" + ); + assert_eq!(url.host(), Some("foo")); + assert_eq!(url.host_as_argument(), ArgumentSafety::Usable("foo")); assert_eq!(url.host_argument_safe(), Some("foo")); + assert_eq!(url.path, "/-oProxyCommand=open$IFS-aCalculator"); - assert_eq!(url.path_argument_safe(), None); + assert_eq!(url.path_argument_safe(), None, "An unsafe path is blocked"); + + Ok(()) +} + +#[test] +fn all_argument_safety_safe() -> crate::Result { + let url = gix_url::parse("ssh://user.name@example.com/path/to/file".into())?; + + assert_eq!(url.user(), Some("user.name")); + assert_eq!(url.user_as_argument(), ArgumentSafety::Usable("user.name")); + assert_eq!(url.user_argument_safe(), Some("user.name")); + + assert_eq!(url.host(), Some("example.com")); + assert_eq!(url.host_as_argument(), ArgumentSafety::Usable("example.com")); + assert_eq!(url.host_argument_safe(), Some("example.com")); + + assert_eq!(url.path, "/path/to/file"); + assert_eq!(url.path_argument_safe(), Some("/path/to/file".into())); + + Ok(()) +} + +#[test] +fn all_argument_safety_not_safe() -> crate::Result { + let all_bad = "ssh://-Fconfigfile@-oProxyCommand=open$IFS-aCalculator/-oProxyCommand=open$IFS-aCalculator"; + let url = gix_url::parse(all_bad.into())?; + + assert_eq!(url.user(), Some("-Fconfigfile")); + assert_eq!(url.user_as_argument(), ArgumentSafety::Dangerous("-Fconfigfile")); + assert_eq!(url.user_argument_safe(), None); // An unsafe username is blocked. + + assert_eq!(url.host(), Some("-oProxyCommand=open$IFS-aCalculator")); + assert_eq!( + url.host_as_argument(), + ArgumentSafety::Dangerous("-oProxyCommand=open$IFS-aCalculator") + ); + assert_eq!(url.host_argument_safe(), None, "An unsafe host string is blocked"); + + assert_eq!(url.path, "/-oProxyCommand=open$IFS-aCalculator"); + assert_eq!(url.path_argument_safe(), None, "An unsafe path is blocked"); + Ok(()) } diff --git a/gix-url/tests/fixtures/fuzzed/short-panic.url b/gix-url/tests/fixtures/fuzzed/short-panic.url new file mode 100644 index 00000000000..7a98f9de1b2 --- /dev/null +++ b/gix-url/tests/fixtures/fuzzed/short-panic.url @@ -0,0 +1 @@ +ws://xn--55555577 \ No newline at end of file diff --git a/gix-url/tests/fixtures/make_baseline.sh b/gix-url/tests/fixtures/make_baseline.sh index 73fe7f81349..8419578150a 100755 --- a/gix-url/tests/fixtures/make_baseline.sh +++ b/gix-url/tests/fixtures/make_baseline.sh @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash set -eu -o pipefail # list of urls that should be tested for all platforms diff --git a/gix-url/tests/fuzzed.rs b/gix-url/tests/fuzzed.rs index 59f65464739..8bc345e2926 100644 --- a/gix-url/tests/fuzzed.rs +++ b/gix-url/tests/fuzzed.rs @@ -5,6 +5,7 @@ use bstr::ByteSlice; #[test] fn fuzzed() { for name in [ + "short-panic", "very-long-abort2", "very-long-abort", "very-long6", diff --git a/gix-utils/CHANGELOG.md b/gix-utils/CHANGELOG.md index 088359bbbde..1f39254f0ae 100644 --- a/gix-utils/CHANGELOG.md +++ b/gix-utils/CHANGELOG.md @@ -5,6 +5,57 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## 0.1.12 (2024-04-13) + +A maintenance release without user-facing changes. + +### Commit Statistics + + + + - 3 commits contributed to the release over the course of 2 calendar days. + - 29 days passed between releases. + - 0 commits were understood as [conventional](https://www.conventionalcommits.org). + - 0 issues like '(#ID)' were seen in commit messages + +### Commit Details + + + +

view details + + * **Uncategorized** + - Prepare changelogs prior to release ([`5755271`](https://github.com/Byron/gitoxide/commit/57552717f46f96c35ba4ddc0a64434354ef845e9)) + - Merge pull request #1341 from szepeviktor/typos ([`55f379b`](https://github.com/Byron/gitoxide/commit/55f379bc47065822d078393d83d30c0835a89782)) + - Fix typos ([`f72ecce`](https://github.com/Byron/gitoxide/commit/f72ecce45babcad2a0c9b73c79d01ff502907a57)) +
+ +## 0.1.11 (2024-03-14) + +A maintenance release without user-facing changes. + +### Commit Statistics + + + + - 4 commits contributed to the release over the course of 4 calendar days. + - 18 days passed between releases. + - 0 commits were understood as [conventional](https://www.conventionalcommits.org). + - 0 issues like '(#ID)' were seen in commit messages + +### Commit Details + + + +
view details + + * **Uncategorized** + - Release gix-date v0.8.5, gix-hash v0.14.2, gix-trace v0.1.8, gix-utils v0.1.11, gix-features v0.38.1, gix-actor v0.31.0, gix-validate v0.8.4, gix-object v0.42.0, gix-path v0.10.7, gix-glob v0.16.2, gix-quote v0.4.12, gix-attributes v0.22.2, gix-command v0.3.6, gix-filter v0.11.0, gix-fs v0.10.1, gix-chunk v0.4.8, gix-commitgraph v0.24.2, gix-hashtable v0.5.2, gix-revwalk v0.13.0, gix-traverse v0.38.0, gix-worktree-stream v0.11.0, gix-archive v0.11.0, gix-config-value v0.14.6, gix-tempfile v13.1.1, gix-lock v13.1.1, gix-ref v0.43.0, gix-sec v0.10.6, gix-config v0.36.0, gix-prompt v0.8.4, gix-url v0.27.2, gix-credentials v0.24.2, gix-ignore v0.11.2, gix-bitmap v0.2.11, gix-index v0.31.0, gix-worktree v0.32.0, gix-diff v0.42.0, gix-discover v0.31.0, gix-pathspec v0.7.1, gix-dir v0.2.0, gix-macros v0.1.4, gix-mailmap v0.23.0, gix-negotiate v0.13.0, gix-pack v0.49.0, gix-odb v0.59.0, gix-packetline v0.17.4, gix-transport v0.41.2, gix-protocol v0.44.2, gix-revision v0.27.0, gix-refspec v0.23.0, gix-status v0.7.0, gix-submodule v0.10.0, gix-worktree-state v0.9.0, gix v0.60.0, safety bump 26 crates ([`b050327`](https://github.com/Byron/gitoxide/commit/b050327e76f234b19be921b78b7b28e034319fdb)) + - Prepare changelogs prior to release ([`52c3bbd`](https://github.com/Byron/gitoxide/commit/52c3bbd36b9e94a0f3a78b4ada84d0c08eba27f6)) + - Merge branch 'status' ([`3e5c974`](https://github.com/Byron/gitoxide/commit/3e5c974dd62ac134711c6c2f5a5490187a6ea55e)) + - Fix lints for nightly, and clippy ([`f8ce3d0`](https://github.com/Byron/gitoxide/commit/f8ce3d0721b6a53713a9392f2451874f520bc44c)) +
+ ## 0.1.10 (2024-02-25) ### New Features @@ -15,7 +66,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - - 13 commits contributed to the release over the course of 16 calendar days. + - 14 commits contributed to the release over the course of 16 calendar days. - 36 days passed between releases. - 1 commit was understood as [conventional](https://www.conventionalcommits.org). - 0 issues like '(#ID)' were seen in commit messages @@ -27,6 +78,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
view details * **Uncategorized** + - Release gix-date v0.8.4, gix-utils v0.1.10, gix-actor v0.30.1, gix-object v0.41.1, gix-path v0.10.6, gix-glob v0.16.1, gix-quote v0.4.11, gix-attributes v0.22.1, gix-command v0.3.5, gix-filter v0.10.0, gix-commitgraph v0.24.1, gix-worktree-stream v0.10.0, gix-archive v0.10.0, gix-config-value v0.14.5, gix-ref v0.42.0, gix-sec v0.10.5, gix-config v0.35.0, gix-prompt v0.8.3, gix-url v0.27.1, gix-credentials v0.24.1, gix-ignore v0.11.1, gix-index v0.30.0, gix-worktree v0.31.0, gix-diff v0.41.0, gix-discover v0.30.0, gix-pathspec v0.7.0, gix-dir v0.1.0, gix-pack v0.48.0, gix-odb v0.58.0, gix-transport v0.41.1, gix-protocol v0.44.1, gix-revision v0.26.1, gix-refspec v0.22.1, gix-status v0.6.0, gix-submodule v0.9.0, gix-worktree-state v0.8.0, gix v0.59.0, gix-fsck v0.3.0, gitoxide-core v0.36.0, gitoxide v0.34.0, safety bump 10 crates ([`45b4470`](https://github.com/Byron/gitoxide/commit/45b447045bc826f252129c300c531acde2652c64)) - Prepare changelogs prior to release ([`f2e111f`](https://github.com/Byron/gitoxide/commit/f2e111f768fc1bc6182355261c20b63610cffec7)) - Merge pull request #1297 from fbstj/main ([`f458966`](https://github.com/Byron/gitoxide/commit/f45896615c25fad8476a889305c25301d9c9f521)) - Replace manual impl with TryFrom bounds & default method ([`a0deb06`](https://github.com/Byron/gitoxide/commit/a0deb0631a2160c7076e884144646c26aec12914)) @@ -76,12 +128,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Chore - - change `rust-version` manifest field back to 1.65. - They didn't actually need to be higher to work, and changing them - unecessarily can break downstream CI. - - Let's keep this value as low as possible, and only increase it when - more recent features are actually used. +- change `rust-version` manifest field back to 1.65. + They didn't actually need to be higher to work, and changing them + unecessarily can break downstream CI. + + Let's keep this value as low as possible, and only increase it when + more recent features are actually used. ### Commit Statistics @@ -109,8 +161,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Chore - - upgrade MSRV to v1.70 - Our MSRV follows the one of `helix`, which in turn follows Firefox. +- upgrade MSRV to v1.70 + Our MSRV follows the one of `helix`, which in turn follows Firefox. ### Commit Statistics diff --git a/gix-utils/Cargo.toml b/gix-utils/Cargo.toml index 3bbf98933a6..134e470c22c 100644 --- a/gix-utils/Cargo.toml +++ b/gix-utils/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "gix-utils" -version = "0.1.10" +version = "0.1.12" repository = "https://github.com/Byron/gitoxide" license = "MIT OR Apache-2.0" description = "A crate with `gitoxide` utilities that don't need feature toggles" diff --git a/gix-utils/src/btoi.rs b/gix-utils/src/btoi.rs index c2cc5e28c61..08dff105a0d 100644 --- a/gix-utils/src/btoi.rs +++ b/gix-utils/src/btoi.rs @@ -195,9 +195,9 @@ pub fn to_signed(bytes: &[u8]) -> Result /// Returns [`ParseIntegerError`] for any of the following conditions: /// /// * `bytes` has no digits -/// * not all characters of `bytes` are `0-9`, `a-z`, `A-Z`, exluding an +/// * not all characters of `bytes` are `0-9`, `a-z`, `A-Z`, excluding an /// optional leading sign -/// * not all characters refer to digits in the given `radix`, exluding an +/// * not all characters refer to digits in the given `radix`, excluding an /// optional leading sign /// * the number overflows or underflows `I` /// @@ -272,7 +272,7 @@ pub fn to_signed_with_radix(bytes: &[u8], radix: u32) -> Result pub trait MinNumTraits: Sized + Copy + TryFrom { /// the 0 value for this type const ZERO: Self; - /// convert from a unsinged 32-bit word + /// convert from a unsigned 32-bit word fn from_u32(n: u32) -> Option { Self::try_from(n).ok() } diff --git a/gix-utils/src/lib.rs b/gix-utils/src/lib.rs index 5e2e32d1365..bd61a08b0d1 100644 --- a/gix-utils/src/lib.rs +++ b/gix-utils/src/lib.rs @@ -5,15 +5,19 @@ #![forbid(unsafe_code)] /// +#[allow(clippy::empty_docs)] pub mod backoff; /// +#[allow(clippy::empty_docs)] pub mod buffers; /// +#[allow(clippy::empty_docs)] pub mod str; /// +#[allow(clippy::empty_docs)] pub mod btoi; /// A utility to do buffer-swapping with. diff --git a/gix-utils/tests/backoff/mod.rs b/gix-utils/tests/backoff/mod.rs index a84ed754360..ca9077841ca 100644 --- a/gix-utils/tests/backoff/mod.rs +++ b/gix-utils/tests/backoff/mod.rs @@ -1,4 +1,4 @@ -use std::{convert::TryInto, time::Duration}; +use std::time::Duration; use gix_utils::backoff::Exponential; diff --git a/gix-validate/CHANGELOG.md b/gix-validate/CHANGELOG.md index 6991f60ef5f..2b76e6a4ff2 100644 --- a/gix-validate/CHANGELOG.md +++ b/gix-validate/CHANGELOG.md @@ -5,22 +5,100 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## 0.8.5 (2024-05-22) + +### New Features + + - add `path::component_is_windows_device()` + That way it's easy to determine if a component contains a windows device name + - defend against `CON` device names and more if `gitoxide.core.protectWindows` is enabled. + Note that trailing `.` are forbidden for some reason, but trailing ` ` (space) is forbidden + as it's just ignored when creating directories or files, allowing them to be clobbered + and merged silently. + - add validation for path components + That way it's easier to assure that forbidden names are never used + as part of path components. + +### Commit Statistics + + + + - 12 commits contributed to the release over the course of 3 calendar days. + - 68 days passed between releases. + - 3 commits were understood as [conventional](https://www.conventionalcommits.org). + - 0 issues like '(#ID)' were seen in commit messages + +### Thanks Clippy + + + +[Clippy](https://github.com/rust-lang/rust-clippy) helped 1 time to make code idiomatic. + +### Commit Details + + + +
view details + + * **Uncategorized** + - Adjust changelogs prior to release ([`9511416`](https://github.com/Byron/gitoxide/commit/9511416a6cd0c571233f958c165329c8705c2498)) + - Merge branch 'various-fixes' ([`d6cd449`](https://github.com/Byron/gitoxide/commit/d6cd44930fb204b06e2b70fc6965e7705530c47a)) + - Merge pull request from GHSA-7w47-3wg8-547c ([`79dce79`](https://github.com/Byron/gitoxide/commit/79dce79c62f6072aa2653780d590dc3993dfa401)) + - Apply suggestions from code review ([`1242151`](https://github.com/Byron/gitoxide/commit/1242151079004ae99fae7b80966de151961a6159)) + - Add `path::component_is_windows_device()` ([`f1f0ba5`](https://github.com/Byron/gitoxide/commit/f1f0ba51cf1633bc2ca7e90904c01b8f8fee810e)) + - Further testing of `.git` path variants ([`4791e31`](https://github.com/Byron/gitoxide/commit/4791e314f217e83b628baec60ad05f5f8f571f36)) + - Apply suggestions from code review ([`ccbc119`](https://github.com/Byron/gitoxide/commit/ccbc1197b6dcb7e7118e206183582d6b46fc5ebc)) + - Address review comments ([`fcc3b69`](https://github.com/Byron/gitoxide/commit/fcc3b69867db1628f6a44d0e0dad8f7417f566bc)) + - Apply suggestions from code review ([`bad9a79`](https://github.com/Byron/gitoxide/commit/bad9a797b99880ce9d1c20e11c801bd0e741db64)) + - Thanks clippy ([`1076375`](https://github.com/Byron/gitoxide/commit/1076375571c493fe4f2cd512b28bb4e28d365292)) + - Defend against `CON` device names and more if `gitoxide.core.protectWindows` is enabled. ([`a67d82d`](https://github.com/Byron/gitoxide/commit/a67d82dce4346c6e76684b4405f33bec47d5bccd)) + - Add validation for path components ([`eff4c00`](https://github.com/Byron/gitoxide/commit/eff4c00fc76b7bc8c8ac6a6ec4c5bd34889cc436)) +
+ +## 0.8.4 (2024-03-14) + +A maintenance release without user-facing changes. + +### Commit Statistics + + + + - 4 commits contributed to the release over the course of 4 calendar days. + - 75 days passed between releases. + - 0 commits were understood as [conventional](https://www.conventionalcommits.org). + - 0 issues like '(#ID)' were seen in commit messages + +### Commit Details + + + +
view details + + * **Uncategorized** + - Release gix-date v0.8.5, gix-hash v0.14.2, gix-trace v0.1.8, gix-utils v0.1.11, gix-features v0.38.1, gix-actor v0.31.0, gix-validate v0.8.4, gix-object v0.42.0, gix-path v0.10.7, gix-glob v0.16.2, gix-quote v0.4.12, gix-attributes v0.22.2, gix-command v0.3.6, gix-filter v0.11.0, gix-fs v0.10.1, gix-chunk v0.4.8, gix-commitgraph v0.24.2, gix-hashtable v0.5.2, gix-revwalk v0.13.0, gix-traverse v0.38.0, gix-worktree-stream v0.11.0, gix-archive v0.11.0, gix-config-value v0.14.6, gix-tempfile v13.1.1, gix-lock v13.1.1, gix-ref v0.43.0, gix-sec v0.10.6, gix-config v0.36.0, gix-prompt v0.8.4, gix-url v0.27.2, gix-credentials v0.24.2, gix-ignore v0.11.2, gix-bitmap v0.2.11, gix-index v0.31.0, gix-worktree v0.32.0, gix-diff v0.42.0, gix-discover v0.31.0, gix-pathspec v0.7.1, gix-dir v0.2.0, gix-macros v0.1.4, gix-mailmap v0.23.0, gix-negotiate v0.13.0, gix-pack v0.49.0, gix-odb v0.59.0, gix-packetline v0.17.4, gix-transport v0.41.2, gix-protocol v0.44.2, gix-revision v0.27.0, gix-refspec v0.23.0, gix-status v0.7.0, gix-submodule v0.10.0, gix-worktree-state v0.9.0, gix v0.60.0, safety bump 26 crates ([`b050327`](https://github.com/Byron/gitoxide/commit/b050327e76f234b19be921b78b7b28e034319fdb)) + - Prepare changelogs prior to release ([`52c3bbd`](https://github.com/Byron/gitoxide/commit/52c3bbd36b9e94a0f3a78b4ada84d0c08eba27f6)) + - Merge branch 'status' ([`3e5c974`](https://github.com/Byron/gitoxide/commit/3e5c974dd62ac134711c6c2f5a5490187a6ea55e)) + - Fix lints for nightly, and clippy ([`f8ce3d0`](https://github.com/Byron/gitoxide/commit/f8ce3d0721b6a53713a9392f2451874f520bc44c)) +
+ ## 0.8.3 (2023-12-30) + + ### Chore - - change `rust-version` manifest field back to 1.65. - They didn't actually need to be higher to work, and changing them - unecessarily can break downstream CI. - - Let's keep this value as low as possible, and only increase it when - more recent features are actually used. +- change `rust-version` manifest field back to 1.65. + They didn't actually need to be higher to work, and changing them + unecessarily can break downstream CI. + + Let's keep this value as low as possible, and only increase it when + more recent features are actually used. ### Commit Statistics - - 2 commits contributed to the release. + - 3 commits contributed to the release. - 1 commit was understood as [conventional](https://www.conventionalcommits.org). - 0 issues like '(#ID)' were seen in commit messages @@ -31,6 +109,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
view details * **Uncategorized** + - Release gix-date v0.8.3, gix-hash v0.14.1, gix-trace v0.1.6, gix-features v0.37.1, gix-actor v0.29.1, gix-validate v0.8.3, gix-object v0.40.1, gix-path v0.10.3, gix-glob v0.15.1, gix-quote v0.4.10, gix-attributes v0.21.1, gix-command v0.3.2, gix-packetline-blocking v0.17.2, gix-utils v0.1.8, gix-filter v0.8.1, gix-fs v0.9.1, gix-chunk v0.4.7, gix-commitgraph v0.23.1, gix-hashtable v0.5.1, gix-revwalk v0.11.1, gix-traverse v0.36.1, gix-worktree-stream v0.8.1, gix-archive v0.8.1, gix-config-value v0.14.3, gix-tempfile v12.0.1, gix-lock v12.0.1, gix-ref v0.40.1, gix-sec v0.10.3, gix-config v0.33.1, gix-prompt v0.8.2, gix-url v0.26.1, gix-credentials v0.23.1, gix-ignore v0.10.1, gix-bitmap v0.2.10, gix-index v0.28.1, gix-worktree v0.29.1, gix-diff v0.39.1, gix-discover v0.28.1, gix-macros v0.1.3, gix-mailmap v0.21.1, gix-negotiate v0.11.1, gix-pack v0.46.1, gix-odb v0.56.1, gix-pathspec v0.5.1, gix-packetline v0.17.2, gix-transport v0.40.1, gix-protocol v0.43.1, gix-revision v0.25.1, gix-refspec v0.21.1, gix-status v0.4.1, gix-submodule v0.7.1, gix-worktree-state v0.6.1, gix v0.57.1 ([`972241f`](https://github.com/Byron/gitoxide/commit/972241f1904944e8b6e84c6aa1649a49be7a85c3)) - Merge branch 'msrv' ([`8c492d7`](https://github.com/Byron/gitoxide/commit/8c492d7b7e6e5d520b1e3ffeb489eeb88266aa75)) - Change `rust-version` manifest field back to 1.65. ([`3bd09ef`](https://github.com/Byron/gitoxide/commit/3bd09ef120945a9669321ea856db4079a5dab930))
@@ -41,8 +120,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Chore - - upgrade MSRV to v1.70 - Our MSRV follows the one of `helix`, which in turn follows Firefox. +- upgrade MSRV to v1.70 + Our MSRV follows the one of `helix`, which in turn follows Firefox. ### Commit Statistics @@ -292,7 +371,7 @@ A maintenance release without any user-facing changes. ### Chore (BREAKING) - - replace `quick-error` with `thiserror` +- replace `quick-error` with `thiserror` ### Changed (BREAKING) @@ -300,8 +379,8 @@ A maintenance release without any user-facing changes. ### Chore - - uniformize deny attributes - - remove default link to cargo doc everywhere +- uniformize deny attributes +- remove default link to cargo doc everywhere ### Documentation @@ -486,7 +565,7 @@ A maintenance release without user-facing changes. ### Chore (BREAKING) - - replace `quick-error` with `thiserror` +- replace `quick-error` with `thiserror` ## 0.5.5 (2022-08-24) @@ -495,8 +574,8 @@ A maintenance release without user-facing changes. ### Chore - - uniformize deny attributes - - remove default link to cargo doc everywhere +- uniformize deny attributes +- remove default link to cargo doc everywhere ## 0.5.4 (2022-05-18) diff --git a/gix-validate/Cargo.toml b/gix-validate/Cargo.toml index 496d54c3c29..d13f944ddaf 100644 --- a/gix-validate/Cargo.toml +++ b/gix-validate/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "gix-validate" -version = "0.8.3" +version = "0.8.5" repository = "https://github.com/Byron/gitoxide" license = "MIT OR Apache-2.0" description = "Validation functions for various kinds of names in git" diff --git a/gix-validate/src/lib.rs b/gix-validate/src/lib.rs index f7b6cc4aa76..0143187a851 100644 --- a/gix-validate/src/lib.rs +++ b/gix-validate/src/lib.rs @@ -3,10 +3,17 @@ #![forbid(unsafe_code)] /// +#[allow(clippy::empty_docs)] pub mod reference; /// +#[allow(clippy::empty_docs)] pub mod tag; /// +#[allow(clippy::empty_docs)] pub mod submodule; + +/// +#[allow(clippy::empty_docs)] +pub mod path; diff --git a/gix-validate/src/path.rs b/gix-validate/src/path.rs new file mode 100644 index 00000000000..6a18fe7468e --- /dev/null +++ b/gix-validate/src/path.rs @@ -0,0 +1,336 @@ +use bstr::{BStr, ByteSlice}; + +/// +#[allow(clippy::empty_docs)] +pub mod component { + /// The error returned by [`component()`](super::component()). + #[derive(Debug, thiserror::Error)] + #[allow(missing_docs)] + pub enum Error { + #[error("A path component must not be empty")] + Empty, + #[error("Path separators like / or \\ are not allowed")] + PathSeparator, + #[error("Windows path prefixes are not allowed")] + WindowsPathPrefix, + #[error("Windows device-names may have side-effects and are not allowed")] + WindowsReservedName, + #[error("Trailing spaces or dots, and the following characters anywhere, are forbidden in Windows paths, along with non-printable ones: <>:\"|?*")] + WindowsIllegalCharacter, + #[error("The .git name may never be used")] + DotGitDir, + #[error("The .gitmodules file must not be a symlink")] + SymlinkedGitModules, + } + + /// Further specify what to check for in [`component()`](super::component()) + /// + /// Note that the `Default` implementation maximizes safety by enabling all protections. + #[derive(Debug, Copy, Clone)] + pub struct Options { + /// This flag should be turned on when on Windows, but can be turned on when on other platforms + /// as well to prevent path components that can cause trouble on Windows. + pub protect_windows: bool, + /// If `true`, protections for the MacOS HFS+ filesystem will be active, checking for + /// special directories that we should never write while ignoring codepoints just like HFS+ would. + /// + /// This field is equivalent to `core.protectHFS`. + pub protect_hfs: bool, + /// If `true`, protections for Windows NTFS specific features will be active. This adds special handling + /// for `8.3` filenames and alternate data streams, both of which could be used to mask the true name of + /// what would be created on disk. + /// + /// This field is equivalent to `core.protectNTFS`. + pub protect_ntfs: bool, + } + + impl Default for Options { + fn default() -> Self { + Options { + protect_windows: true, + protect_hfs: true, + protect_ntfs: true, + } + } + } + + /// The mode of the component, if it's the leaf of a path. + #[derive(Debug, Copy, Clone, PartialEq, Eq)] + pub enum Mode { + /// The item is a symbolic link. + Symlink, + } +} + +/// Assure the given `input` resembles a valid name for a tree or blob, and in that sense, a path component. +/// `mode` indicates the kind of `input` and it should be `Some` if `input` is the last component in the underlying +/// path. +/// +/// `input` must not make it possible to exit the repository, or to specify absolute paths. +pub fn component( + input: &BStr, + mode: Option, + component::Options { + protect_windows, + protect_hfs, + protect_ntfs, + }: component::Options, +) -> Result<&BStr, component::Error> { + if input.is_empty() { + return Err(component::Error::Empty); + } + if protect_windows { + if input.find_byteset(b"/\\").is_some() { + return Err(component::Error::PathSeparator); + } + if input.chars().nth(1) == Some(':') { + return Err(component::Error::WindowsPathPrefix); + } + } else if input.find_byte(b'/').is_some() { + return Err(component::Error::PathSeparator); + } + if protect_hfs { + if is_dot_hfs(input, "git") { + return Err(component::Error::DotGitDir); + } + if is_symlink(mode) && is_dot_hfs(input, "gitmodules") { + return Err(component::Error::SymlinkedGitModules); + } + } + + if protect_ntfs { + if is_dot_git_ntfs(input) { + return Err(component::Error::DotGitDir); + } + if is_symlink(mode) && is_dot_ntfs(input, "gitmodules", "gi7eba") { + return Err(component::Error::SymlinkedGitModules); + } + + if protect_windows { + if let Some(err) = check_win_devices_and_illegal_characters(input) { + return Err(err); + } + } + } + + if !(protect_hfs | protect_ntfs) { + if input.eq_ignore_ascii_case(b".git") { + return Err(component::Error::DotGitDir); + } + if is_symlink(mode) && input.eq_ignore_ascii_case(b".gitmodules") { + return Err(component::Error::SymlinkedGitModules); + } + } + Ok(input) +} + +/// Return `true` if the path component at `input` looks like a Windows device, like `CON` +/// or `LPT1` (case-insensitively). +/// +/// This is relevant only on Windows, where one may be tricked into reading or writing to such devices. +/// When reading from `CON`, a console-program may block until the user provided input. +pub fn component_is_windows_device(input: &BStr) -> bool { + is_win_device(input) +} + +fn is_win_device(input: &BStr) -> bool { + let Some(in3) = input.get(..3) else { return false }; + if in3.eq_ignore_ascii_case(b"AUX") && is_done_windows(input.get(3..)) { + return true; + } + if in3.eq_ignore_ascii_case(b"NUL") && is_done_windows(input.get(3..)) { + return true; + } + if in3.eq_ignore_ascii_case(b"PRN") && is_done_windows(input.get(3..)) { + return true; + } + // Note that the following allows `COM0`, even though `LPT0` is not allowed. + // Even though tests seem to indicate that neither `LPT0` nor `COM0` are valid + // device names, it's unclear this truly is the case in all possible versions and editions + // of Windows. + // Hence, justification for this asymmetry is merely to do exactly the same as Git does, + // and to have exactly the same behaviour during validation (for worktree-writes). + if in3.eq_ignore_ascii_case(b"COM") + && input.get(3).map_or(false, |n| *n >= b'1' && *n <= b'9') + && is_done_windows(input.get(4..)) + { + return true; + } + if in3.eq_ignore_ascii_case(b"LPT") + && input.get(3).map_or(false, u8::is_ascii_digit) + && is_done_windows(input.get(4..)) + { + return true; + } + if in3.eq_ignore_ascii_case(b"CON") + && (is_done_windows(input.get(3..)) + || (input.get(3..6).map_or(false, |n| n.eq_ignore_ascii_case(b"IN$")) && is_done_windows(input.get(6..))) + || (input.get(3..7).map_or(false, |n| n.eq_ignore_ascii_case(b"OUT$")) && is_done_windows(input.get(7..)))) + { + return true; + } + false +} + +fn check_win_devices_and_illegal_characters(input: &BStr) -> Option { + if is_win_device(input) { + return Some(component::Error::WindowsReservedName); + } + if input.iter().any(|b| *b < 0x20 || b":<>\"|?*".contains(b)) { + return Some(component::Error::WindowsIllegalCharacter); + } + if input.ends_with(b".") || input.ends_with(b" ") { + return Some(component::Error::WindowsIllegalCharacter); + } + None +} + +fn is_symlink(mode: Option) -> bool { + mode.map_or(false, |m| m == component::Mode::Symlink) +} + +fn is_dot_hfs(input: &BStr, search_case_insensitive: &str) -> bool { + let mut input = input.chars().filter(|c| match *c as u32 { + // Case-insensitive HFS+ skips these code points as "ignorable" when comparing filenames. See: + // https://github.com/git/git/commit/6162a1d323d24fd8cbbb1a6145a91fb849b2568f + // https://developer.apple.com/library/archive/technotes/tn/tn1150.html#StringComparisonAlgorithm + // https://github.com/apple-oss-distributions/hfs/blob/main/core/UCStringCompareData.h + 0x200c | // ZERO WIDTH NON-JOINER + 0x200d | // ZERO WIDTH JOINER + 0x200e | // LEFT-TO-RIGHT MARK + 0x200f | // RIGHT-TO-LEFT MARK + 0x202a | // LEFT-TO-RIGHT EMBEDDING + 0x202b | // RIGHT-TO-LEFT EMBEDDING + 0x202c | // POP DIRECTIONAL FORMATTING + 0x202d | // LEFT-TO-RIGHT OVERRIDE + 0x202e | // RIGHT-TO-LEFT OVERRIDE + 0x206a | // INHIBIT SYMMETRIC SWAPPING + 0x206b | // ACTIVATE SYMMETRIC SWAPPING + 0x206c | // INHIBIT ARABIC FORM SHAPING + 0x206d | // ACTIVATE ARABIC FORM SHAPING + 0x206e | // NATIONAL DIGIT SHAPES + 0x206f | // NOMINAL DIGIT SHAPES + 0xfeff => false, // ZERO WIDTH NO-BREAK SPACE + _ => true + }); + if input.next() != Some('.') { + return false; + } + + let mut comp = search_case_insensitive.chars(); + loop { + match (comp.next(), input.next()) { + (Some(a), Some(b)) => { + if !a.eq_ignore_ascii_case(&b) { + return false; + } + } + (None, None) => return true, + _ => return false, + } + } +} + +fn is_dot_git_ntfs(input: &BStr) -> bool { + if input + .get(..4) + .map_or(false, |input| input.eq_ignore_ascii_case(b".git")) + { + return is_done_ntfs(input.get(4..)); + } + if input + .get(..5) + .map_or(false, |input| input.eq_ignore_ascii_case(b"git~1")) + { + return is_done_ntfs(input.get(5..)); + } + false +} + +/// The `search_case_insensitive` name is the actual name to look for (in a case-insensitive way). +/// Opposed to that there is the special `ntfs_shortname_prefix` which is derived from `search_case_insensitive` +/// but looks more like a hash, one that NTFS uses to disambiguate things, for when there is a lot of files +/// with the same prefix. +fn is_dot_ntfs(input: &BStr, search_case_insensitive: &str, ntfs_shortname_prefix: &str) -> bool { + if input.first() == Some(&b'.') { + let end_pos = 1 + search_case_insensitive.len(); + if input.get(1..end_pos).map_or(false, |input| { + input.eq_ignore_ascii_case(search_case_insensitive.as_bytes()) + }) { + is_done_ntfs(input.get(end_pos..)) + } else { + false + } + } else { + let search_case_insensitive: &[u8] = search_case_insensitive.as_bytes(); + if search_case_insensitive + .get(..6) + .zip(input.get(..6)) + .map_or(false, |(ntfs_prefix, first_6_of_input)| { + first_6_of_input.eq_ignore_ascii_case(ntfs_prefix) + && input.get(6) == Some(&b'~') + // It's notable that only `~1` to `~4` are possible before the disambiguation algorithm + // switches to using the `ntfs_shortname_prefix`, which is checked hereafter. + && input.get(7).map_or(false, |num| (b'1'..=b'4').contains(num)) + }) + { + return is_done_ntfs(input.get(8..)); + } + + let ntfs_shortname_prefix: &[u8] = ntfs_shortname_prefix.as_bytes(); + let mut saw_tilde = false; + let mut pos = 0; + while pos < 8 { + let Some(b) = input.get(pos).copied() else { + return false; + }; + if saw_tilde { + if !b.is_ascii_digit() { + return false; + } + } else if b == b'~' { + saw_tilde = true; + pos += 1; + let Some(b) = input.get(pos).copied() else { + return false; + }; + if !(b'1'..=b'9').contains(&b) { + return false; + } + } else if pos >= 6 + || b & 0x80 == 0x80 + || ntfs_shortname_prefix + .get(pos) + .map_or(true, |ob| !b.eq_ignore_ascii_case(ob)) + { + return false; + } + pos += 1; + } + is_done_ntfs(input.get(pos..)) + } +} + +/// Check if trailing filename bytes leave a match to special files like `.git` unchanged in NTFS. +fn is_done_ntfs(input: Option<&[u8]>) -> bool { + // Skip spaces and dots. Then return true if we are at the end or a colon. + let Some(input) = input else { return true }; + for b in input.bytes() { + if b == b':' { + return true; + } + if b != b' ' && b != b'.' { + return false; + } + } + true +} + +/// Check if trailing filename bytes leave a match to Windows reserved device names unchanged. +fn is_done_windows(input: Option<&[u8]>) -> bool { + // Skip spaces. Then return true if we are at the end or a dot or colon. + let Some(input) = input else { return true }; + let skip = input.bytes().take_while(|b| *b == b' ').count(); + let Some(next) = input.get(skip) else { return true }; + *next == b'.' || *next == b':' +} diff --git a/gix-validate/src/reference.rs b/gix-validate/src/reference.rs index fff87e3b40c..ac222ed105c 100644 --- a/gix-validate/src/reference.rs +++ b/gix-validate/src/reference.rs @@ -1,4 +1,5 @@ /// +#[allow(clippy::empty_docs)] pub mod name { use std::convert::Infallible; diff --git a/gix-validate/src/submodule.rs b/gix-validate/src/submodule.rs index 6811f4ff2eb..c524cb22a5a 100644 --- a/gix-validate/src/submodule.rs +++ b/gix-validate/src/submodule.rs @@ -1,6 +1,7 @@ use bstr::{BStr, ByteSlice}; /// +#[allow(clippy::empty_docs)] pub mod name { /// The error used in [name()](super::name()). #[derive(Debug, thiserror::Error)] diff --git a/gix-validate/src/tag.rs b/gix-validate/src/tag.rs index 91ceec18549..eb5b7e12066 100644 --- a/gix-validate/src/tag.rs +++ b/gix-validate/src/tag.rs @@ -1,6 +1,7 @@ use bstr::BStr; /// +#[allow(clippy::empty_docs)] pub mod name { use bstr::BString; diff --git a/gix-validate/tests/path/mod.rs b/gix-validate/tests/path/mod.rs new file mode 100644 index 00000000000..e9aa7a6ec88 --- /dev/null +++ b/gix-validate/tests/path/mod.rs @@ -0,0 +1,350 @@ +#[test] +fn component_is_windows_device() { + for device in ["con", "CONIN$", "lpt1.txt", "AUX", "Prn", "NUL", "COM9"] { + assert!(gix_validate::path::component_is_windows_device(device.into())); + } + for not_device in ["coni", "CONIN", "lpt", "AUXi", "aPrn", "NULl", "COM"] { + assert!(!gix_validate::path::component_is_windows_device(not_device.into())); + } +} + +mod component { + use gix_validate::path::component; + + const NO_OPTS: component::Options = component::Options { + protect_windows: false, + protect_hfs: false, + protect_ntfs: false, + }; + const ALL_OPTS: component::Options = component::Options { + protect_windows: true, + protect_hfs: true, + protect_ntfs: true, + }; + + mod valid { + use crate::path::component::{ALL_OPTS, NO_OPTS}; + use bstr::ByteSlice; + use gix_validate::path::component; + use gix_validate::path::component::Mode::Symlink; + macro_rules! mktest { + ($name:ident, $input:expr) => { + mktest!($name, $input, ALL_OPTS); + }; + ($name:ident, $input:expr, $opts:expr) => { + #[test] + fn $name() { + assert!(gix_validate::path::component($input.as_bstr(), None, $opts).is_ok()) + } + }; + ($name:ident, $input:expr, $mode:expr, $opts:expr) => { + #[test] + fn $name() { + assert!(gix_validate::path::component($input.as_bstr(), Some($mode), $opts).is_ok()) + } + }; + } + + const UNIX_OPTS: component::Options = component::Options { + protect_windows: false, + protect_hfs: true, + protect_ntfs: true, + }; + + mktest!(ascii, b"ascii-only_and-that"); + mktest!(unicode, "😁👍👌".as_bytes()); + mktest!(backslashes_on_unix, b"\\", UNIX_OPTS); + mktest!(drive_letters_on_unix, b"c:", UNIX_OPTS); + mktest!(virtual_drive_letters_on_unix, "֍:".as_bytes(), UNIX_OPTS); + mktest!(unc_path_on_unix, b"\\\\?\\pictures", UNIX_OPTS); + mktest!(not_dot_git_longer, b".gitu", NO_OPTS); + mktest!(not_dot_git_longer_all, b".gitu"); + mktest!(not_dot_gitmodules_shorter, b".gitmodule", Symlink, NO_OPTS); + mktest!(not_dot_gitmodules_shorter_all, b".gitmodule", Symlink, ALL_OPTS); + mktest!(not_dot_gitmodules_longer, b".gitmodulesa", Symlink, NO_OPTS); + mktest!(not_dot_gitmodules_longer_all, b".gitmodulesa", Symlink, ALL_OPTS); + mktest!(dot_gitmodules_as_file, b".gitmodules", UNIX_OPTS); + mktest!( + starts_with_dot_git_with_backslashes_on_linux, + b".git\\hooks\\pre-commit", + UNIX_OPTS + ); + mktest!(not_dot_git_shorter, b".gi", NO_OPTS); + mktest!(not_dot_git_shorter_ntfs_8_3, b"gi~1"); + mktest!(not_dot_git_longer_ntfs_8_3, b"gitu~1"); + mktest!(not_dot_git_shorter_ntfs_8_3_disabled, b"git~1", NO_OPTS); + mktest!(not_dot_git_longer_hfs, ".g\u{200c}itu".as_bytes()); + mktest!(not_dot_git_shorter_hfs, ".g\u{200c}i".as_bytes()); + mktest!(com_0_lower, b"com0"); + mktest!(com_without_number_0_lower, b"comm"); + mktest!(conout_without_dollar_with_extension, b"conout.c"); + mktest!(conin_without_dollar_with_extension, b"conin.c"); + mktest!(conin_without_dollar, b"conin"); + mktest!(not_con, b"com"); + mktest!(also_not_con, b"co"); + mktest!(not_nul, b"null"); + mktest!( + not_dot_gitmodules_shorter_hfs, + ".gitm\u{200c}odule".as_bytes(), + Symlink, + UNIX_OPTS + ); + mktest!(dot_gitmodules_as_file_hfs, ".g\u{200c}itmodules".as_bytes(), UNIX_OPTS); + mktest!(dot_gitmodules_ntfs_8_3_disabled, b"gItMOD~1", Symlink, NO_OPTS); + mktest!( + not_dot_gitmodules_longer_hfs, + "\u{200c}.gitmodulesa".as_bytes(), + Symlink, + UNIX_OPTS + ); + } + + mod invalid { + use crate::path::component::{ALL_OPTS, NO_OPTS}; + use bstr::ByteSlice; + use gix_validate::path::component::Error; + use gix_validate::path::component::Mode::Symlink; + + macro_rules! mktest { + ($name:ident, $input:expr, $expected:pat) => { + mktest!($name, $input, $expected, ALL_OPTS); + }; + ($name:ident, $input:expr, $expected:pat, $opts:expr) => { + #[test] + fn $name() { + match gix_validate::path::component($input.as_bstr(), None, $opts) { + Err($expected) => {} + got => panic!("Wanted {}, got {:?}", stringify!($expected), got), + } + } + }; + ($name:ident, $input:expr, $expected:pat, $mode:expr, $opts:expr) => { + #[test] + fn $name() { + match gix_validate::path::component($input.as_bstr(), Some($mode), $opts) { + Err($expected) => {} + got => panic!("Wanted {}, got {:?}", stringify!($expected), got), + } + } + }; + } + + mktest!(empty, b"", Error::Empty); + mktest!(dot_git_lower, b".git", Error::DotGitDir, NO_OPTS); + mktest!(dot_git_lower_hfs, ".g\u{200c}it".as_bytes(), Error::DotGitDir); + mktest!(dot_git_mixed_hfs_simple, b".Git", Error::DotGitDir); + mktest!(dot_git_upper, b".GIT", Error::DotGitDir, NO_OPTS); + mktest!( + starts_with_dot_git_with_backslashes_on_windows, + b".git\\hooks\\pre-commit", + Error::PathSeparator + ); + mktest!(dot_git_upper_hfs, ".GIT\u{200e}".as_bytes(), Error::DotGitDir); + mktest!(dot_git_upper_ntfs_8_3, b"GIT~1", Error::DotGitDir); + mktest!(dot_git_mixed, b".gIt", Error::DotGitDir, NO_OPTS); + mktest!(dot_git_mixed_ntfs_8_3, b"gIt~1", Error::DotGitDir); + mktest!( + dot_gitmodules_mixed, + b".gItmodules", + Error::SymlinkedGitModules, + Symlink, + NO_OPTS + ); + mktest!(dot_git_mixed_hfs, "\u{206e}.gIt".as_bytes(), Error::DotGitDir); + mktest!( + dot_git_ntfs_8_3_numbers_only, + b"~1000000", + Error::SymlinkedGitModules, + Symlink, + ALL_OPTS + ); + mktest!( + dot_git_ntfs_8_3_numbers_only_too, + b"~9999999", + Error::SymlinkedGitModules, + Symlink, + ALL_OPTS + ); + mktest!( + dot_gitmodules_mixed_hfs, + "\u{206e}.gItmodules".as_bytes(), + Error::SymlinkedGitModules, + Symlink, + ALL_OPTS + ); + mktest!( + dot_gitmodules_mixed_ntfs_8_3, + b"gItMOD~1", + Error::SymlinkedGitModules, + Symlink, + ALL_OPTS + ); + mktest!( + dot_gitmodules_mixed_ntfs_stream, + b".giTmodUles:$DATA", + Error::SymlinkedGitModules, + Symlink, + ALL_OPTS + ); + mktest!( + dot_gitmodules_lower_ntfs_stream_default_implicit, + b".gitmodules::$DATA", + Error::SymlinkedGitModules, + Symlink, + ALL_OPTS + ); + mktest!( + ntfs_stream_default_implicit, + b"file::$DATA", + Error::WindowsIllegalCharacter + ); + mktest!( + ntfs_stream_explicit, + b"file:ANYTHING_REALLY:$DATA", + Error::WindowsIllegalCharacter + ); + mktest!( + dot_gitmodules_lower_ntfs_stream, + b".gitmodules:$DATA:$DATA", + Error::SymlinkedGitModules, + Symlink, + ALL_OPTS + ); + mktest!( + not_gitmodules_trailing_space, + b".gitmodules x ", + Error::WindowsIllegalCharacter + ); + mktest!( + not_gitmodules_trailing_stream, + b".gitmodules,:$DATA", + Error::WindowsIllegalCharacter + ); + mktest!(path_separator_slash_between, b"a/b", Error::PathSeparator); + mktest!(path_separator_slash_leading, b"/a", Error::PathSeparator); + mktest!(path_separator_slash_trailing, b"a/", Error::PathSeparator); + mktest!(path_separator_slash_only, b"/", Error::PathSeparator); + mktest!(slashes_on_windows, b"/", Error::PathSeparator, ALL_OPTS); + mktest!(backslashes_on_windows, b"\\", Error::PathSeparator, ALL_OPTS); + mktest!(path_separator_backslash_between, b"a\\b", Error::PathSeparator); + mktest!(path_separator_backslash_leading, b"\\a", Error::PathSeparator); + mktest!(path_separator_backslash_trailing, b"a\\", Error::PathSeparator); + mktest!(aux_mixed, b"Aux", Error::WindowsReservedName); + mktest!(aux_with_extension, b"aux.c", Error::WindowsReservedName); + mktest!(com_lower, b"com1", Error::WindowsReservedName); + mktest!(com_upper_with_extension, b"COM9.c", Error::WindowsReservedName); + mktest!(trailing_space, b"win32 ", Error::WindowsIllegalCharacter); + mktest!(trailing_dot, b"win32.", Error::WindowsIllegalCharacter); + mktest!(trailing_dot_dot, b"win32 . .", Error::WindowsIllegalCharacter); + mktest!(colon_inbetween, b"colon:separates", Error::WindowsIllegalCharacter); + mktest!(left_arrow, b"arrowright", Error::WindowsIllegalCharacter); + mktest!(apostrophe, b"a\"b", Error::WindowsIllegalCharacter); + mktest!(pipe, b"a|b", Error::WindowsIllegalCharacter); + mktest!(questionmark, b"a?b", Error::WindowsIllegalCharacter); + mktest!(asterisk, b"a*b", Error::WindowsIllegalCharacter); + mktest!(lpt_mixed_with_number, b"LPt8", Error::WindowsReservedName); + mktest!(nul_mixed, b"NuL", Error::WindowsReservedName); + mktest!(prn_mixed_with_extension, b"PrN.abc", Error::WindowsReservedName); + mktest!(con, b"CON", Error::WindowsReservedName); + mktest!(con_with_extension, b"CON.abc", Error::WindowsReservedName); + mktest!( + conout_mixed_with_extension, + b"ConOut$ .xyz", + Error::WindowsReservedName + ); + mktest!(conin_mixed, b"conIn$ ", Error::WindowsReservedName); + mktest!(drive_letters, b"c:", Error::WindowsPathPrefix, ALL_OPTS); + mktest!( + virtual_drive_letters, + "֍:".as_bytes(), + Error::WindowsPathPrefix, + ALL_OPTS + ); + mktest!(unc_path, b"\\\\?\\pictures", Error::PathSeparator, ALL_OPTS); + + #[test] + fn ntfs_gitmodules() { + for invalid in [ + ".gitmodules", + ".Gitmodules", + ".gitmoduleS", + ".gitmodules ", + ".gitmodules.", + ".gitmodules ", + ".gitmodules. ", + ".gitmodules .", + ".gitmodules..", + ".gitmodules ", + ".gitmodules. ", + ".gitmodules . ", + ".gitmodules .", + ".Gitmodules ", + ".Gitmodules.", + ".Gitmodules ", + ".Gitmodules. ", + ".Gitmodules .", + ".Gitmodules..", + ".Gitmodules ", + ".Gitmodules. ", + ".Gitmodules . ", + ".Gitmodules .", + "GITMOD~1", + "gitmod~1", + "GITMOD~2", + "giTmod~3", + "GITMOD~4", + "GITMOD~1 ", + "gitMod~2.", + "GITMOD~3 ", + "gitmod~4. ", + "GITMoD~1 .", + "gitmod~2 ", + "GITMOD~3. ", + "gitmoD~4 . ", + "GI7EBA~1", + "gi7eba~9", + "GI7EB~10", + "GI7EB~11", + "GI7EB~99", + "GI7EB~10", + "GI7E~100", + "GI7E~101", + "GI7E~999", + ".gitmodules:$DATA", + "gitmod~4 . :$DATA", + ] { + match gix_validate::path::component(invalid.into(), Some(Symlink), ALL_OPTS) { + Ok(_) => { + unreachable!("{invalid:?} should not validate successfully") + } + Err(err) => { + assert!(matches!(err, Error::SymlinkedGitModules)) + } + } + } + + for valid in [ + ".gitmodules x", + ".gitmodules .x", + " .gitmodules", + "..gitmodules", + "gitmodules", + ".gitmodule", + ".gitmodules .x", + "GI7EBA~", + "GI7EBA~0", + "GI7EBA~~1", + "GI7EBA~X", + "Gx7EBA~1", + "GI7EBX~1", + "GI7EB~1", + "GI7EB~01", + "GI7EB~1X", + ] { + gix_validate::path::component(valid.into(), Some(Symlink), ALL_OPTS) + .unwrap_or_else(|_| panic!("{valid:?} should have been valid")); + } + } + } +} diff --git a/gix-validate/tests/validate.rs b/gix-validate/tests/validate.rs index db45c4aac56..d1951c3d159 100644 --- a/gix-validate/tests/validate.rs +++ b/gix-validate/tests/validate.rs @@ -1,3 +1,4 @@ +mod path; mod reference; mod submodule; mod tag; diff --git a/gix-worktree-state/CHANGELOG.md b/gix-worktree-state/CHANGELOG.md index 483cd64acab..b3cb5b83841 100644 --- a/gix-worktree-state/CHANGELOG.md +++ b/gix-worktree-state/CHANGELOG.md @@ -5,6 +5,93 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## 0.11.0 (2024-05-22) + +### Bug Fixes + + - default to creating file-symlinks if it is dangling on Windows + This behaviour is the same as in Git. + +### Commit Statistics + + + + - 10 commits contributed to the release over the course of 33 calendar days. + - 38 days passed between releases. + - 1 commit was understood as [conventional](https://www.conventionalcommits.org). + - 2 unique issues were worked on: [#1354](https://github.com/Byron/gitoxide/issues/1354), [#1373](https://github.com/Byron/gitoxide/issues/1373) + +### Commit Details + + + +
view details + + * **[#1354](https://github.com/Byron/gitoxide/issues/1354)** + - Default to creating file-symlinks if it is dangling on Windows ([`31d02a8`](https://github.com/Byron/gitoxide/commit/31d02a89ee15a0df463a5b5c34864497f8adfae1)) + * **[#1373](https://github.com/Byron/gitoxide/issues/1373)** + - Better detection of pre-requisites for symlink test ([`00a1c47`](https://github.com/Byron/gitoxide/commit/00a1c47e7f3566feb14a4926b0cd3834f7007686)) + * **Uncategorized** + - Adjust changelogs prior to release ([`9511416`](https://github.com/Byron/gitoxide/commit/9511416a6cd0c571233f958c165329c8705c2498)) + - Merge branch 'various-fixes' ([`d6cd449`](https://github.com/Byron/gitoxide/commit/d6cd44930fb204b06e2b70fc6965e7705530c47a)) + - Merge pull request from GHSA-7w47-3wg8-547c ([`79dce79`](https://github.com/Byron/gitoxide/commit/79dce79c62f6072aa2653780d590dc3993dfa401)) + - Add tests for actual worktree checkouts to assure validations kick in ([`a6710c5`](https://github.com/Byron/gitoxide/commit/a6710c552670412cbb3d3d175c243ed086f25f33)) + - Adapt to changes in `gix-worktree` ([`1ca6a3c`](https://github.com/Byron/gitoxide/commit/1ca6a3ce22887c7eb42ec3e0a19f6e1202715745)) + - Merge branch 'status' ([`04ef31e`](https://github.com/Byron/gitoxide/commit/04ef31e9d6f5332d49037a5a4c248ebbb5aaf92b)) + - Merge branch 'cargo-fixes' ([`977346e`](https://github.com/Byron/gitoxide/commit/977346ee61de6207c66f3de003db6e8c722fb81c)) + - Release gix-index v0.32.1, gix-pathspec v0.7.4, gix-worktree v0.33.1, gix-dir v0.4.1 ([`54ac559`](https://github.com/Byron/gitoxide/commit/54ac55946bb04635cd74582a1ce2e4bee70f2e60)) +
+ +## 0.10.0 (2024-04-13) + +A maintenance release without user-facing changes. + +### Commit Statistics + + + + - 2 commits contributed to the release. + - 29 days passed between releases. + - 0 commits were understood as [conventional](https://www.conventionalcommits.org). + - 0 issues like '(#ID)' were seen in commit messages + +### Commit Details + + + +
view details + + * **Uncategorized** + - Release gix-trace v0.1.9, gix-utils v0.1.12, gix-packetline-blocking v0.17.4, gix-filter v0.11.1, gix-fs v0.10.2, gix-traverse v0.39.0, gix-worktree-stream v0.12.0, gix-archive v0.12.0, gix-config v0.36.1, gix-url v0.27.3, gix-index v0.32.0, gix-worktree v0.33.0, gix-diff v0.43.0, gix-pathspec v0.7.3, gix-dir v0.4.0, gix-pack v0.50.0, gix-odb v0.60.0, gix-transport v0.42.0, gix-protocol v0.45.0, gix-status v0.9.0, gix-worktree-state v0.10.0, gix v0.62.0, gix-fsck v0.4.0, gitoxide-core v0.37.0, gitoxide v0.35.0, safety bump 14 crates ([`095c673`](https://github.com/Byron/gitoxide/commit/095c6739b2722a8b9af90776b435ef2da454c0e6)) + - Prepare changelogs prior to release ([`5755271`](https://github.com/Byron/gitoxide/commit/57552717f46f96c35ba4ddc0a64434354ef845e9)) +
+ +## 0.9.0 (2024-03-14) + +A maintenance release without user-facing changes. + +### Commit Statistics + + + + - 4 commits contributed to the release over the course of 4 calendar days. + - 18 days passed between releases. + - 0 commits were understood as [conventional](https://www.conventionalcommits.org). + - 0 issues like '(#ID)' were seen in commit messages + +### Commit Details + + + +
view details + + * **Uncategorized** + - Release gix-date v0.8.5, gix-hash v0.14.2, gix-trace v0.1.8, gix-utils v0.1.11, gix-features v0.38.1, gix-actor v0.31.0, gix-validate v0.8.4, gix-object v0.42.0, gix-path v0.10.7, gix-glob v0.16.2, gix-quote v0.4.12, gix-attributes v0.22.2, gix-command v0.3.6, gix-filter v0.11.0, gix-fs v0.10.1, gix-chunk v0.4.8, gix-commitgraph v0.24.2, gix-hashtable v0.5.2, gix-revwalk v0.13.0, gix-traverse v0.38.0, gix-worktree-stream v0.11.0, gix-archive v0.11.0, gix-config-value v0.14.6, gix-tempfile v13.1.1, gix-lock v13.1.1, gix-ref v0.43.0, gix-sec v0.10.6, gix-config v0.36.0, gix-prompt v0.8.4, gix-url v0.27.2, gix-credentials v0.24.2, gix-ignore v0.11.2, gix-bitmap v0.2.11, gix-index v0.31.0, gix-worktree v0.32.0, gix-diff v0.42.0, gix-discover v0.31.0, gix-pathspec v0.7.1, gix-dir v0.2.0, gix-macros v0.1.4, gix-mailmap v0.23.0, gix-negotiate v0.13.0, gix-pack v0.49.0, gix-odb v0.59.0, gix-packetline v0.17.4, gix-transport v0.41.2, gix-protocol v0.44.2, gix-revision v0.27.0, gix-refspec v0.23.0, gix-status v0.7.0, gix-submodule v0.10.0, gix-worktree-state v0.9.0, gix v0.60.0, safety bump 26 crates ([`b050327`](https://github.com/Byron/gitoxide/commit/b050327e76f234b19be921b78b7b28e034319fdb)) + - Prepare changelogs prior to release ([`52c3bbd`](https://github.com/Byron/gitoxide/commit/52c3bbd36b9e94a0f3a78b4ada84d0c08eba27f6)) + - Merge branch 'status' ([`3e5c974`](https://github.com/Byron/gitoxide/commit/3e5c974dd62ac134711c6c2f5a5490187a6ea55e)) + - Fix lints for nightly, and clippy ([`f8ce3d0`](https://github.com/Byron/gitoxide/commit/f8ce3d0721b6a53713a9392f2451874f520bc44c)) +
+ ## 0.8.0 (2024-02-25) A maintenance release without user-facing changes. @@ -13,7 +100,7 @@ A maintenance release without user-facing changes. - - 4 commits contributed to the release over the course of 30 calendar days. + - 5 commits contributed to the release over the course of 30 calendar days. - 36 days passed between releases. - 0 commits were understood as [conventional](https://www.conventionalcommits.org). - 0 issues like '(#ID)' were seen in commit messages @@ -25,6 +112,7 @@ A maintenance release without user-facing changes.
view details * **Uncategorized** + - Release gix-date v0.8.4, gix-utils v0.1.10, gix-actor v0.30.1, gix-object v0.41.1, gix-path v0.10.6, gix-glob v0.16.1, gix-quote v0.4.11, gix-attributes v0.22.1, gix-command v0.3.5, gix-filter v0.10.0, gix-commitgraph v0.24.1, gix-worktree-stream v0.10.0, gix-archive v0.10.0, gix-config-value v0.14.5, gix-ref v0.42.0, gix-sec v0.10.5, gix-config v0.35.0, gix-prompt v0.8.3, gix-url v0.27.1, gix-credentials v0.24.1, gix-ignore v0.11.1, gix-index v0.30.0, gix-worktree v0.31.0, gix-diff v0.41.0, gix-discover v0.30.0, gix-pathspec v0.7.0, gix-dir v0.1.0, gix-pack v0.48.0, gix-odb v0.58.0, gix-transport v0.41.1, gix-protocol v0.44.1, gix-revision v0.26.1, gix-refspec v0.22.1, gix-status v0.6.0, gix-submodule v0.9.0, gix-worktree-state v0.8.0, gix v0.59.0, gix-fsck v0.3.0, gitoxide-core v0.36.0, gitoxide v0.34.0, safety bump 10 crates ([`45b4470`](https://github.com/Byron/gitoxide/commit/45b447045bc826f252129c300c531acde2652c64)) - Prepare changelogs prior to release ([`f2e111f`](https://github.com/Byron/gitoxide/commit/f2e111f768fc1bc6182355261c20b63610cffec7)) - Merge branch 'tempfile-permissions' ([`7b44c7f`](https://github.com/Byron/gitoxide/commit/7b44c7ff1dc0b8875214d2673c7f52948cf04ff0)) - Release gix-tempfile v13.1.0, gix-lock v13.1.0, safety bump 12 crates ([`8430442`](https://github.com/Byron/gitoxide/commit/84304427dfe4d170c7732161b126961719f70059)) diff --git a/gix-worktree-state/Cargo.toml b/gix-worktree-state/Cargo.toml index 96e8551b266..acfa8930309 100644 --- a/gix-worktree-state/Cargo.toml +++ b/gix-worktree-state/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "gix-worktree-state" -version = "0.8.0" +version = "0.11.0" repository = "https://github.com/Byron/gitoxide" license = "MIT OR Apache-2.0" description = "A crate of the gitoxide project implementing setting the worktree to a particular state" @@ -14,15 +14,15 @@ autotests = false doctest = false [dependencies] -gix-worktree = { version = "^0.31.0", path = "../gix-worktree", default-features = false, features = ["attributes"] } -gix-index = { version = "^0.30.0", path = "../gix-index" } -gix-fs = { version = "^0.10.0", path = "../gix-fs" } -gix-hash = { version = "^0.14.1", path = "../gix-hash" } -gix-object = { version = "^0.41.1", path = "../gix-object" } -gix-glob = { version = "^0.16.1", path = "../gix-glob" } -gix-path = { version = "^0.10.6", path = "../gix-path" } -gix-features = { version = "^0.38.0", path = "../gix-features" } -gix-filter = { version = "^0.10.0", path = "../gix-filter" } +gix-worktree = { version = "^0.34.0", path = "../gix-worktree", default-features = false, features = ["attributes"] } +gix-index = { version = "^0.33.0", path = "../gix-index" } +gix-fs = { version = "^0.11.1", path = "../gix-fs" } +gix-hash = { version = "^0.14.2", path = "../gix-hash" } +gix-object = { version = "^0.42.2", path = "../gix-object" } +gix-glob = { version = "^0.16.3", path = "../gix-glob" } +gix-path = { version = "^0.10.7", path = "../gix-path" } +gix-features = { version = "^0.38.2", path = "../gix-features" } +gix-filter = { version = "^0.11.2", path = "../gix-filter" } io-close = "0.3.7" thiserror = "1.0.26" diff --git a/gix-worktree-state/src/checkout/entry.rs b/gix-worktree-state/src/checkout/entry.rs index 77db18daa1e..b08563c60c1 100644 --- a/gix-worktree-state/src/checkout/entry.rs +++ b/gix-worktree-state/src/checkout/entry.rs @@ -80,8 +80,7 @@ where let dest_relative = gix_path::try_from_bstr(entry_path).map_err(|_| crate::checkout::Error::IllformedUtf8 { path: entry_path.to_owned(), })?; - let is_dir = Some(entry.mode == gix_index::entry::Mode::COMMIT || entry.mode == gix_index::entry::Mode::DIR); - let path_cache = path_cache.at_path(dest_relative, is_dir, &*objects)?; + let path_cache = path_cache.at_path(dest_relative, Some(entry.mode), &*objects)?; let dest = path_cache.path(); let object_size = match entry.mode { diff --git a/gix-worktree-state/src/checkout/function.rs b/gix-worktree-state/src/checkout/function.rs index 9046af47110..6342f34cd59 100644 --- a/gix-worktree-state/src/checkout/function.rs +++ b/gix-worktree-state/src/checkout/function.rs @@ -64,7 +64,11 @@ where path_cache: Stack::from_state_and_ignore_case( dir, options.fs.ignore_case, - stack::State::for_checkout(options.overwrite_existing, std::mem::take(&mut options.attributes)), + stack::State::for_checkout( + options.overwrite_existing, + options.validate, + std::mem::take(&mut options.attributes), + ), index, paths, ), diff --git a/gix-worktree-state/src/checkout/mod.rs b/gix-worktree-state/src/checkout/mod.rs index 0206e7e3408..57a0d3b5f2a 100644 --- a/gix-worktree-state/src/checkout/mod.rs +++ b/gix-worktree-state/src/checkout/mod.rs @@ -41,6 +41,8 @@ pub struct Outcome { pub struct Options { /// capabilities of the file system pub fs: gix_fs::Capabilities, + /// Options to configure how to validate path components. + pub validate: gix_worktree::validate::path::component::Options, /// If set, don't use more than this amount of threads. /// Otherwise, usually use as many threads as there are logical cores. /// A value of 0 is interpreted as no-limit diff --git a/gix-worktree-state/src/lib.rs b/gix-worktree-state/src/lib.rs index 2c2cf67f64d..32ab5653bc3 100644 --- a/gix-worktree-state/src/lib.rs +++ b/gix-worktree-state/src/lib.rs @@ -2,5 +2,6 @@ #![deny(missing_docs, rust_2018_idioms, unsafe_code)] /// +#[allow(clippy::empty_docs)] pub mod checkout; pub use checkout::function::checkout; diff --git a/gix-worktree-state/tests/fixtures/generated-archives/make_dangling_symlink.tar.xz b/gix-worktree-state/tests/fixtures/generated-archives/make_dangling_symlink.tar.xz new file mode 100644 index 00000000000..ffbcd148aa9 Binary files /dev/null and b/gix-worktree-state/tests/fixtures/generated-archives/make_dangling_symlink.tar.xz differ diff --git a/gix-worktree-state/tests/fixtures/generated-archives/make_traverse_trees.tar.xz b/gix-worktree-state/tests/fixtures/generated-archives/make_traverse_trees.tar.xz new file mode 100644 index 00000000000..fa5f898161f Binary files /dev/null and b/gix-worktree-state/tests/fixtures/generated-archives/make_traverse_trees.tar.xz differ diff --git a/gix-worktree-state/tests/fixtures/make_dangerous_symlink.sh b/gix-worktree-state/tests/fixtures/make_dangerous_symlink.sh index 31437285a37..31d0e3b0177 100755 --- a/gix-worktree-state/tests/fixtures/make_dangerous_symlink.sh +++ b/gix-worktree-state/tests/fixtures/make_dangerous_symlink.sh @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash set -eu -o pipefail git init -q diff --git a/gix-worktree-state/tests/fixtures/make_dangling_symlink.sh b/gix-worktree-state/tests/fixtures/make_dangling_symlink.sh new file mode 100644 index 00000000000..03010acb752 --- /dev/null +++ b/gix-worktree-state/tests/fixtures/make_dangling_symlink.sh @@ -0,0 +1,11 @@ +#!/usr/bin/env bash +set -eu -o pipefail + +git init -q + +target_oid=$(echo -n "non-existing-target" | git hash-object -w --stdin) + git update-index --index-info <<-EOF +120000 $target_oid dangling +EOF + +git commit -m "dangling symlink in index" diff --git a/gix-worktree-state/tests/fixtures/make_ignorecase_collisions.sh b/gix-worktree-state/tests/fixtures/make_ignorecase_collisions.sh index d91bd542588..7a279b41c3c 100755 --- a/gix-worktree-state/tests/fixtures/make_ignorecase_collisions.sh +++ b/gix-worktree-state/tests/fixtures/make_ignorecase_collisions.sh @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash set -eu -o pipefail git init -q diff --git a/gix-worktree-state/tests/fixtures/make_mixed.sh b/gix-worktree-state/tests/fixtures/make_mixed.sh index 5e56a43a854..8e23ccbfc30 100755 --- a/gix-worktree-state/tests/fixtures/make_mixed.sh +++ b/gix-worktree-state/tests/fixtures/make_mixed.sh @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash set -eu -o pipefail git init -q diff --git a/gix-worktree-state/tests/fixtures/make_mixed_without_submodules.sh b/gix-worktree-state/tests/fixtures/make_mixed_without_submodules.sh index 43fafbad944..78d36a2f2d0 100755 --- a/gix-worktree-state/tests/fixtures/make_mixed_without_submodules.sh +++ b/gix-worktree-state/tests/fixtures/make_mixed_without_submodules.sh @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash set -eu -o pipefail git init -q diff --git a/gix-worktree-state/tests/fixtures/make_mixed_without_submodules_and_symlinks.sh b/gix-worktree-state/tests/fixtures/make_mixed_without_submodules_and_symlinks.sh index 0e0e95ae301..77c3aea2362 100755 --- a/gix-worktree-state/tests/fixtures/make_mixed_without_submodules_and_symlinks.sh +++ b/gix-worktree-state/tests/fixtures/make_mixed_without_submodules_and_symlinks.sh @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash set -eu -o pipefail git init -q diff --git a/gix-worktree-state/tests/fixtures/make_traverse_trees.sh b/gix-worktree-state/tests/fixtures/make_traverse_trees.sh new file mode 100755 index 00000000000..af1e9c9a43a --- /dev/null +++ b/gix-worktree-state/tests/fixtures/make_traverse_trees.sh @@ -0,0 +1,39 @@ +#!/usr/bin/env bash +set -eu -o pipefail + +# Makes a repo carrying a tree structure representing the given path to a blob. +# File content is from stdin. Args are repo name, path, -x or +x, and tr sets. +function make_repo() ( + local repo="$1" path="$2" xbit="$3" set1="$4" set2="$5" + local dir dir_standin path_standin path_standin_pattern path_replacement + + git init -- "$repo" + cd -- "$repo" # Temporary, as the function body is a ( ) subshell. + + dir="${path%/*}" + dir_standin="$(tr "$set1" "$set2" <<<"$dir")" + path_standin="$(tr "$set1" "$set2" <<<"$path")" + mkdir -p -- "$dir_standin" + cat >"$path_standin" + git add --chmod="$xbit" -- "$path_standin" + path_standin_pattern="$(sed 's/[|.*^$\]/\\&/g' <<<"$path_standin")" + path_replacement="$(sed 's/[|&\]/\\&/g' <<<"$path")" + cp .git/index old_index + LC_ALL=C sed "s|$path_standin_pattern|$path_replacement|g" old_index >.git/index + git commit -m 'Initial commit' +) + +make_repo traverse_dotdot_trees '../outside' -x '.' '@' \ + <<<'A file outside the working tree, somehow.' + +make_repo traverse_dotgit_trees '.git/hooks/pre-commit' +x '.' '@' <<'EOF' +#!/bin/sh +printf 'Vulnerable!\n' +date >vulnerable +EOF + +make_repo traverse_dotgit_stream '.git::$INDEX_ALLOCATION/hooks/pre-commit' +x ':' ',' <<'EOF' +#!/bin/sh +printf 'Vulnerable!\n' +date >vulnerable +EOF diff --git a/gix-worktree-state/tests/state/checkout.rs b/gix-worktree-state/tests/state/checkout.rs index 27a1df1dd48..2a852ce7165 100644 --- a/gix-worktree-state/tests/state/checkout.rs +++ b/gix-worktree-state/tests/state/checkout.rs @@ -42,11 +42,16 @@ fn driver_exe() -> String { exe } +fn assure_is_empty(dir: impl AsRef) -> std::io::Result<()> { + assert_eq!(std::fs::read_dir(dir)?.count(), 0); + Ok(()) +} + #[test] fn submodules_are_instantiated_as_directories() -> crate::Result { let mut opts = opts_from_probe(); opts.overwrite_existing = false; - let (_source_tree, destination, _index, _outcome) = checkout_index_in_tmp_dir(opts.clone(), "make_mixed")?; + let (_source_tree, destination, _index, _outcome) = checkout_index_in_tmp_dir(opts.clone(), "make_mixed", None)?; for path in ["m1", "modules/m1"] { let sm = destination.path().join(path); @@ -57,18 +62,13 @@ fn submodules_are_instantiated_as_directories() -> crate::Result { Ok(()) } -fn assure_is_empty(dir: impl AsRef) -> std::io::Result<()> { - assert_eq!(std::fs::read_dir(dir)?.count(), 0); - Ok(()) -} - #[test] fn accidental_writes_through_symlinks_are_prevented_if_overwriting_is_forbidden() { let mut opts = opts_from_probe(); // without overwrite mode, everything is safe. opts.overwrite_existing = false; let (source_tree, destination, _index, outcome) = - checkout_index_in_tmp_dir(opts.clone(), "make_dangerous_symlink").unwrap(); + checkout_index_in_tmp_dir(opts.clone(), "make_dangerous_symlink", None).unwrap(); let source_files = dir_structure(&source_tree); let worktree_files = dir_structure(&destination); @@ -109,7 +109,7 @@ fn writes_through_symlinks_are_prevented_even_if_overwriting_is_allowed() { // with overwrite mode opts.overwrite_existing = true; let (source_tree, destination, _index, outcome) = - checkout_index_in_tmp_dir(opts.clone(), "make_dangerous_symlink").unwrap(); + checkout_index_in_tmp_dir(opts.clone(), "make_dangerous_symlink", None).unwrap(); let source_files = dir_structure(&source_tree); let worktree_files = dir_structure(&destination); @@ -125,7 +125,7 @@ fn writes_through_symlinks_are_prevented_even_if_overwriting_is_allowed() { if cfg!(windows) { "A-dir\\a" } else { "A-dir/a" }, "A-file", "FAKE-DIR", - if cfg!(windows) { "fake-file" } else { "FAKE-FILE" } + "FAKE-FILE" ]), ); assert!(outcome.collisions.is_empty()); @@ -144,8 +144,13 @@ fn delayed_driver_process() -> crate::Result { opts.filter_process_delay = gix_filter::driver::apply::Delay::Allow; opts.destination_is_initially_empty = false; setup_filter_pipeline(opts.filters.options_mut()); - let (_source, destination, _index, outcome) = - checkout_index_in_tmp_dir_opts(opts, "make_mixed_without_submodules_and_symlinks", |_| true, |_| Ok(()))?; + let (_source, destination, _index, outcome) = checkout_index_in_tmp_dir_opts( + opts, + "make_mixed_without_submodules_and_symlinks", + None, + |_| true, + |_| Ok(()), + )?; assert_eq!(outcome.collisions.len(), 0); assert_eq!(outcome.errors.len(), 0); assert_eq!(outcome.files_updated, 5); @@ -178,6 +183,10 @@ fn overwriting_files_and_lone_directories_works() -> crate::Result { gix_filter::driver::apply::Delay::Forbid, ] { let mut opts = opts_from_probe(); + assert!( + opts.fs.symlink, + "BUG: the probe must detect to be able to generate symlinks" + ); opts.overwrite_existing = true; opts.filter_process_delay = delay; opts.destination_is_initially_empty = false; @@ -185,6 +194,7 @@ fn overwriting_files_and_lone_directories_works() -> crate::Result { let (source, destination, _index, outcome) = checkout_index_in_tmp_dir_opts( opts.clone(), "make_mixed", + None, |_| true, |d| { let empty = d.join("empty"); @@ -197,7 +207,7 @@ fn overwriting_files_and_lone_directories_works() -> crate::Result { let dir = dir.join("sub-dir"); std::fs::create_dir(&dir)?; - symlink::symlink_dir(empty, dir.join("symlink"))?; // 'symlink' is a symlink to another file + symlink::symlink_dir(empty, dir.join("symlink"))?; // 'symlink' is a symlink to a directory. Ok(()) }, )?; @@ -250,20 +260,44 @@ fn symlinks_become_files_if_disabled() -> crate::Result { let mut opts = opts_from_probe(); opts.fs.symlink = false; let (source_tree, destination, _index, outcome) = - checkout_index_in_tmp_dir(opts.clone(), "make_mixed_without_submodules")?; + checkout_index_in_tmp_dir(opts.clone(), "make_mixed_without_submodules", None)?; assert_equality(&source_tree, &destination, opts.fs.symlink)?; assert!(outcome.collisions.is_empty()); Ok(()) } +#[test] +fn dangling_symlinks_can_be_created() -> crate::Result { + let opts = opts_from_probe(); + if !opts.fs.symlink { + eprintln!("Skipping dangling symlink test on filesystem that doesn't support it"); + return Ok(()); + } + + let (_source_tree, destination, _index, outcome) = + checkout_index_in_tmp_dir(opts.clone(), "make_dangling_symlink", None)?; + let worktree_files = dir_structure(&destination); + let worktree_files_stripped = stripped_prefix(&destination, &worktree_files); + + assert_eq!(worktree_files_stripped, paths(["dangling"])); + let symlink_path = &worktree_files[0]; + assert!(symlink_path + .symlink_metadata() + .expect("dangling symlink is on disk") + .is_symlink()); + assert_eq!(std::fs::read_link(symlink_path)?, Path::new("non-existing-target")); + assert!(outcome.collisions.is_empty()); + Ok(()) +} + #[test] fn allow_or_disallow_symlinks() -> crate::Result { let mut opts = opts_from_probe(); for allowed in &[false, true] { opts.fs.symlink = *allowed; let (source_tree, destination, _index, outcome) = - checkout_index_in_tmp_dir(opts.clone(), "make_mixed_without_submodules")?; + checkout_index_in_tmp_dir(opts.clone(), "make_mixed_without_submodules", None)?; assert_equality(&source_tree, &destination, opts.fs.symlink)?; assert!(outcome.collisions.is_empty()); @@ -279,6 +313,7 @@ fn keep_going_collects_results() { let (_source_tree, destination, _index, outcome) = checkout_index_in_tmp_dir_opts( opts, "make_mixed_without_submodules", + None, |_id| { count .fetch_update(Ordering::SeqCst, Ordering::SeqCst, |current| { @@ -303,12 +338,7 @@ fn keep_going_collects_results() { .iter() .map(|r| r.path.to_path_lossy().into_owned()) .collect::>(), - paths(if cfg!(unix) { - [".gitattributes", "dir/content"] - } else { - // not actually a symlink anymore, even though symlinks are supported but git think differently. - ["dir/content", "dir/sub-dir/symlink"] - }) + paths([".gitattributes", "dir/content"]) ); } @@ -322,11 +352,15 @@ fn keep_going_collects_results() { } else { assert_eq!( stripped_prefix(&destination, &dir_structure(&destination)), - paths(if cfg!(unix) { - Box::new(["dir/sub-dir/symlink", "empty", "executable"].into_iter()) as Box> - } else { - Box::new(["empty", "executable"].into_iter()) - }), + paths([ + if cfg!(unix) { + "dir/sub-dir/symlink" + } else { + "dir\\sub-dir\\symlink" + }, + "empty", + "executable", + ]), "some files could not be created" ); } @@ -342,7 +376,7 @@ fn no_case_related_collisions_on_case_sensitive_filesystem() { return; } let (source_tree, destination, index, outcome) = - checkout_index_in_tmp_dir(opts.clone(), "make_ignorecase_collisions").unwrap(); + checkout_index_in_tmp_dir(opts.clone(), "make_ignorecase_collisions", None).unwrap(); assert!(outcome.collisions.is_empty()); let num_files = assert_equality(&source_tree, &destination, opts.fs.symlink).unwrap(); @@ -357,6 +391,48 @@ fn no_case_related_collisions_on_case_sensitive_filesystem() { ); } +#[test] +fn safety_checks_dotdot_trees() { + let mut opts = opts_from_probe(); + let err = + checkout_index_in_tmp_dir(opts.clone(), "make_traverse_trees", Some("traverse_dotdot_trees")).unwrap_err(); + let expected_err_msg = "Input path \"../outside\" contains relative or absolute components"; + assert_eq!(err.source().expect("inner").to_string(), expected_err_msg); + + opts.keep_going = true; + let (_source_tree, _destination, _index, outcome) = + checkout_index_in_tmp_dir(opts, "make_traverse_trees", Some("traverse_dotdot_trees")) + .expect("keep-going checks out as much as possible"); + assert_eq!(outcome.errors.len(), 1, "one path could not be checked out"); + assert_eq!( + outcome.errors[0].error.source().expect("inner").to_string(), + expected_err_msg + ); +} + +#[test] +fn safety_checks_dotgit_trees() { + let opts = opts_from_probe(); + let err = + checkout_index_in_tmp_dir(opts.clone(), "make_traverse_trees", Some("traverse_dotgit_trees")).unwrap_err(); + assert_eq!( + err.source().expect("inner").to_string(), + "The .git name may never be used" + ); +} + +#[test] +fn safety_checks_dotgit_ntfs_stream() { + let opts = opts_from_probe(); + let err = + checkout_index_in_tmp_dir(opts.clone(), "make_traverse_trees", Some("traverse_dotgit_stream")).unwrap_err(); + assert_eq!( + err.source().expect("inner").to_string(), + "The .git name may never be used", + "note how it is still discovered even though the path is `.git::$INDEX_ALLOCATION`" + ); +} + #[test] fn collisions_are_detected_on_a_case_insensitive_filesystem_even_with_delayed_filters() { let mut opts = opts_from_probe(); @@ -367,7 +443,7 @@ fn collisions_are_detected_on_a_case_insensitive_filesystem_even_with_delayed_fi setup_filter_pipeline(opts.filters.options_mut()); opts.filter_process_delay = gix_filter::driver::apply::Delay::Allow; let (source_tree, destination, _index, outcome) = - checkout_index_in_tmp_dir(opts, "make_ignorecase_collisions").unwrap(); + checkout_index_in_tmp_dir(opts, "make_ignorecase_collisions", None).unwrap(); let source_files = dir_structure(&source_tree); assert_eq!( @@ -479,17 +555,26 @@ pub fn dir_structure>(path: P) -> Vec, ) -> crate::Result<(PathBuf, TempDir, gix_index::File, gix_worktree_state::checkout::Outcome)> { - checkout_index_in_tmp_dir_opts(opts, name, |_d| true, |_| Ok(())) + checkout_index_in_tmp_dir_opts(opts, name, subdir_name, |_d| true, |_| Ok(())) } fn checkout_index_in_tmp_dir_opts( opts: gix_worktree_state::checkout::Options, - name: &str, + script_name: &str, + subdir_name: Option<&str>, allow_return_object: impl FnMut(&gix_hash::oid) -> bool + Send + Clone, prep_dest: impl Fn(&Path) -> std::io::Result<()>, ) -> crate::Result<(PathBuf, TempDir, gix_index::File, gix_worktree_state::checkout::Outcome)> { - let source_tree = fixture_path(name); + let source_tree = { + let root = fixture_path(script_name); + if let Some(name) = subdir_name { + root.join(name) + } else { + root + } + }; let git_dir = source_tree.join(".git"); let mut index = gix_index::File::at(git_dir.join("index"), gix_hash::Kind::Sha1, false, Default::default())?; let odb = gix_odb::at(git_dir.join("objects"))?.into_inner().into_arc()?; @@ -550,8 +635,10 @@ fn probe_gitoxide_dir() -> crate::Result { } fn opts_from_probe() -> gix_worktree_state::checkout::Options { + static CAPABILITIES: Lazy = Lazy::new(|| probe_gitoxide_dir().unwrap()); + gix_worktree_state::checkout::Options { - fs: probe_gitoxide_dir().unwrap(), + fs: *CAPABILITIES, destination_is_initially_empty: true, thread_limit: gix_features::parallel::num_threads(None).into(), ..Default::default() diff --git a/gix-worktree-stream/CHANGELOG.md b/gix-worktree-stream/CHANGELOG.md index ce2efd33d37..afb22f7173e 100644 --- a/gix-worktree-stream/CHANGELOG.md +++ b/gix-worktree-stream/CHANGELOG.md @@ -5,6 +5,82 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## 0.13.0 (2024-05-22) + +A maintenance release without user-facing changes. + +### Commit Statistics + + + + - 4 commits contributed to the release over the course of 3 calendar days. + - 38 days passed between releases. + - 0 commits were understood as [conventional](https://www.conventionalcommits.org). + - 0 issues like '(#ID)' were seen in commit messages + +### Commit Details + + + +
view details + + * **Uncategorized** + - Adjust changelogs prior to release ([`9511416`](https://github.com/Byron/gitoxide/commit/9511416a6cd0c571233f958c165329c8705c2498)) + - Merge branch 'various-fixes' ([`d6cd449`](https://github.com/Byron/gitoxide/commit/d6cd44930fb204b06e2b70fc6965e7705530c47a)) + - Merge pull request from GHSA-7w47-3wg8-547c ([`79dce79`](https://github.com/Byron/gitoxide/commit/79dce79c62f6072aa2653780d590dc3993dfa401)) + - Adapt to changes in `gix-worktree` ([`1ca6a3c`](https://github.com/Byron/gitoxide/commit/1ca6a3ce22887c7eb42ec3e0a19f6e1202715745)) +
+ +## 0.12.0 (2024-04-13) + +A maintenance release without user-facing changes. + +### Commit Statistics + + + + - 2 commits contributed to the release. + - 29 days passed between releases. + - 0 commits were understood as [conventional](https://www.conventionalcommits.org). + - 0 issues like '(#ID)' were seen in commit messages + +### Commit Details + + + +
view details + + * **Uncategorized** + - Release gix-trace v0.1.9, gix-utils v0.1.12, gix-packetline-blocking v0.17.4, gix-filter v0.11.1, gix-fs v0.10.2, gix-traverse v0.39.0, gix-worktree-stream v0.12.0, gix-archive v0.12.0, gix-config v0.36.1, gix-url v0.27.3, gix-index v0.32.0, gix-worktree v0.33.0, gix-diff v0.43.0, gix-pathspec v0.7.3, gix-dir v0.4.0, gix-pack v0.50.0, gix-odb v0.60.0, gix-transport v0.42.0, gix-protocol v0.45.0, gix-status v0.9.0, gix-worktree-state v0.10.0, gix v0.62.0, gix-fsck v0.4.0, gitoxide-core v0.37.0, gitoxide v0.35.0, safety bump 14 crates ([`095c673`](https://github.com/Byron/gitoxide/commit/095c6739b2722a8b9af90776b435ef2da454c0e6)) + - Prepare changelogs prior to release ([`5755271`](https://github.com/Byron/gitoxide/commit/57552717f46f96c35ba4ddc0a64434354ef845e9)) +
+ +## 0.11.0 (2024-03-14) + +A maintenance release without user-facing changes. + +### Commit Statistics + + + + - 4 commits contributed to the release over the course of 4 calendar days. + - 18 days passed between releases. + - 0 commits were understood as [conventional](https://www.conventionalcommits.org). + - 0 issues like '(#ID)' were seen in commit messages + +### Commit Details + + + +
view details + + * **Uncategorized** + - Release gix-date v0.8.5, gix-hash v0.14.2, gix-trace v0.1.8, gix-utils v0.1.11, gix-features v0.38.1, gix-actor v0.31.0, gix-validate v0.8.4, gix-object v0.42.0, gix-path v0.10.7, gix-glob v0.16.2, gix-quote v0.4.12, gix-attributes v0.22.2, gix-command v0.3.6, gix-filter v0.11.0, gix-fs v0.10.1, gix-chunk v0.4.8, gix-commitgraph v0.24.2, gix-hashtable v0.5.2, gix-revwalk v0.13.0, gix-traverse v0.38.0, gix-worktree-stream v0.11.0, gix-archive v0.11.0, gix-config-value v0.14.6, gix-tempfile v13.1.1, gix-lock v13.1.1, gix-ref v0.43.0, gix-sec v0.10.6, gix-config v0.36.0, gix-prompt v0.8.4, gix-url v0.27.2, gix-credentials v0.24.2, gix-ignore v0.11.2, gix-bitmap v0.2.11, gix-index v0.31.0, gix-worktree v0.32.0, gix-diff v0.42.0, gix-discover v0.31.0, gix-pathspec v0.7.1, gix-dir v0.2.0, gix-macros v0.1.4, gix-mailmap v0.23.0, gix-negotiate v0.13.0, gix-pack v0.49.0, gix-odb v0.59.0, gix-packetline v0.17.4, gix-transport v0.41.2, gix-protocol v0.44.2, gix-revision v0.27.0, gix-refspec v0.23.0, gix-status v0.7.0, gix-submodule v0.10.0, gix-worktree-state v0.9.0, gix v0.60.0, safety bump 26 crates ([`b050327`](https://github.com/Byron/gitoxide/commit/b050327e76f234b19be921b78b7b28e034319fdb)) + - Prepare changelogs prior to release ([`52c3bbd`](https://github.com/Byron/gitoxide/commit/52c3bbd36b9e94a0f3a78b4ada84d0c08eba27f6)) + - Merge branch 'status' ([`3e5c974`](https://github.com/Byron/gitoxide/commit/3e5c974dd62ac134711c6c2f5a5490187a6ea55e)) + - Fix lints for nightly, and clippy ([`f8ce3d0`](https://github.com/Byron/gitoxide/commit/f8ce3d0721b6a53713a9392f2451874f520bc44c)) +
+ ## 0.10.0 (2024-02-25) A maintenance release without user-facing changes. @@ -13,7 +89,7 @@ A maintenance release without user-facing changes. - - 4 commits contributed to the release over the course of 30 calendar days. + - 5 commits contributed to the release over the course of 30 calendar days. - 36 days passed between releases. - 0 commits were understood as [conventional](https://www.conventionalcommits.org). - 0 issues like '(#ID)' were seen in commit messages @@ -25,6 +101,7 @@ A maintenance release without user-facing changes.
view details * **Uncategorized** + - Release gix-date v0.8.4, gix-utils v0.1.10, gix-actor v0.30.1, gix-object v0.41.1, gix-path v0.10.6, gix-glob v0.16.1, gix-quote v0.4.11, gix-attributes v0.22.1, gix-command v0.3.5, gix-filter v0.10.0, gix-commitgraph v0.24.1, gix-worktree-stream v0.10.0, gix-archive v0.10.0, gix-config-value v0.14.5, gix-ref v0.42.0, gix-sec v0.10.5, gix-config v0.35.0, gix-prompt v0.8.3, gix-url v0.27.1, gix-credentials v0.24.1, gix-ignore v0.11.1, gix-index v0.30.0, gix-worktree v0.31.0, gix-diff v0.41.0, gix-discover v0.30.0, gix-pathspec v0.7.0, gix-dir v0.1.0, gix-pack v0.48.0, gix-odb v0.58.0, gix-transport v0.41.1, gix-protocol v0.44.1, gix-revision v0.26.1, gix-refspec v0.22.1, gix-status v0.6.0, gix-submodule v0.9.0, gix-worktree-state v0.8.0, gix v0.59.0, gix-fsck v0.3.0, gitoxide-core v0.36.0, gitoxide v0.34.0, safety bump 10 crates ([`45b4470`](https://github.com/Byron/gitoxide/commit/45b447045bc826f252129c300c531acde2652c64)) - Prepare changelogs prior to release ([`f2e111f`](https://github.com/Byron/gitoxide/commit/f2e111f768fc1bc6182355261c20b63610cffec7)) - Merge branch 'entryoom' ([`684fa5c`](https://github.com/Byron/gitoxide/commit/684fa5caf82fc38dd238361d6482e77901ca0265)) - Handle OOM when copying to buffers ([`0be338f`](https://github.com/Byron/gitoxide/commit/0be338fdf80a11877599545cfa1b215092ce9afb)) diff --git a/gix-worktree-stream/Cargo.toml b/gix-worktree-stream/Cargo.toml index d4e72276d4c..82a3ca849a3 100644 --- a/gix-worktree-stream/Cargo.toml +++ b/gix-worktree-stream/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "gix-worktree-stream" -version = "0.10.0" +version = "0.13.0" repository = "https://github.com/Byron/gitoxide" license = "MIT OR Apache-2.0" description = "generate a byte-stream from a git-tree" @@ -13,14 +13,14 @@ include = ["src/**/*", "LICENSE-*"] doctest = false [dependencies] -gix-features = { version = "^0.38.0", path = "../gix-features", features = ["progress", "io-pipe"] } -gix-hash = { version = "^0.14.1", path = "../gix-hash" } -gix-object = { version = "^0.41.1", path = "../gix-object" } -gix-attributes = { version = "^0.22.1", path = "../gix-attributes" } -gix-filter = { version = "^0.10.0", path = "../gix-filter" } -gix-traverse = { version = "^0.37.0", path = "../gix-traverse" } -gix-fs = { version = "^0.10.0", path = "../gix-fs" } -gix-path = { version = "^0.10.6", path = "../gix-path" } +gix-features = { version = "^0.38.2", path = "../gix-features", features = ["progress", "io-pipe"] } +gix-hash = { version = "^0.14.2", path = "../gix-hash" } +gix-object = { version = "^0.42.2", path = "../gix-object" } +gix-attributes = { version = "^0.22.2", path = "../gix-attributes" } +gix-filter = { version = "^0.11.2", path = "../gix-filter" } +gix-traverse = { version = "^0.39.1", path = "../gix-traverse" } +gix-fs = { version = "^0.11.1", path = "../gix-fs" } +gix-path = { version = "^0.10.7", path = "../gix-path" } thiserror = "1.0.26" parking_lot = "0.12.1" diff --git a/gix-worktree-stream/src/lib.rs b/gix-worktree-stream/src/lib.rs index 8169f888d46..82c57b809cb 100644 --- a/gix-worktree-stream/src/lib.rs +++ b/gix-worktree-stream/src/lib.rs @@ -26,6 +26,7 @@ pub struct Stream { } /// +#[allow(clippy::empty_docs)] pub mod entry; pub(crate) mod protocol; diff --git a/gix-worktree-stream/tests/fixtures/basic.sh b/gix-worktree-stream/tests/fixtures/basic.sh index 2cdc44653b2..1a29eb68a8b 100755 --- a/gix-worktree-stream/tests/fixtures/basic.sh +++ b/gix-worktree-stream/tests/fixtures/basic.sh @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash set -eu -o pipefail git init diff --git a/gix-worktree-stream/tests/stream.rs b/gix-worktree-stream/tests/stream.rs index 3c2964e1918..56a211d5600 100644 --- a/gix-worktree-stream/tests/stream.rs +++ b/gix-worktree-stream/tests/stream.rs @@ -67,7 +67,7 @@ mod from_tree { mutating_pipeline(true), move |rela_path, mode, attrs| { cache - .at_entry(rela_path, mode.is_tree().into(), &odb) + .at_entry(rela_path, Some(mode.into()), &odb) .map(|entry| entry.matching_attributes(attrs)) .map(|_| ()) }, @@ -225,7 +225,7 @@ mod from_tree { mutating_pipeline(false), move |rela_path, mode, attrs| { cache - .at_entry(rela_path, mode.is_tree().into(), &odb) + .at_entry(rela_path, Some(mode.into()), &odb) .map(|entry| entry.matching_attributes(attrs)) .map(|_| ()) }, diff --git a/gix-worktree/CHANGELOG.md b/gix-worktree/CHANGELOG.md index 3ce1b3437ea..30d502f2ac9 100644 --- a/gix-worktree/CHANGELOG.md +++ b/gix-worktree/CHANGELOG.md @@ -5,6 +5,158 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## 0.34.0 (2024-05-22) + +### New Features (BREAKING) + + - `Stack::at_path()` replaces `is_dir` parameter with `mode`. + That way, detailed information about the path-to-be is available not + only for evaluating attributes or excludes, but also for validating + path components (in this case, relevant for `.gitmodules`). + +### Bug Fixes (BREAKING) + + - validate all components pushed onto the stack when creating leading paths. + This way, everyone using the stack with the purpose of + altering the working tree will run additional checks to prevent callers + from sneaking in forbidden paths. + + Note that these checks don't run otherwise, so one has to be careful + to not forget to run these checks whenever needed. + +### Commit Statistics + + + + - 30 commits contributed to the release over the course of 33 calendar days. + - 33 days passed between releases. + - 2 commits were understood as [conventional](https://www.conventionalcommits.org). + - 0 issues like '(#ID)' were seen in commit messages + +### Thanks Clippy + + + +[Clippy](https://github.com/rust-lang/rust-clippy) helped 1 time to make code idiomatic. + +### Commit Details + + + +
view details + + * **Uncategorized** + - Adjust changelogs prior to release ([`9511416`](https://github.com/Byron/gitoxide/commit/9511416a6cd0c571233f958c165329c8705c2498)) + - Merge branch 'various-fixes' ([`d6cd449`](https://github.com/Byron/gitoxide/commit/d6cd44930fb204b06e2b70fc6965e7705530c47a)) + - Merge pull request from GHSA-7w47-3wg8-547c ([`79dce79`](https://github.com/Byron/gitoxide/commit/79dce79c62f6072aa2653780d590dc3993dfa401)) + - Add tests for actual worktree checkouts to assure validations kick in ([`a6710c5`](https://github.com/Byron/gitoxide/commit/a6710c552670412cbb3d3d175c243ed086f25f33)) + - Make more test repos with traversal-attempting blob names ([`f3edaa3`](https://github.com/Byron/gitoxide/commit/f3edaa352ab266de2d24b7b71133bcc17ee661b3)) + - Combine non-"slashes" (i.e. trees) scripts and make it a fixture ([`6f44aca`](https://github.com/Byron/gitoxide/commit/6f44aca4dc1e04f082f5d6c6bdf0b11df28d3a28)) + - Combine "slashes" scripts and make it a fixture ([`7e9c769`](https://github.com/Byron/gitoxide/commit/7e9c76993a72f1d982cb1c1f73dcd42afe3ec6d2)) + - Adjust make_traverse_dotdot_slashes.sh for environment ([`fe8c2c9`](https://github.com/Byron/gitoxide/commit/fe8c2c939db69ffce855059e2b16be50efcc05e6)) + - Start on demo script making repo with ../… filename ([`4c684ca`](https://github.com/Byron/gitoxide/commit/4c684cae998d757eb5013825a1bc62bb46122f2a)) + - Pass --literally to hash-object when making tree ([`6846c90`](https://github.com/Byron/gitoxide/commit/6846c90efbc6fa861709b719ab2629818c4b1fee)) + - Reword to be more portable and self-documenting ([`89ee180`](https://github.com/Byron/gitoxide/commit/89ee1806c91692af9266fb9fad6dd1b235292dad)) + - Split into commented sections ([`9436f3f`](https://github.com/Byron/gitoxide/commit/9436f3f498bbba6bc781a17033ccfcceb45a5721)) + - Show the new commit, once made and on the branch ([`981cf5b`](https://github.com/Byron/gitoxide/commit/981cf5b944dff07ea59c89e5454614876e0bc831)) + - Start on demo script making repo with .git/… filename ([`7daca49`](https://github.com/Byron/gitoxide/commit/7daca4924f33b40df9437c339ca090996e8f9b0d)) + - Use .git::$INDEX_ALLOCATION instead of .git:$I30 ([`7041e73`](https://github.com/Byron/gitoxide/commit/7041e73d21f5be06308a51650ded012cbc5675c2)) + - Start on demo script making repo with NTFS stream ([`49eb14c`](https://github.com/Byron/gitoxide/commit/49eb14cc94fde0924490917385a79ceb97cf57f1)) + - Stage and set mode in one step instead of two ([`a59c05a`](https://github.com/Byron/gitoxide/commit/a59c05aa8c6de6f83a9b9ae11eba5f9b42f045c5)) + - Don't require the filesystem that makes the repo to support +x ([`0581966`](https://github.com/Byron/gitoxide/commit/0581966a9a1ea7a143a8557a9f83fa28a51972c0)) + - Don't bother running `git show --stat` ([`845c6bc`](https://github.com/Byron/gitoxide/commit/845c6bc34e00f4d0c830bb9900b96d075cf6ce9d)) + - No need to actually create the directories ([`0d15e5c`](https://github.com/Byron/gitoxide/commit/0d15e5c561a3aaf2eac439b022b95e5b0ebdde51)) + - Set LC_ALL=C when using sed on a binary file ([`9180dde`](https://github.com/Byron/gitoxide/commit/9180dde2a3a173f0cab3f34440066b6c9db26e52)) + - Make the script more robust, and don't require `ex` ([`474bf0d`](https://github.com/Byron/gitoxide/commit/474bf0dc6efae8c939b963a47c8139cf5710a617)) + - Add missing executable bit to payloads ([`4e3b77d`](https://github.com/Byron/gitoxide/commit/4e3b77d07ea8ada921a40cc256a2cd1c02423c36)) + - Hard-code target to fix remaining replacement bugs ([`bf49d73`](https://github.com/Byron/gitoxide/commit/bf49d7328e46b0e94625276e272cca9a495d6707)) + - Start on demo script making repo with .. trees, deploying above repo ([`7fa0185`](https://github.com/Byron/gitoxide/commit/7fa0185e7dc3f250255c47f105e7a8a33bb43180)) + - Thanks clippy ([`1076375`](https://github.com/Byron/gitoxide/commit/1076375571c493fe4f2cd512b28bb4e28d365292)) + - `Stack::at_path()` replaces `is_dir` parameter with `mode`. ([`595fe87`](https://github.com/Byron/gitoxide/commit/595fe877455824ee1f079976b61d4a5bad74383d)) + - Validate all components pushed onto the stack when creating leading paths. ([`874cfd6`](https://github.com/Byron/gitoxide/commit/874cfd6dd7e371f178ec5f63368220b272608805)) + - Add validation for path components and tree-names ([`0d78db2`](https://github.com/Byron/gitoxide/commit/0d78db2440c3866bfa972c8773aa7d8e7b245f2e)) + - Merge branch 'cargo-fixes' ([`977346e`](https://github.com/Byron/gitoxide/commit/977346ee61de6207c66f3de003db6e8c722fb81c)) +
+ +## 0.33.1 (2024-04-18) + +A maintenance release without user-facing changes. + +### Commit Statistics + + + + - 4 commits contributed to the release over the course of 4 calendar days. + - 5 days passed between releases. + - 0 commits were understood as [conventional](https://www.conventionalcommits.org). + - 0 issues like '(#ID)' were seen in commit messages + +### Commit Details + + + +
view details + + * **Uncategorized** + - Release gix-index v0.32.1, gix-pathspec v0.7.4, gix-worktree v0.33.1, gix-dir v0.4.1 ([`54ac559`](https://github.com/Byron/gitoxide/commit/54ac55946bb04635cd74582a1ce2e4bee70f2e60)) + - Prepare changelog prior to `gix-dir` patch release ([`6ca6fa6`](https://github.com/Byron/gitoxide/commit/6ca6fa69b5c21c8d8e9e07e21558e98201504cda)) + - Merge pull request #1345 from EliahKagan/shell-scripts ([`fe24c89`](https://github.com/Byron/gitoxide/commit/fe24c89e326670deaa3aaa643276d612d866072e)) + - Add missing +x bit on scripts that are run and not sourced ([`41bf65a`](https://github.com/Byron/gitoxide/commit/41bf65adef6f7d2cdd28fede262173ec7ba10822)) +
+ +## 0.33.0 (2024-04-13) + +A maintenance release without user-facing changes. + +### Commit Statistics + + + + - 5 commits contributed to the release over the course of 9 calendar days. + - 29 days passed between releases. + - 0 commits were understood as [conventional](https://www.conventionalcommits.org). + - 0 issues like '(#ID)' were seen in commit messages + +### Commit Details + + + +
view details + + * **Uncategorized** + - Release gix-trace v0.1.9, gix-utils v0.1.12, gix-packetline-blocking v0.17.4, gix-filter v0.11.1, gix-fs v0.10.2, gix-traverse v0.39.0, gix-worktree-stream v0.12.0, gix-archive v0.12.0, gix-config v0.36.1, gix-url v0.27.3, gix-index v0.32.0, gix-worktree v0.33.0, gix-diff v0.43.0, gix-pathspec v0.7.3, gix-dir v0.4.0, gix-pack v0.50.0, gix-odb v0.60.0, gix-transport v0.42.0, gix-protocol v0.45.0, gix-status v0.9.0, gix-worktree-state v0.10.0, gix v0.62.0, gix-fsck v0.4.0, gitoxide-core v0.37.0, gitoxide v0.35.0, safety bump 14 crates ([`095c673`](https://github.com/Byron/gitoxide/commit/095c6739b2722a8b9af90776b435ef2da454c0e6)) + - Prepare changelogs prior to release ([`5755271`](https://github.com/Byron/gitoxide/commit/57552717f46f96c35ba4ddc0a64434354ef845e9)) + - Merge branch 'status' ([`45edd2e`](https://github.com/Byron/gitoxide/commit/45edd2ea66035adf526cb2f617873dcba60a2a9a)) + - Make it easier to discover `is_path_excluded()` in documentation ([`c136329`](https://github.com/Byron/gitoxide/commit/c13632959e287f31a00c1ba8fc6e97470f0cd734)) + - Adapt to changes in `gix-index` ([`1e1fce1`](https://github.com/Byron/gitoxide/commit/1e1fce11a968ebbcede1135ccbd0b03e749a1267)) +
+ +## 0.32.0 (2024-03-14) + +A maintenance release without user-facing changes. + +### Commit Statistics + + + + - 4 commits contributed to the release over the course of 4 calendar days. + - 18 days passed between releases. + - 0 commits were understood as [conventional](https://www.conventionalcommits.org). + - 0 issues like '(#ID)' were seen in commit messages + +### Commit Details + + + +
view details + + * **Uncategorized** + - Release gix-date v0.8.5, gix-hash v0.14.2, gix-trace v0.1.8, gix-utils v0.1.11, gix-features v0.38.1, gix-actor v0.31.0, gix-validate v0.8.4, gix-object v0.42.0, gix-path v0.10.7, gix-glob v0.16.2, gix-quote v0.4.12, gix-attributes v0.22.2, gix-command v0.3.6, gix-filter v0.11.0, gix-fs v0.10.1, gix-chunk v0.4.8, gix-commitgraph v0.24.2, gix-hashtable v0.5.2, gix-revwalk v0.13.0, gix-traverse v0.38.0, gix-worktree-stream v0.11.0, gix-archive v0.11.0, gix-config-value v0.14.6, gix-tempfile v13.1.1, gix-lock v13.1.1, gix-ref v0.43.0, gix-sec v0.10.6, gix-config v0.36.0, gix-prompt v0.8.4, gix-url v0.27.2, gix-credentials v0.24.2, gix-ignore v0.11.2, gix-bitmap v0.2.11, gix-index v0.31.0, gix-worktree v0.32.0, gix-diff v0.42.0, gix-discover v0.31.0, gix-pathspec v0.7.1, gix-dir v0.2.0, gix-macros v0.1.4, gix-mailmap v0.23.0, gix-negotiate v0.13.0, gix-pack v0.49.0, gix-odb v0.59.0, gix-packetline v0.17.4, gix-transport v0.41.2, gix-protocol v0.44.2, gix-revision v0.27.0, gix-refspec v0.23.0, gix-status v0.7.0, gix-submodule v0.10.0, gix-worktree-state v0.9.0, gix v0.60.0, safety bump 26 crates ([`b050327`](https://github.com/Byron/gitoxide/commit/b050327e76f234b19be921b78b7b28e034319fdb)) + - Prepare changelogs prior to release ([`52c3bbd`](https://github.com/Byron/gitoxide/commit/52c3bbd36b9e94a0f3a78b4ada84d0c08eba27f6)) + - Merge branch 'status' ([`3e5c974`](https://github.com/Byron/gitoxide/commit/3e5c974dd62ac134711c6c2f5a5490187a6ea55e)) + - Fix lints for nightly, and clippy ([`f8ce3d0`](https://github.com/Byron/gitoxide/commit/f8ce3d0721b6a53713a9392f2451874f520bc44c)) +
+ ## 0.31.0 (2024-02-25) ### Bug Fixes @@ -16,7 +168,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - - 6 commits contributed to the release over the course of 30 calendar days. + - 7 commits contributed to the release over the course of 30 calendar days. - 36 days passed between releases. - 1 commit was understood as [conventional](https://www.conventionalcommits.org). - 0 issues like '(#ID)' were seen in commit messages @@ -28,6 +180,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
view details * **Uncategorized** + - Release gix-date v0.8.4, gix-utils v0.1.10, gix-actor v0.30.1, gix-object v0.41.1, gix-path v0.10.6, gix-glob v0.16.1, gix-quote v0.4.11, gix-attributes v0.22.1, gix-command v0.3.5, gix-filter v0.10.0, gix-commitgraph v0.24.1, gix-worktree-stream v0.10.0, gix-archive v0.10.0, gix-config-value v0.14.5, gix-ref v0.42.0, gix-sec v0.10.5, gix-config v0.35.0, gix-prompt v0.8.3, gix-url v0.27.1, gix-credentials v0.24.1, gix-ignore v0.11.1, gix-index v0.30.0, gix-worktree v0.31.0, gix-diff v0.41.0, gix-discover v0.30.0, gix-pathspec v0.7.0, gix-dir v0.1.0, gix-pack v0.48.0, gix-odb v0.58.0, gix-transport v0.41.1, gix-protocol v0.44.1, gix-revision v0.26.1, gix-refspec v0.22.1, gix-status v0.6.0, gix-submodule v0.9.0, gix-worktree-state v0.8.0, gix v0.59.0, gix-fsck v0.3.0, gitoxide-core v0.36.0, gitoxide v0.34.0, safety bump 10 crates ([`45b4470`](https://github.com/Byron/gitoxide/commit/45b447045bc826f252129c300c531acde2652c64)) - Prepare changelogs prior to release ([`f2e111f`](https://github.com/Byron/gitoxide/commit/f2e111f768fc1bc6182355261c20b63610cffec7)) - Merge branch 'dirwalk' ([`face359`](https://github.com/Byron/gitoxide/commit/face359443ba33e8985ec1525d5ec38b743ea7a9)) - Re-export crates whose types are used in the API. ([`d0c5a0e`](https://github.com/Byron/gitoxide/commit/d0c5a0eb6801814df2ff21d850d8980e639fabaf)) @@ -72,12 +225,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Chore - - change `rust-version` manifest field back to 1.65. - They didn't actually need to be higher to work, and changing them - unecessarily can break downstream CI. - - Let's keep this value as low as possible, and only increase it when - more recent features are actually used. +- change `rust-version` manifest field back to 1.65. + They didn't actually need to be higher to work, and changing them + unecessarily can break downstream CI. + + Let's keep this value as low as possible, and only increase it when + more recent features are actually used. ### Commit Statistics @@ -105,8 +258,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Chore - - upgrade MSRV to v1.70 - Our MSRV follows the one of `helix`, which in turn follows Firefox. +- upgrade MSRV to v1.70 + Our MSRV follows the one of `helix`, which in turn follows Firefox. ### Commit Statistics @@ -287,11 +440,11 @@ A maintenance release without user-facing changes. ### Chore - - split tests off into their own crate to allow feature toggles. - That way we can test with the `parallel` feature and won't have to - create bogus feature toggles that are only used for testing, yet visbible - to users. - - don't call crate 'WIP' in manifest anymore. +- split tests off into their own crate to allow feature toggles. + That way we can test with the `parallel` feature and won't have to + create bogus feature toggles that are only used for testing, yet visbible + to users. +- don't call crate 'WIP' in manifest anymore. ### New Features @@ -306,9 +459,9 @@ A maintenance release without user-facing changes. ### Chore (BREAKING) - - remove `checkout` and `status` functionality so only `Cache` remains. - `gix-worktree` is now a base-crate for use by derived crates that provide additional - functionality. Shared types or utilities go here. +- remove `checkout` and `status` functionality so only `Cache` remains. + `gix-worktree` is now a base-crate for use by derived crates that provide additional + functionality. Shared types or utilities go here. ### Commit Statistics @@ -514,7 +667,7 @@ A maintenance release without user-facing changes. ### Chore - - Add `clippy::redundant-closure-for-method-calls` lint +- Add `clippy::redundant-closure-for-method-calls` lint ### Commit Statistics @@ -901,8 +1054,8 @@ A maintenance release without user-facing changes. ### Chore - - uniformize deny attributes - - remove default link to cargo doc everywhere +- uniformize deny attributes +- remove default link to cargo doc everywhere ### Documentation diff --git a/gix-worktree/Cargo.toml b/gix-worktree/Cargo.toml index 39bde8a7d4e..58aee67a085 100644 --- a/gix-worktree/Cargo.toml +++ b/gix-worktree/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "gix-worktree" -version = "0.31.0" +version = "0.34.0" repository = "https://github.com/Byron/gitoxide" license = "MIT OR Apache-2.0" description = "A crate of the gitoxide project for shared worktree related types and utilities." @@ -16,22 +16,23 @@ doctest = false [features] default = ["attributes"] ## Instantiate stacks that can access `.gitattributes` information. -attributes = ["dep:gix-attributes"] +attributes = ["dep:gix-attributes", "dep:gix-validate"] ## Data structures implement `serde::Serialize` and `serde::Deserialize`. -serde = [ "dep:serde", "bstr/serde", "gix-index/serde", "gix-hash/serde", "gix-object/serde", "gix-attributes?/serde", "gix-ignore/serde" ] +serde = ["dep:serde", "bstr/serde", "gix-index/serde", "gix-hash/serde", "gix-object/serde", "gix-attributes?/serde", "gix-ignore/serde"] [dependencies] -gix-index = { version = "^0.30.0", path = "../gix-index" } -gix-fs = { version = "^0.10.0", path = "../gix-fs" } -gix-hash = { version = "^0.14.1", path = "../gix-hash" } -gix-object = { version = "^0.41.1", path = "../gix-object" } -gix-glob = { version = "^0.16.1", path = "../gix-glob" } -gix-path = { version = "^0.10.6", path = "../gix-path" } -gix-attributes = { version = "^0.22.1", path = "../gix-attributes", optional = true } -gix-ignore = { version = "^0.11.1", path = "../gix-ignore" } -gix-features = { version = "^0.38.0", path = "../gix-features" } +gix-index = { version = "^0.33.0", path = "../gix-index" } +gix-fs = { version = "^0.11.1", path = "../gix-fs" } +gix-hash = { version = "^0.14.2", path = "../gix-hash" } +gix-object = { version = "^0.42.2", path = "../gix-object" } +gix-glob = { version = "^0.16.3", path = "../gix-glob" } +gix-path = { version = "^0.10.7", path = "../gix-path" } +gix-attributes = { version = "^0.22.2", path = "../gix-attributes", optional = true } +gix-validate = { version = "^0.8.5", path = "../gix-validate", optional = true } +gix-ignore = { version = "^0.11.2", path = "../gix-ignore" } +gix-features = { version = "^0.38.2", path = "../gix-features" } -serde = { version = "1.0.114", optional = true, default-features = false, features = ["derive"]} +serde = { version = "1.0.114", optional = true, default-features = false, features = ["derive"] } bstr = { version = "1.3.0", default-features = false } document-features = { version = "0.2.0", optional = true } diff --git a/gix-worktree/src/lib.rs b/gix-worktree/src/lib.rs index 240285a9128..a68bea99826 100644 --- a/gix-worktree/src/lib.rs +++ b/gix-worktree/src/lib.rs @@ -19,6 +19,9 @@ pub use gix_glob as glob; pub use gix_ignore as ignore; /// Provides types needed for using [`Stack::at_path()`] and [`Stack::at_entry()`]. pub use gix_object as object; +/// Provides types needed for using [`stack::State::for_checkout()`]. +#[cfg(feature = "attributes")] +pub use gix_validate as validate; /// A cache for efficiently executing operations on directories and files which are encountered in sorted order. /// That way, these operations can be re-used for subsequent invocations in the same directory. @@ -57,4 +60,5 @@ pub struct Stack { pub(crate) type PathIdMapping = (BString, gix_hash::ObjectId); /// +#[allow(clippy::empty_docs)] pub mod stack; diff --git a/gix-worktree/src/stack/delegate.rs b/gix-worktree/src/stack/delegate.rs index 1234346c5de..14a48be4e80 100644 --- a/gix-worktree/src/stack/delegate.rs +++ b/gix-worktree/src/stack/delegate.rs @@ -1,5 +1,3 @@ -use bstr::{BStr, ByteSlice}; - use crate::{stack::State, PathIdMapping}; /// Various aggregate numbers related to the stack delegate itself. @@ -22,7 +20,7 @@ pub(crate) struct StackDelegate<'a, 'find> { pub state: &'a mut State, pub buf: &'a mut Vec, #[cfg_attr(not(feature = "attributes"), allow(dead_code))] - pub is_dir: bool, + pub mode: Option, pub id_mappings: &'a Vec, pub objects: &'find dyn gix_object::Find, pub case: gix_glob::pattern::Case, @@ -32,29 +30,15 @@ pub(crate) struct StackDelegate<'a, 'find> { impl<'a, 'find> gix_fs::stack::Delegate for StackDelegate<'a, 'find> { fn push_directory(&mut self, stack: &gix_fs::Stack) -> std::io::Result<()> { self.statistics.delegate.push_directory += 1; - let dir_bstr = gix_path::into_bstr(stack.current()); - let rela_dir_cow = gix_path::to_unix_separators_on_windows( - gix_glob::search::pattern::strip_base_handle_recompute_basename_pos( - gix_path::into_bstr(stack.root()).as_ref(), - dir_bstr.as_ref(), - None, - self.case, - ) - .expect("dir in root") - .0, - ); - let rela_dir: &BStr = if rela_dir_cow.starts_with(b"/") { - rela_dir_cow[1..].as_bstr() - } else { - rela_dir_cow.as_ref() - }; + let rela_dir_bstr = gix_path::into_bstr(stack.current_relative()); + let rela_dir = gix_path::to_unix_separators_on_windows(rela_dir_bstr); match &mut self.state { #[cfg(feature = "attributes")] State::CreateDirectoryAndAttributesStack { attributes, .. } | State::AttributesStack(attributes) => { attributes.push_directory( stack.root(), stack.current(), - rela_dir, + &rela_dir, self.buf, self.id_mappings, self.objects, @@ -66,7 +50,7 @@ impl<'a, 'find> gix_fs::stack::Delegate for StackDelegate<'a, 'find> { attributes.push_directory( stack.root(), stack.current(), - rela_dir, + &rela_dir, self.buf, self.id_mappings, self.objects, @@ -75,7 +59,7 @@ impl<'a, 'find> gix_fs::stack::Delegate for StackDelegate<'a, 'find> { ignore.push_directory( stack.root(), stack.current(), - rela_dir, + &rela_dir, self.buf, self.id_mappings, self.objects, @@ -86,7 +70,7 @@ impl<'a, 'find> gix_fs::stack::Delegate for StackDelegate<'a, 'find> { State::IgnoreStack(ignore) => ignore.push_directory( stack.root(), stack.current(), - rela_dir, + &rela_dir, self.buf, self.id_mappings, self.objects, @@ -104,14 +88,18 @@ impl<'a, 'find> gix_fs::stack::Delegate for StackDelegate<'a, 'find> { #[cfg(feature = "attributes")] State::CreateDirectoryAndAttributesStack { unlink_on_collision, + validate, attributes: _, - } => create_leading_directory( - is_last_component, - stack, - self.is_dir, - &mut self.statistics.delegate.num_mkdir_calls, - *unlink_on_collision, - )?, + } => { + validate_last_component(stack, self.mode, *validate)?; + create_leading_directory( + is_last_component, + stack, + self.mode, + &mut self.statistics.delegate.num_mkdir_calls, + *unlink_on_collision, + )? + } #[cfg(feature = "attributes")] State::AttributesAndIgnoreStack { .. } | State::AttributesStack(_) => {} State::IgnoreStack(_) => {} @@ -138,15 +126,47 @@ impl<'a, 'find> gix_fs::stack::Delegate for StackDelegate<'a, 'find> { } } +#[cfg(feature = "attributes")] +fn validate_last_component( + stack: &gix_fs::Stack, + mode: Option, + opts: gix_validate::path::component::Options, +) -> std::io::Result<()> { + let Some(last_component) = stack.current_relative().components().next_back() else { + return Ok(()); + }; + let last_component = + gix_path::try_into_bstr(std::borrow::Cow::Borrowed(last_component.as_os_str().as_ref())).map_err(|_err| { + std::io::Error::new( + std::io::ErrorKind::Other, + format!( + "Path component {last_component:?} of path \"{}\" contained invalid UTF-8 and could not be validated", + stack.current_relative().display() + ), + ) + })?; + + if let Err(err) = gix_validate::path::component( + last_component.as_ref(), + mode.and_then(|m| { + (m == gix_index::entry::Mode::SYMLINK).then_some(gix_validate::path::component::Mode::Symlink) + }), + opts, + ) { + return Err(std::io::Error::new(std::io::ErrorKind::Other, err)); + } + Ok(()) +} + #[cfg(feature = "attributes")] fn create_leading_directory( is_last_component: bool, stack: &gix_fs::Stack, - is_dir: bool, + mode: Option, mkdir_calls: &mut usize, unlink_on_collision: bool, ) -> std::io::Result<()> { - if is_last_component && !is_dir { + if is_last_component && !crate::stack::mode_is_dir(mode).unwrap_or(false) { return Ok(()); } *mkdir_calls += 1; diff --git a/gix-worktree/src/stack/mod.rs b/gix-worktree/src/stack/mod.rs index b6c03d175bf..1e321650ca7 100644 --- a/gix-worktree/src/stack/mod.rs +++ b/gix-worktree/src/stack/mod.rs @@ -28,6 +28,8 @@ pub enum State { CreateDirectoryAndAttributesStack { /// If there is a symlink or a file in our path, try to unlink it before creating the directory. unlink_on_collision: bool, + /// Options to control how newly created path components should be validated. + validate: gix_validate::path::component::Options, /// State to handle attribute information attributes: state::Attributes, }, @@ -103,22 +105,23 @@ impl Stack { impl Stack { /// Append the `relative` path to the root directory of the cache and efficiently create leading directories, while assuring that no /// symlinks are in that path. - /// Unless `is_dir` is known with `Some(…)`, then `relative` points to a directory itself in which case the entire resulting - /// path is created as directory. If it's not known it is assumed to be a file. + /// Unless `mode` is known with `Some(gix_index::entry::Mode::DIR|COMMIT)`, + /// then `relative` points to a directory itself in which case the entire resulting path is created as directory. + /// If it's not known it is assumed to be a file. /// `objects` maybe used to lookup objects from an [id mapping][crate::stack::State::id_mappings_from_index()], with mappnigs /// /// Provide access to cached information for that `relative` path via the returned platform. pub fn at_path( &mut self, relative: impl AsRef, - is_dir: Option, + mode: Option, objects: &dyn gix_object::Find, ) -> std::io::Result> { self.statistics.platforms += 1; let mut delegate = StackDelegate { state: &mut self.state, buf: &mut self.buf, - is_dir: is_dir.unwrap_or(false), + mode, id_mappings: &self.id_mappings, objects, case: self.case, @@ -126,36 +129,46 @@ impl Stack { }; self.stack .make_relative_path_current(relative.as_ref(), &mut delegate)?; - Ok(Platform { parent: self, is_dir }) + Ok(Platform { + parent: self, + is_dir: mode_is_dir(mode), + }) } - /// Obtain a platform for lookups from a repo-`relative` path, typically obtained from an index entry. `is_dir` should reflect - /// whether it's a directory or not, or left at `None` if unknown. + /// Obtain a platform for lookups from a repo-`relative` path, typically obtained from an index entry. `mode` should reflect + /// the kind of item set here, or left at `None` if unknown. /// `objects` maybe used to lookup objects from an [id mapping][crate::stack::State::id_mappings_from_index()]. /// All effects are similar to [`at_path()`][Self::at_path()]. /// - /// If `relative` ends with `/` and `is_dir` is `None`, it is automatically assumed to be a directory. - /// - /// ### Panics - /// - /// on illformed UTF8 in `relative` + /// If `relative` ends with `/` and `mode` is `None`, it is automatically assumed set to be a directory. pub fn at_entry<'r>( &mut self, relative: impl Into<&'r BStr>, - is_dir: Option, + mode: Option, objects: &dyn gix_object::Find, ) -> std::io::Result> { let relative = relative.into(); - let relative_path = gix_path::from_bstr(relative); + let relative_path = gix_path::try_from_bstr(relative).map_err(|_err| { + std::io::Error::new( + std::io::ErrorKind::Other, + format!("The path \"{relative}\" contained invalid UTF-8 and could not be turned into a path"), + ) + })?; self.at_path( relative_path, - is_dir.or_else(|| relative.ends_with_str("/").then_some(true)), + mode.or_else(|| relative.ends_with_str("/").then_some(gix_index::entry::Mode::DIR)), objects, ) } } +fn mode_is_dir(mode: Option) -> Option { + mode.map(|m| + // This applies to directories and commits (submodules are directories on disk) + m.is_sparse() || m.is_submodule()) +} + /// Mutation impl Stack { /// Reset the statistics after returning them. @@ -195,9 +208,11 @@ impl Stack { } /// +#[allow(clippy::empty_docs)] pub mod delegate; use delegate::StackDelegate; mod platform; /// +#[allow(clippy::empty_docs)] pub mod state; diff --git a/gix-worktree/src/stack/platform.rs b/gix-worktree/src/stack/platform.rs index 0fcdf90f216..0b270516d24 100644 --- a/gix-worktree/src/stack/platform.rs +++ b/gix-worktree/src/stack/platform.rs @@ -20,6 +20,7 @@ impl<'a> Platform<'a> { /// # Panics /// /// If the cache was configured without exclude patterns. + #[doc(alias = "is_path_ignored", alias = "git2")] pub fn is_excluded(&self) -> bool { self.matching_exclude_pattern() .map_or(false, |m| !m.pattern.is_negative()) diff --git a/gix-worktree/src/stack/state/attributes.rs b/gix-worktree/src/stack/state/attributes.rs index 04ad8b5c712..1071e4a9f27 100644 --- a/gix-worktree/src/stack/state/attributes.rs +++ b/gix-worktree/src/stack/state/attributes.rs @@ -98,8 +98,7 @@ impl Attributes { objects: &dyn gix_object::Find, stats: &mut Statistics, ) -> std::io::Result<()> { - let attr_path_relative = - gix_path::to_unix_separators_on_windows(gix_path::join_bstr_unix_pathsep(rela_dir, ".gitattributes")); + let attr_path_relative = gix_path::join_bstr_unix_pathsep(rela_dir, ".gitattributes"); let attr_file_in_index = id_mappings.binary_search_by(|t| t.0.as_bstr().cmp(attr_path_relative.as_ref())); // Git does not follow symbolic links as per documentation. let no_follow_symlinks = false; diff --git a/gix-worktree/src/stack/state/mod.rs b/gix-worktree/src/stack/state/mod.rs index 30e3c609f1b..52d74daac61 100644 --- a/gix-worktree/src/stack/state/mod.rs +++ b/gix-worktree/src/stack/state/mod.rs @@ -53,15 +53,21 @@ pub struct Ignore { #[cfg(feature = "attributes")] pub mod attributes; /// +#[allow(clippy::empty_docs)] pub mod ignore; /// Initialization impl State { /// Configure a state to be suitable for checking out files, which only needs access to attribute files read from the index. #[cfg(feature = "attributes")] - pub fn for_checkout(unlink_on_collision: bool, attributes: Attributes) -> Self { + pub fn for_checkout( + unlink_on_collision: bool, + validate: gix_validate::path::component::Options, + attributes: Attributes, + ) -> Self { State::CreateDirectoryAndAttributesStack { unlink_on_collision, + validate, attributes, } } @@ -130,7 +136,7 @@ impl State { // Stage 0 means there is no merge going on, stage 2 means it's 'our' side of the merge, but then // there won't be a stage 0. - if entry.mode == gix_index::entry::Mode::FILE && (entry.stage() == 0 || entry.stage() == 2) { + if entry.mode == gix_index::entry::Mode::FILE && (entry.stage_raw() == 0 || entry.stage_raw() == 2) { let basename = path.rfind_byte(b'/').map_or(path, |pos| path[pos + 1..].as_bstr()); let ignore_source = names.iter().find_map(|t| { match case { diff --git a/gix-worktree/tests/fixtures/generated-archives/make_special_exclude_case.tar.xz b/gix-worktree/tests/fixtures/generated-archives/make_special_exclude_case.tar.xz index dc7355252c4..6afa1984db6 100644 Binary files a/gix-worktree/tests/fixtures/generated-archives/make_special_exclude_case.tar.xz and b/gix-worktree/tests/fixtures/generated-archives/make_special_exclude_case.tar.xz differ diff --git a/gix-worktree/tests/fixtures/make_attributes_baseline.sh b/gix-worktree/tests/fixtures/make_attributes_baseline.sh index e2885df6e2d..d3606633fbe 100755 --- a/gix-worktree/tests/fixtures/make_attributes_baseline.sh +++ b/gix-worktree/tests/fixtures/make_attributes_baseline.sh @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash set -eu -o pipefail mkdir basics; diff --git a/gix-worktree/tests/fixtures/make_ignore_and_attributes_setup.sh b/gix-worktree/tests/fixtures/make_ignore_and_attributes_setup.sh index 10be29adbcd..956606626c1 100755 --- a/gix-worktree/tests/fixtures/make_ignore_and_attributes_setup.sh +++ b/gix-worktree/tests/fixtures/make_ignore_and_attributes_setup.sh @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash set -eu -o pipefail cat <user.exclude diff --git a/gix-worktree/tests/fixtures/make_special_exclude_case.sh b/gix-worktree/tests/fixtures/make_special_exclude_case.sh index 17a279eda9d..a4bf40719ba 100755 --- a/gix-worktree/tests/fixtures/make_special_exclude_case.sh +++ b/gix-worktree/tests/fixtures/make_special_exclude_case.sh @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash set -eu -o pipefail git init -q diff --git a/gix-worktree/tests/fixtures/symlink_stack.sh b/gix-worktree/tests/fixtures/symlink_stack.sh old mode 100644 new mode 100755 index 0bddba20d8d..5bbd7608599 --- a/gix-worktree/tests/fixtures/symlink_stack.sh +++ b/gix-worktree/tests/fixtures/symlink_stack.sh @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash set -eu -o pipefail git init base; diff --git a/gix-worktree/tests/worktree/stack/attributes.rs b/gix-worktree/tests/worktree/stack/attributes.rs index ae5d7a6b542..8a2079eb011 100644 --- a/gix-worktree/tests/worktree/stack/attributes.rs +++ b/gix-worktree/tests/worktree/stack/attributes.rs @@ -17,6 +17,11 @@ fn baseline() -> crate::Result { let mut collection = gix_attributes::search::MetadataCollection::default(); let state = gix_worktree::stack::State::for_checkout( false, + gix_worktree::validate::path::component::Options { + protect_windows: false, + protect_ntfs: false, + ..Default::default() + }, state::Attributes::new( gix_attributes::Search::new_globals([base.join("user.attributes")], &mut buf, &mut collection)?, Some(git_dir.join("info").join("attributes")), diff --git a/gix-worktree/tests/worktree/stack/create_directory.rs b/gix-worktree/tests/worktree/stack/create_directory.rs index 19130d6559f..65b4b04fd7d 100644 --- a/gix-worktree/tests/worktree/stack/create_directory.rs +++ b/gix-worktree/tests/worktree/stack/create_directory.rs @@ -3,19 +3,22 @@ use std::path::Path; use gix_testtools::tempfile::{tempdir, TempDir}; use gix_worktree::{stack, Stack}; +const IS_FILE: Option = Some(gix_index::entry::Mode::FILE); +const IS_DIR: Option = Some(gix_index::entry::Mode::DIR); + #[test] fn root_is_assumed_to_exist_and_files_in_root_do_not_create_directory() -> crate::Result { let dir = tempdir()?; let mut cache = Stack::new( dir.path().join("non-existing-root"), - stack::State::for_checkout(false, Default::default()), + stack::State::for_checkout(false, Default::default(), Default::default()), Default::default(), Vec::new(), Default::default(), ); assert_eq!(cache.statistics().delegate.num_mkdir_calls, 0); - let path = cache.at_path("hello", Some(false), &gix_object::find::Never)?.path(); + let path = cache.at_path("hello", IS_FILE, &gix_object::find::Never)?.path(); assert!(!path.parent().unwrap().exists(), "prefix itself is never created"); assert_eq!(cache.statistics().delegate.num_mkdir_calls, 0); Ok(()) @@ -25,15 +28,15 @@ fn root_is_assumed_to_exist_and_files_in_root_do_not_create_directory() -> crate fn directory_paths_are_created_in_full() { let (mut cache, _tmp) = new_cache(); - for (name, is_dir) in &[ - ("dir", Some(true)), - ("submodule", Some(true)), - ("file", Some(false)), - ("exe", Some(false)), + for (name, mode) in [ + ("dir", IS_DIR), + ("submodule", IS_DIR), + ("file", IS_FILE), + ("exe", IS_FILE), ("link", None), ] { let path = cache - .at_path(Path::new("dir").join(name), *is_dir, &gix_object::find::Never) + .at_path(Path::new("dir").join(name), mode, &gix_object::find::Never) .unwrap() .path(); assert!(path.parent().unwrap().is_dir(), "dir exists"); @@ -47,13 +50,30 @@ fn existing_directories_are_fine() -> crate::Result { let (mut cache, tmp) = new_cache(); std::fs::create_dir(tmp.path().join("dir"))?; - let path = cache.at_path("dir/file", Some(false), &gix_object::find::Never)?.path(); + let path = cache.at_path("dir/file", IS_FILE, &gix_object::find::Never)?.path(); assert!(path.parent().unwrap().is_dir(), "directory is still present"); assert!(!path.exists(), "it won't create the file"); assert_eq!(cache.statistics().delegate.num_mkdir_calls, 1); Ok(()) } +#[test] +fn validation_to_each_component() -> crate::Result { + let (mut cache, tmp) = new_cache(); + + let err = cache + .at_path("valid/.gIt", IS_FILE, &gix_object::find::Never) + .unwrap_err(); + assert_eq!( + cache.statistics().delegate.num_mkdir_calls, + 1, + "the valid directory was created" + ); + assert!(tmp.path().join("valid").is_dir(), "it was actually created"); + assert_eq!(err.to_string(), "The .git name may never be used"); + Ok(()) +} + #[test] fn symlinks_or_files_in_path_are_forbidden_or_unlinked_when_forced() -> crate::Result { let (mut cache, tmp) = new_cache(); @@ -72,7 +92,7 @@ fn symlinks_or_files_in_path_are_forbidden_or_unlinked_when_forced() -> crate::R let relative_path = format!("{dirname}/file"); assert_eq!( cache - .at_path(&relative_path, Some(false), &gix_object::find::Never) + .at_path(&relative_path, IS_FILE, &gix_object::find::Never) .unwrap_err() .kind(), std::io::ErrorKind::AlreadyExists @@ -92,9 +112,7 @@ fn symlinks_or_files_in_path_are_forbidden_or_unlinked_when_forced() -> crate::R *unlink_on_collision = true; } let relative_path = format!("{dirname}/file"); - let path = cache - .at_path(&relative_path, Some(false), &gix_object::find::Never)? - .path(); + let path = cache.at_path(&relative_path, IS_FILE, &gix_object::find::Never)?.path(); assert!(path.parent().unwrap().is_dir(), "directory was forcefully created"); assert!(!path.exists()); } @@ -110,7 +128,7 @@ fn new_cache() -> (Stack, TempDir) { let dir = tempdir().unwrap(); let cache = Stack::new( dir.path(), - stack::State::for_checkout(false, Default::default()), + stack::State::for_checkout(false, Default::default(), Default::default()), Default::default(), Vec::new(), Default::default(), diff --git a/gix-worktree/tests/worktree/stack/ignore.rs b/gix-worktree/tests/worktree/stack/ignore.rs index 6e578f352d8..7ea4fbcebe5 100644 --- a/gix-worktree/tests/worktree/stack/ignore.rs +++ b/gix-worktree/tests/worktree/stack/ignore.rs @@ -1,5 +1,7 @@ use bstr::{BStr, ByteSlice}; +use gix_index::entry::Mode; use gix_worktree::{stack::state::ignore::Source, Stack}; +use std::fs::Metadata; use crate::{hex_to_id, worktree::stack::probe_case}; @@ -62,7 +64,7 @@ fn exclude_by_dir_is_handled_just_like_git() { for (relative_entry, source_and_line) in expectations { let (source, line, expected_pattern) = source_and_line.expect("every value is matched"); let relative_path = gix_path::from_byte_slice(relative_entry); - let is_dir = dir.join(relative_path).metadata().ok().map(|m| m.is_dir()); + let is_dir = dir.join(relative_path).metadata().ok().map(metadata_to_mode); let platform = cache.at_entry(relative_entry, is_dir, &FindError).unwrap(); let match_ = platform.matching_exclude_pattern().expect("match all values"); @@ -87,6 +89,14 @@ fn exclude_by_dir_is_handled_just_like_git() { } } +fn metadata_to_mode(meta: Metadata) -> Mode { + if meta.is_dir() { + gix_index::entry::Mode::DIR + } else { + gix_index::entry::Mode::FILE + } +} + #[test] fn check_against_baseline() -> crate::Result { let dir = gix_testtools::scripted_fixture_read_only_standalone("make_ignore_and_attributes_setup.sh")?; @@ -127,7 +137,7 @@ fn check_against_baseline() -> crate::Result { }; for (relative_entry, source_and_line) in expectations { let relative_path = gix_path::from_byte_slice(relative_entry); - let is_dir = worktree_dir.join(relative_path).metadata().ok().map(|m| m.is_dir()); + let is_dir = worktree_dir.join(relative_path).metadata().ok().map(metadata_to_mode); let platform = cache.at_entry(relative_entry, is_dir, &odb)?; diff --git a/gix/CHANGELOG.md b/gix/CHANGELOG.md index ce4ba366df1..8ba01aa8710 100644 --- a/gix/CHANGELOG.md +++ b/gix/CHANGELOG.md @@ -5,6 +5,269 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## 0.63.0 (2024-05-22) + +### New Features + + - checkout respects options for `core.protectHFS` and `core.protectNTFS`. + This also adds `gitoxide.core.protectWindows` as a way to enforce + additional restrictions that are usually only available on Windows. + + Note that `core.protectNFS` is always enabled by default, just like + [it is in Git](https://github.com/git/git/commit/9102f958ee5254b10c0be72672aa3305bf4f4704). + +### Bug Fixes + + - empty paths as configured will not be an error with lenient configuration enabled. + When using `gix::open_opts(path, options.strict_config(false))`, empty `core.excludesFile` values + will not cause an error anymore. + + Note that in strict mode, the behaviour is unchanged so invalid configuration can rather be fixed + than ignored. + - don't unwrap when reading possibly left-over bytes from pack-stream + +### Commit Statistics + + + + - 23 commits contributed to the release over the course of 38 calendar days. + - 38 days passed between releases. + - 3 commits were understood as [conventional](https://www.conventionalcommits.org). + - 2 unique issues were worked on: [#1352](https://github.com/Byron/gitoxide/issues/1352), [#1370](https://github.com/Byron/gitoxide/issues/1370) + +### Commit Details + + + +
view details + + * **[#1352](https://github.com/Byron/gitoxide/issues/1352)** + - Don't unwrap when reading possibly left-over bytes from pack-stream ([`88a6a4e`](https://github.com/Byron/gitoxide/commit/88a6a4e6d882fc7a3a0b4017d772a3fe38e57598)) + * **[#1370](https://github.com/Byron/gitoxide/issues/1370)** + - Empty paths as configured will not be an error with lenient configuration enabled. ([`3c7b7b3`](https://github.com/Byron/gitoxide/commit/3c7b7b3a7b981040cd51417202d7022597179114)) + * **Uncategorized** + - Adjust changelogs prior to release ([`9511416`](https://github.com/Byron/gitoxide/commit/9511416a6cd0c571233f958c165329c8705c2498)) + - Merge branch 'various-fixes' ([`d6cd449`](https://github.com/Byron/gitoxide/commit/d6cd44930fb204b06e2b70fc6965e7705530c47a)) + - Update dependencies ([`cd4de83`](https://github.com/Byron/gitoxide/commit/cd4de8327fc195eb862ab6e138f2315a87374f85)) + - Fix-CI ([`6f55f2a`](https://github.com/Byron/gitoxide/commit/6f55f2abd13078f94e8c4e10922806f195ae0d8b)) + - Merge pull request from GHSA-7w47-3wg8-547c ([`79dce79`](https://github.com/Byron/gitoxide/commit/79dce79c62f6072aa2653780d590dc3993dfa401)) + - Adapt to changes in `gix-ref` ([`d2ae9d5`](https://github.com/Byron/gitoxide/commit/d2ae9d5f11be9f2561f6799d88804d0d8eae33ef)) + - Adapt to changes in `gix-index` ([`5f86e6b`](https://github.com/Byron/gitoxide/commit/5f86e6b11bb73921b458ffee9091bc028a7d6204)) + - Fix compile warnings ([`f961687`](https://github.com/Byron/gitoxide/commit/f9616871e83502e720edad621bc6a9cbcfc53de3)) + - Address review comments ([`fcc3b69`](https://github.com/Byron/gitoxide/commit/fcc3b69867db1628f6a44d0e0dad8f7417f566bc)) + - Apply suggestions from code review ([`bad9a79`](https://github.com/Byron/gitoxide/commit/bad9a797b99880ce9d1c20e11c801bd0e741db64)) + - Checkout respects options for `core.protectHFS` and `core.protectNTFS`. ([`886d6b5`](https://github.com/Byron/gitoxide/commit/886d6b58e4612ac21cc660ea4ddf1dd0b49d1c6e)) + - Adapt to changes in `gix-worktree` ([`1ca6a3c`](https://github.com/Byron/gitoxide/commit/1ca6a3ce22887c7eb42ec3e0a19f6e1202715745)) + - Merge pull request #1371 from Byron/fix-empty-excludes-file ([`3c21741`](https://github.com/Byron/gitoxide/commit/3c2174101ed35dcb9bdb4585b3245507b15efe59)) + - Release gix-date v0.8.6 ([`d3588ca`](https://github.com/Byron/gitoxide/commit/d3588ca4fe0364c88e42cdac24ceae548355d99d)) + - Merge branch 'status' ([`04ef31e`](https://github.com/Byron/gitoxide/commit/04ef31e9d6f5332d49037a5a4c248ebbb5aaf92b)) + - Improve docs to be more approachable from `git2` ([`5197b5a`](https://github.com/Byron/gitoxide/commit/5197b5abd988002ffbb40f34bbe000ce5dcaffcf)) + - Merge branch 'status' ([`e791bc5`](https://github.com/Byron/gitoxide/commit/e791bc5da52a1237fb7cac230af583199162825d)) + - Merge branch 'cargo-fixes' ([`977346e`](https://github.com/Byron/gitoxide/commit/977346ee61de6207c66f3de003db6e8c722fb81c)) + - Release gix-index v0.32.1, gix-pathspec v0.7.4, gix-worktree v0.33.1, gix-dir v0.4.1 ([`54ac559`](https://github.com/Byron/gitoxide/commit/54ac55946bb04635cd74582a1ce2e4bee70f2e60)) + - Merge pull request #1345 from EliahKagan/shell-scripts ([`fe24c89`](https://github.com/Byron/gitoxide/commit/fe24c89e326670deaa3aaa643276d612d866072e)) + - Add missing +x bit on scripts that are run and not sourced ([`41bf65a`](https://github.com/Byron/gitoxide/commit/41bf65adef6f7d2cdd28fede262173ec7ba10822)) +
+ +## 0.62.0 (2024-04-13) + +Please note that this release contains a security fix originally implemented in `gix-transport` via [this PR](https://github.com/Byron/gitoxide/pull/1342) +which prevents `ssh` options to be smuggled into the `ssh` command-line invocation with a username provided to a clone or fetch URL. + +Details can be found [in the advisory](https://github.com/Byron/gitoxide/security/advisories/GHSA-98p4-xjmm-8mfh). + +### Bug Fixes + + - `into_index_worktree_iter()` now takes an iterator, instead of a Vec. + This makes the API more consistent, and one can pass `None` + as well. + - show submodules in status independently of their active state. + Even inactive submodules are shown in the status by `git status`, + so `gix` should do the same. + + First observed in https://github.com/helix-editor/helix/pull/5645#issuecomment-2016798212 + - forward `curl` rustls feature from `gix-transport` to avoid `curl` in `gix`. + This removes the `curl` dependency just for configuring it, and removes + a hazard which became evident with reqwest. + +### Bug Fixes (BREAKING) + + - Make `topo` more similar to `Ancestors`, but also rename `Ancestors` to `Simple` + +### Commit Statistics + + + + - 17 commits contributed to the release over the course of 20 calendar days. + - 22 days passed between releases. + - 4 commits were understood as [conventional](https://www.conventionalcommits.org). + - 1 unique issue was worked on: [#1328](https://github.com/Byron/gitoxide/issues/1328) + +### Thanks Clippy + + + +[Clippy](https://github.com/rust-lang/rust-clippy) helped 1 time to make code idiomatic. + +### Commit Details + + + +
view details + + * **[#1328](https://github.com/Byron/gitoxide/issues/1328)** + - Forward `curl` rustls feature from `gix-transport` to avoid `curl` in `gix`. ([`98cfbec`](https://github.com/Byron/gitoxide/commit/98cfbec51276bbd6caa48fd6d8942247df091c94)) + * **Uncategorized** + - Release gix-trace v0.1.9, gix-utils v0.1.12, gix-packetline-blocking v0.17.4, gix-filter v0.11.1, gix-fs v0.10.2, gix-traverse v0.39.0, gix-worktree-stream v0.12.0, gix-archive v0.12.0, gix-config v0.36.1, gix-url v0.27.3, gix-index v0.32.0, gix-worktree v0.33.0, gix-diff v0.43.0, gix-pathspec v0.7.3, gix-dir v0.4.0, gix-pack v0.50.0, gix-odb v0.60.0, gix-transport v0.42.0, gix-protocol v0.45.0, gix-status v0.9.0, gix-worktree-state v0.10.0, gix v0.62.0, gix-fsck v0.4.0, gitoxide-core v0.37.0, gitoxide v0.35.0, safety bump 14 crates ([`095c673`](https://github.com/Byron/gitoxide/commit/095c6739b2722a8b9af90776b435ef2da454c0e6)) + - Prepare changelogs prior to release ([`5755271`](https://github.com/Byron/gitoxide/commit/57552717f46f96c35ba4ddc0a64434354ef845e9)) + - Merge pull request #1341 from szepeviktor/typos ([`55f379b`](https://github.com/Byron/gitoxide/commit/55f379bc47065822d078393d83d30c0835a89782)) + - Fix typos ([`f72ecce`](https://github.com/Byron/gitoxide/commit/f72ecce45babcad2a0c9b73c79d01ff502907a57)) + - Merge branch 'add-topo-walk' ([`b590a9d`](https://github.com/Byron/gitoxide/commit/b590a9d2b6a273f76f0320d2b9fe1f679c08f549)) + - Adapt to changes in `gix-traverse` ([`1cfeb11`](https://github.com/Byron/gitoxide/commit/1cfeb11f1fe9ad9c7b9084840ed7f5c5877f2f9a)) + - Make `topo` more similar to `Ancestors`, but also rename `Ancestors` to `Simple` ([`2a9c178`](https://github.com/Byron/gitoxide/commit/2a9c178326b7f13ba6bc1f89fc2b9d9facbecf48)) + - Adapt to changes in `gix-traverse` ([`6154bf3`](https://github.com/Byron/gitoxide/commit/6154bf3a346d69f9749271d50e4f3aacdcbad4d0)) + - Thanks clippy ([`7f6bee5`](https://github.com/Byron/gitoxide/commit/7f6bee5452ee01638f89a0cec2d4ee2a6f0d0136)) + - Merge branch 'status' ([`45edd2e`](https://github.com/Byron/gitoxide/commit/45edd2ea66035adf526cb2f617873dcba60a2a9a)) + - `into_index_worktree_iter()` now takes an iterator, instead of a Vec. ([`18b2921`](https://github.com/Byron/gitoxide/commit/18b2921aaa28df536faf74098d5f1f13d34148f9)) + - Show submodules in status independently of their active state. ([`719ced8`](https://github.com/Byron/gitoxide/commit/719ced8a7949ba1f30fef13801e3466a7d1da590)) + - Make it easier to discover `is_path_excluded()` in documentation ([`c136329`](https://github.com/Byron/gitoxide/commit/c13632959e287f31a00c1ba8fc6e97470f0cd734)) + - Adapt to changes in `gix-index` ([`1e1fce1`](https://github.com/Byron/gitoxide/commit/1e1fce11a968ebbcede1135ccbd0b03e749a1267)) + - Merge branch 'patch-1' ([`9e9c653`](https://github.com/Byron/gitoxide/commit/9e9c653a83df58f8cdfe3a7adb2d824c8a368e72)) + - Remove dep reqwest from gix ([`e3eedd8`](https://github.com/Byron/gitoxide/commit/e3eedd8b5326b8de2e6fe8941e1851bdbad673ab)) +
+ +## 0.61.1 (2024-03-22) + +This release also updates `reqwest` to v0.12, bringing hyper 1.0 and a more recent `rustls` version. + +### Bug Fixes + + - missing closing backtick in gix lib documentation + +### Commit Statistics + + + + - 8 commits contributed to the release over the course of 2 calendar days. + - 3 days passed between releases. + - 1 commit was understood as [conventional](https://www.conventionalcommits.org). + - 0 issues like '(#ID)' were seen in commit messages + +### Commit Details + + + +
view details + + * **Uncategorized** + - Release gix-packetline v0.17.5, gix-transport v0.41.3, gix v0.61.1 ([`57579f1`](https://github.com/Byron/gitoxide/commit/57579f1ee4ef12c214db36325a2a0b2e8b2b14fd)) + - Prepare changelogs prior to release ([`7018a92`](https://github.com/Byron/gitoxide/commit/7018a928a405ba0534442f0b538d58f520145376)) + - Merge branch 'patch-1' ([`8fde62b`](https://github.com/Byron/gitoxide/commit/8fde62b2617985f835e2e2fa07c735a5158789cf)) + - Turn`curl` into a workspace package ([`adee500`](https://github.com/Byron/gitoxide/commit/adee50016007619495c93580e845ae757377c4f0)) + - Make reqwest a workspace package ([`369cf1b`](https://github.com/Byron/gitoxide/commit/369cf1b03735617debe1527b3f23247685181e7d)) + - Merge pull request #1325 from kdelorey/fix/simple-docs-formatting ([`3b34699`](https://github.com/Byron/gitoxide/commit/3b34699d127a2fccbf4345ddc74070e56e26dd6e)) + - Fixed opening of backtick in documentation. ([`f1bc4cd`](https://github.com/Byron/gitoxide/commit/f1bc4cd11aad91fc026c20979a02f3e9d8814d3d)) + - Missing closing backtick in gix lib documentation ([`e1fec3c`](https://github.com/Byron/gitoxide/commit/e1fec3c3a46a358036255a2487c2a48cc7176b4e)) +
+ +## 0.61.0 (2024-03-18) + +### Documentation + + - fix typo + +### New Features (BREAKING) + + - provide `Repository::dirwalk_iter()`. + That way, more copying happens but the usability increases tremendously as well. + It's breaking as public types moved from `repository::dirwalk` to `dirwalk`, + dissolving `repository::dirwalk` entirely. + +### Commit Statistics + + + + - 8 commits contributed to the release over the course of 3 calendar days. + - 3 days passed between releases. + - 2 commits were understood as [conventional](https://www.conventionalcommits.org). + - 0 issues like '(#ID)' were seen in commit messages + +### Commit Details + + + +
view details + + * **Uncategorized** + - Release gix-actor v0.31.1, gix-object v0.42.1, gix-index v0.31.1, gix-pathspec v0.7.2, gix-dir v0.3.0, gix-status v0.8.0, gix v0.61.0, safety bump 2 crates ([`155cc45`](https://github.com/Byron/gitoxide/commit/155cc45730b259e662d7c4be42a469a3af3750e1)) + - Prepare changelog prior to release ([`129ba3d`](https://github.com/Byron/gitoxide/commit/129ba3deccc9ada0dc571466458845939502763d)) + - Merge branch 'improvements-for-cargo' ([`41cd53e`](https://github.com/Byron/gitoxide/commit/41cd53e2af76e35e047aac4eca6324774df4cb50)) + - Provide `Repository::dirwalk_iter()`. ([`ba3f2db`](https://github.com/Byron/gitoxide/commit/ba3f2db0b65582a917466127dc16e4945104b01b)) + - Adapt to changes in `gix-dir` ([`b90ab3d`](https://github.com/Byron/gitoxide/commit/b90ab3dd5e8986e28624f3e1cf54f8a9171ce9f0)) + - Merge pull request #1318 from wtlin1228/main ([`4ccf39b`](https://github.com/Byron/gitoxide/commit/4ccf39b9f52fd318a2eba4c63dd13e96269a4c99)) + - Refine typo-fix ([`c18734b`](https://github.com/Byron/gitoxide/commit/c18734b05fcbc3d6f57bcb2c6525ec10015a7192)) + - Fix typo ([`e51b6b6`](https://github.com/Byron/gitoxide/commit/e51b6b624994714c7e25d00e1204cefbf1b4ca12)) +
+ +## 0.60.0 (2024-03-14) + +### New Features + + - add `gix status --index-worktree-renames` + This enables rename-tracking between worktree and index, something + that Git also doesn't do or doesn't do by default. + It is, however, available in `git2`. + - describing commits can now be done with conditional dirty-suffix using `commit::describe::Resolution::format_with_dirty_suffix()` + - add `Repository::is_dirty()` + The simplest way to learn if the repository is dirty or not. + - Add `Submodule::status()` method. + That way it's possible to obtain submodule status information, + with enough information to implement `git status`-like commands. + - add `Status` iterator. + We also move the `IndexPersistedOrInMemory` type to the `worktree` module + as its more widely useful. + +### New Features (BREAKING) + + - `diff::resource_cache()` now takes the attribute stack directly. + That way, the constructor becaomes more versatile as the user can chose + to pass attribute stacks that have more functionality, and thus can be + used in more places. + +### Commit Statistics + + + + - 16 commits contributed to the release over the course of 10 calendar days. + - 18 days passed between releases. + - 6 commits were understood as [conventional](https://www.conventionalcommits.org). + - 0 issues like '(#ID)' were seen in commit messages + +### Commit Details + + + +
view details + + * **Uncategorized** + - Release gix-date v0.8.5, gix-hash v0.14.2, gix-trace v0.1.8, gix-utils v0.1.11, gix-features v0.38.1, gix-actor v0.31.0, gix-validate v0.8.4, gix-object v0.42.0, gix-path v0.10.7, gix-glob v0.16.2, gix-quote v0.4.12, gix-attributes v0.22.2, gix-command v0.3.6, gix-filter v0.11.0, gix-fs v0.10.1, gix-chunk v0.4.8, gix-commitgraph v0.24.2, gix-hashtable v0.5.2, gix-revwalk v0.13.0, gix-traverse v0.38.0, gix-worktree-stream v0.11.0, gix-archive v0.11.0, gix-config-value v0.14.6, gix-tempfile v13.1.1, gix-lock v13.1.1, gix-ref v0.43.0, gix-sec v0.10.6, gix-config v0.36.0, gix-prompt v0.8.4, gix-url v0.27.2, gix-credentials v0.24.2, gix-ignore v0.11.2, gix-bitmap v0.2.11, gix-index v0.31.0, gix-worktree v0.32.0, gix-diff v0.42.0, gix-discover v0.31.0, gix-pathspec v0.7.1, gix-dir v0.2.0, gix-macros v0.1.4, gix-mailmap v0.23.0, gix-negotiate v0.13.0, gix-pack v0.49.0, gix-odb v0.59.0, gix-packetline v0.17.4, gix-transport v0.41.2, gix-protocol v0.44.2, gix-revision v0.27.0, gix-refspec v0.23.0, gix-status v0.7.0, gix-submodule v0.10.0, gix-worktree-state v0.9.0, gix v0.60.0, safety bump 26 crates ([`b050327`](https://github.com/Byron/gitoxide/commit/b050327e76f234b19be921b78b7b28e034319fdb)) + - Prepare changelogs prior to release ([`52c3bbd`](https://github.com/Byron/gitoxide/commit/52c3bbd36b9e94a0f3a78b4ada84d0c08eba27f6)) + - Merge branch 'status' ([`3e5c974`](https://github.com/Byron/gitoxide/commit/3e5c974dd62ac134711c6c2f5a5490187a6ea55e)) + - Assure submodule status doesn't operate if there is no worktree checkout ([`3753592`](https://github.com/Byron/gitoxide/commit/3753592ef2e33f138544f761d8e77742b80680d2)) + - Make `summary` available for `Item`. ([`da45d92`](https://github.com/Byron/gitoxide/commit/da45d92f844d670dd23712a031584a4c3352708b)) + - Add `gix status --index-worktree-renames` ([`66e87cd`](https://github.com/Byron/gitoxide/commit/66e87cd31c060c3f97ac685ee0541c408f600362)) + - Add `status.showUntrackedFiles` to config-tree and use it in `status()` ([`22abf60`](https://github.com/Byron/gitoxide/commit/22abf605858404fcd38a5f4b8713358a526819ac)) + - Fix lints for nightly, and clippy ([`f8ce3d0`](https://github.com/Byron/gitoxide/commit/f8ce3d0721b6a53713a9392f2451874f520bc44c)) + - Allow configuration of interrupts in status iter ([`f1ba7bd`](https://github.com/Byron/gitoxide/commit/f1ba7bd459390080052024920992054f1d11cd3e)) + - Provide a non-parallel version of the status iteration ([`17bef30`](https://github.com/Byron/gitoxide/commit/17bef301f2be29c8d0545b35d1581e57037e69df)) + - Describing commits can now be done with conditional dirty-suffix using `commit::describe::Resolution::format_with_dirty_suffix()` ([`c7ddd30`](https://github.com/Byron/gitoxide/commit/c7ddd30fc9fde6cac55153fa8e7fd783c83b336f)) + - Add `Repository::is_dirty()` ([`c20ad28`](https://github.com/Byron/gitoxide/commit/c20ad287128132cda995a47abac1dd18f415f02d)) + - Add submodule support for status iterator ([`4a4989d`](https://github.com/Byron/gitoxide/commit/4a4989d5170173269dcdc19890827911d13e7a89)) + - Add `Submodule::status()` method. ([`a29fa00`](https://github.com/Byron/gitoxide/commit/a29fa00d0727baffcba10c8f2f09115a362a2baf)) + - Add `Status` iterator. ([`0330ad7`](https://github.com/Byron/gitoxide/commit/0330ad77edab88e14812c57f812c96c5e4561045)) + - `diff::resource_cache()` now takes the attribute stack directly. ([`57cf83b`](https://github.com/Byron/gitoxide/commit/57cf83b57b0de01bd69f63ec3637859ccd757272)) +
+ ## 0.59.0 (2024-02-25) ### New Features @@ -31,7 +294,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - - 18 commits contributed to the release over the course of 30 calendar days. + - 19 commits contributed to the release over the course of 30 calendar days. - 36 days passed between releases. - 4 commits were understood as [conventional](https://www.conventionalcommits.org). - 0 issues like '(#ID)' were seen in commit messages @@ -43,6 +306,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
view details * **Uncategorized** + - Release gix-date v0.8.4, gix-utils v0.1.10, gix-actor v0.30.1, gix-object v0.41.1, gix-path v0.10.6, gix-glob v0.16.1, gix-quote v0.4.11, gix-attributes v0.22.1, gix-command v0.3.5, gix-filter v0.10.0, gix-commitgraph v0.24.1, gix-worktree-stream v0.10.0, gix-archive v0.10.0, gix-config-value v0.14.5, gix-ref v0.42.0, gix-sec v0.10.5, gix-config v0.35.0, gix-prompt v0.8.3, gix-url v0.27.1, gix-credentials v0.24.1, gix-ignore v0.11.1, gix-index v0.30.0, gix-worktree v0.31.0, gix-diff v0.41.0, gix-discover v0.30.0, gix-pathspec v0.7.0, gix-dir v0.1.0, gix-pack v0.48.0, gix-odb v0.58.0, gix-transport v0.41.1, gix-protocol v0.44.1, gix-revision v0.26.1, gix-refspec v0.22.1, gix-status v0.6.0, gix-submodule v0.9.0, gix-worktree-state v0.8.0, gix v0.59.0, gix-fsck v0.3.0, gitoxide-core v0.36.0, gitoxide v0.34.0, safety bump 10 crates ([`45b4470`](https://github.com/Byron/gitoxide/commit/45b447045bc826f252129c300c531acde2652c64)) - Prepare changelogs prior to release ([`f2e111f`](https://github.com/Byron/gitoxide/commit/f2e111f768fc1bc6182355261c20b63610cffec7)) - Merge branch 'status' ([`bb48c4c`](https://github.com/Byron/gitoxide/commit/bb48c4ce22650b8c76af3b147e252ebe7cedb205)) - Empty pathspecs with prefix now are optionally matching the prefix. ([`0b1b44f`](https://github.com/Byron/gitoxide/commit/0b1b44fa79a60ed40a9da154f7487408e6436941)) diff --git a/gix/Cargo.toml b/gix/Cargo.toml index 7bac0835b57..ddb8dbf2c32 100644 --- a/gix/Cargo.toml +++ b/gix/Cargo.toml @@ -3,7 +3,7 @@ name = "gix" repository = "https://github.com/Byron/gitoxide" description = "Interact with git repositories just like git would" license = "MIT OR Apache-2.0" -version = "0.59.0" +version = "0.63.0" authors = ["Sebastian Thiel "] edition = "2021" include = ["src/**/*", "LICENSE-*"] @@ -51,10 +51,25 @@ default = ["max-performance-safe", "comfort", "basic", "extras"] basic = ["blob-diff", "revision", "index"] ## Various additional features and capabilities that are not necessarily part of what most users would need. -extras = ["worktree-stream", "worktree-archive", "revparse-regex", "mailmap", "excludes", "attributes", "worktree-mutation", "credentials", "interrupt", "status", "dirwalk"] +extras = [ + "worktree-stream", + "worktree-archive", + "revparse-regex", + "mailmap", + "excludes", + "attributes", + "worktree-mutation", + "credentials", + "interrupt", + "status", + "dirwalk", +] ## Various progress-related features that improve the look of progress message units. -comfort = ["gix-features/progress-unit-bytes", "gix-features/progress-unit-human-numbers"] +comfort = [ + "gix-features/progress-unit-bytes", + "gix-features/progress-unit-human-numbers", +] #! #### Components #! @@ -65,7 +80,7 @@ comfort = ["gix-features/progress-unit-bytes", "gix-features/progress-unit-human command = ["dep:gix-command"] ## Obtain information similar to `git status`. -status = ["gix-status"] +status = ["gix-status", "dirwalk", "index", "blob-diff"] ## Utilities for interrupting computations and cleaning up tempfiles. interrupt = ["dep:signal-hook", "gix-tempfile/signals"] @@ -74,7 +89,7 @@ interrupt = ["dep:signal-hook", "gix-tempfile/signals"] index = ["dep:gix-index"] ## Support directory walks with Git-style annoations. -dirwalk = ["dep:gix-dir"] +dirwalk = ["dep:gix-dir", "attributes", "excludes"] ## Access to credential helpers, which provide credentials for URLs. # Note that `gix-negotiate` just piggibacks here, as 'credentials' is equivalent to 'fetch & push' right now. @@ -87,7 +102,15 @@ worktree-mutation = ["attributes", "dep:gix-worktree-state"] excludes = ["dep:gix-ignore", "dep:gix-worktree", "index"] ## Query attributes and excludes. Enables access to pathspecs, worktree checkouts, filter-pipelines and submodules. -attributes = ["excludes", "dep:gix-filter", "dep:gix-pathspec", "dep:gix-attributes", "dep:gix-submodule", "gix-worktree?/attributes", "command"] +attributes = [ + "excludes", + "dep:gix-filter", + "dep:gix-pathspec", + "dep:gix-attributes", + "dep:gix-submodule", + "gix-worktree?/attributes", + "command", +] ## Add support for mailmaps, as way of determining the final name of commmiters and authors. mailmap = ["dep:gix-mailmap", "revision"] @@ -119,24 +142,56 @@ worktree-archive = ["gix-archive", "worktree-stream", "attributes"] #! Making a choice here also affects which crypto-library ends up being used. ## Make `gix-protocol` available along with an async client. -async-network-client = ["gix-protocol/async-client", "gix-pack/streaming-input", "attributes", "credentials"] +async-network-client = [ + "gix-protocol/async-client", + "gix-pack/streaming-input", + "attributes", + "credentials", +] ## Use this if your crate uses `async-std` as runtime, and enable basic runtime integration when connecting to remote servers via the `git://` protocol. -async-network-client-async-std = ["async-std", "async-network-client", "gix-transport/async-std"] -## Make `gix-protocol` available along with a blocking client, providing access to the `file://`, git://` and `ssh://` transports. -blocking-network-client = ["gix-protocol/blocking-client", "gix-pack/streaming-input", "attributes", "credentials"] +async-network-client-async-std = [ + "async-std", + "async-network-client", + "gix-transport/async-std", +] +## Make `gix-protocol` available along with a blocking client, providing access to the `file://`, `git://` and `ssh://` transports. +blocking-network-client = [ + "gix-protocol/blocking-client", + "gix-pack/streaming-input", + "attributes", + "credentials", +] ## Stacks with `blocking-network-client` to provide support for HTTP/S using **curl**, and implies blocking networking as a whole, making the `https://` transport available. -blocking-http-transport-curl = ["blocking-network-client", "gix-transport/http-client-curl"] +blocking-http-transport-curl = [ + "blocking-network-client", + "gix-transport/http-client-curl", +] ## Stacks with `blocking-http-transport-curl` and also enables the `rustls` backend to avoid `openssl`. -blocking-http-transport-curl-rustls = ["blocking-http-transport-curl", "dep:curl-for-configuration-only", "curl-for-configuration-only?/rustls"] +blocking-http-transport-curl-rustls = [ + "blocking-http-transport-curl", + "gix-transport/http-client-curl-rust-tls", +] ## Stacks with `blocking-network-client` to provide support for HTTP/S using **reqwest**, and implies blocking networking as a whole, making the `https://` transport available. -blocking-http-transport-reqwest = ["blocking-network-client", "gix-transport/http-client-reqwest"] +blocking-http-transport-reqwest = [ + "blocking-network-client", + "gix-transport/http-client-reqwest", +] ## Stacks with `blocking-http-transport-reqwest` and enables `https://` via the `rustls` crate. -blocking-http-transport-reqwest-rust-tls = ["blocking-http-transport-reqwest", "reqwest-for-configuration-only/rustls-tls" ] +blocking-http-transport-reqwest-rust-tls = [ + "blocking-http-transport-reqwest", + "gix-transport/http-client-reqwest-rust-tls", +] ## Stacks with `blocking-http-transport-reqwest` and enables `https://` via the `rustls` crate. ## This also makes use of `trust-dns` to avoid `getaddrinfo`, but note it comes with its own problems. -blocking-http-transport-reqwest-rust-tls-trust-dns = ["blocking-http-transport-reqwest", "reqwest-for-configuration-only/rustls-tls", "reqwest-for-configuration-only/trust-dns"] +blocking-http-transport-reqwest-rust-tls-trust-dns = [ + "blocking-http-transport-reqwest", + "gix-transport/http-client-reqwest-rust-tls-trust-dns", +] ## Stacks with `blocking-http-transport-reqwest` and enables `https://` via the `native-tls` crate. -blocking-http-transport-reqwest-native-tls = ["blocking-http-transport-reqwest", "reqwest-for-configuration-only/default-tls" ] +blocking-http-transport-reqwest-native-tls = [ + "blocking-http-transport-reqwest", + "gix-transport/http-client-reqwest-native-tls", +] #! #### Performance @@ -147,20 +202,13 @@ blocking-http-transport-reqwest-native-tls = ["blocking-http-transport-reqwest", ## Activate features that maximize performance, like using threads, but leave everything else that might affect compatibility out to allow users more fine-grained ## control over performance features like which `zlib*` implementation to use. ## No C toolchain is involved. -max-control = [ - "parallel", - "pack-cache-lru-static", - "pack-cache-lru-dynamic", -] +max-control = ["parallel", "pack-cache-lru-static", "pack-cache-lru-dynamic"] ## Activate features that maximize performance, like usage of threads, `and access to caching in object databases, skipping the ones known to cause compile failures ## on some platforms. ## Note that this configuration still uses a pure Rust zlib implementation which isn't the fastest compared to its C-alternatives. ## No C toolchain is involved. -max-performance-safe = [ - "max-control", - "parallel-walkdir" -] +max-performance-safe = ["max-control", "parallel-walkdir"] ## If set, walkdir iterators will be multi-threaded which affects the listing of loose objects and references. ## Note, however, that this will use `rayon` under the hood and spawn threads for each traversal to avoid a global rayon thread pool. @@ -186,11 +234,11 @@ pack-cache-lru-dynamic = ["gix-pack/pack-cache-lru-dynamic"] ## Activate other features that maximize performance, like usage of threads, `zlib-ng` and access to caching in object databases. ## Note that some platforms might suffer from compile failures, which is when `max-performance-safe` should be used. -max-performance = [ "max-performance-safe", "zlib-ng", "fast-sha1" ] +max-performance = ["max-performance-safe", "zlib-ng", "fast-sha1"] ## If enabled, use assembly versions of sha1 on supported platforms. ## This might cause compile failures as well which is why it can be turned off separately. -fast-sha1 = [ "gix-features/fast-sha1" ] +fast-sha1 = ["gix-features/fast-sha1"] ## Use the C-based zlib-ng backend, which can compress and decompress significantly faster. ## Note that this will cause duplicate symbol errors if the application also depends on `zlib` - use `zlib-ng-compat` in that case. @@ -215,7 +263,8 @@ zlib-stock = ["gix-features/zlib-stock"] verbose-object-parsing-errors = ["gix-object/verbose-object-parsing-errors"] ## Data structures implement `serde::Serialize` and `serde::Deserialize`. -serde = [ "dep:serde", +serde = [ + "dep:serde", "gix-pack/serde", "gix-object/serde", "gix-protocol?/serde", @@ -230,7 +279,8 @@ serde = [ "dep:serde", "gix-revision/serde", "gix-worktree?/serde", "gix-commitgraph/serde", - "gix-credentials?/serde"] + "gix-credentials?/serde", +] ## Re-export the progress tree root which allows to obtain progress from various functions which take `impl gix::Progress`. ## Applications which want to display progress will probably need this implementation. @@ -240,82 +290,86 @@ progress-tree = ["prodash/progress-tree"] cache-efficiency-debug = ["gix-features/cache-efficiency-debug"] - [dependencies] -gix-macros = { version = "^0.1.3", path = "../gix-macros" } -gix-utils = { version = "^0.1.10", path = "../gix-utils" } -gix-fs = { version = "^0.10.0", path = "../gix-fs" } -gix-ref = { version = "^0.42.0", path = "../gix-ref" } -gix-discover = { version = "^0.30.0", path = "../gix-discover" } -gix-tempfile = { version = "^13.0.0", path = "../gix-tempfile", default-features = false } -gix-lock = { version = "^13.0.0", path = "../gix-lock" } -gix-validate = { version = "^0.8.3", path = "../gix-validate" } -gix-sec = { version = "^0.10.5", path = "../gix-sec" } -gix-date = { version = "^0.8.4", path = "../gix-date" } -gix-refspec = { version = "^0.22.1", path = "../gix-refspec" } -gix-filter = { version = "^0.10.0", path = "../gix-filter", optional = true } -gix-dir = { version = "^0.1.0", path = "../gix-dir", optional = true } - -gix-config = { version = "^0.35.0", path = "../gix-config" } -gix-odb = { version = "^0.58.0", path = "../gix-odb" } -gix-hash = { version = "^0.14.1", path = "../gix-hash" } -gix-object = { version = "^0.41.1", path = "../gix-object" } -gix-actor = { version = "^0.30.1", path = "../gix-actor" } -gix-pack = { version = "^0.48.0", path = "../gix-pack", default-features = false, features = ["object-cache-dynamic"] } -gix-revision = { version = "^0.26.1", path = "../gix-revision", default-features = false } -gix-revwalk = { version = "^0.12.0", path = "../gix-revwalk" } -gix-negotiate = { version = "^0.12.0", path = "../gix-negotiate", optional = true } - -gix-path = { version = "^0.10.6", path = "../gix-path" } -gix-url = { version = "^0.27.1", path = "../gix-url" } -gix-traverse = { version = "^0.37.0", path = "../gix-traverse" } -gix-diff = { version = "^0.41.0", path = "../gix-diff", default-features = false } -gix-mailmap = { version = "^0.22.0", path = "../gix-mailmap", optional = true } -gix-features = { version = "^0.38.0", path = "../gix-features", features = ["progress", "once_cell"] } -gix-trace = { version = "^0.1.7", path = "../gix-trace" } - -gix-glob = { version = "^0.16.1", path = "../gix-glob" } -gix-credentials = { version = "^0.24.1", path = "../gix-credentials", optional = true } -gix-prompt = { version = "^0.8.3", path = "../gix-prompt", optional = true } -gix-index = { version = "^0.30.0", path = "../gix-index", optional = true } -gix-attributes = { version = "^0.22.1", path = "../gix-attributes", optional = true } -gix-ignore = { version = "^0.11.1", path = "../gix-ignore", optional = true } -gix-worktree = { version = "^0.31.0", path = "../gix-worktree", optional = true, default-features = false } -gix-worktree-state = { version = "^0.8.0", path = "../gix-worktree-state", optional = true } -gix-hashtable = { version = "^0.5.1", path = "../gix-hashtable" } -gix-commitgraph = { version = "^0.24.1", path = "../gix-commitgraph" } -gix-pathspec = { version = "^0.7.0", path = "../gix-pathspec", optional = true } -gix-submodule = { version = "^0.9.0", path = "../gix-submodule", optional = true } -gix-status = { version = "^0.6.0", path = "../gix-status", optional = true } -gix-command = { version = "^0.3.5", path = "../gix-command", optional = true } - -gix-worktree-stream = { version = "^0.10.0", path = "../gix-worktree-stream", optional = true } -gix-archive = { version = "^0.10.0", path = "../gix-archive", default-features = false, optional = true } +gix-macros = { version = "^0.1.5", path = "../gix-macros" } +gix-utils = { version = "^0.1.12", path = "../gix-utils" } +gix-fs = { version = "^0.11.1", path = "../gix-fs" } +gix-ref = { version = "^0.44.1", path = "../gix-ref" } +gix-discover = { version = "^0.32.0", path = "../gix-discover" } +gix-tempfile = { version = "^14.0.0", path = "../gix-tempfile", default-features = false } +gix-lock = { version = "^14.0.0", path = "../gix-lock" } +gix-validate = { version = "^0.8.5", path = "../gix-validate" } +gix-sec = { version = "^0.10.6", path = "../gix-sec" } +gix-date = { version = "^0.8.6", path = "../gix-date" } +gix-refspec = { version = "^0.23.0", path = "../gix-refspec" } +gix-filter = { version = "^0.11.2", path = "../gix-filter", optional = true } +gix-dir = { version = "^0.5.0", path = "../gix-dir", optional = true } + +gix-config = { version = "^0.37.0", path = "../gix-config" } +gix-odb = { version = "^0.61.0", path = "../gix-odb" } +gix-hash = { version = "^0.14.2", path = "../gix-hash" } +gix-object = { version = "^0.42.2", path = "../gix-object" } +gix-actor = { version = "^0.31.2", path = "../gix-actor" } +gix-pack = { version = "^0.51.0", path = "../gix-pack", default-features = false, features = [ + "object-cache-dynamic", +] } +gix-revision = { version = "^0.27.1", path = "../gix-revision", default-features = false } +gix-revwalk = { version = "^0.13.1", path = "../gix-revwalk" } +gix-negotiate = { version = "^0.13.1", path = "../gix-negotiate", optional = true } + +gix-path = { version = "^0.10.7", path = "../gix-path" } +gix-url = { version = "^0.27.3", path = "../gix-url" } +gix-traverse = { version = "^0.39.1", path = "../gix-traverse" } +gix-diff = { version = "^0.44.0", path = "../gix-diff", default-features = false } +gix-mailmap = { version = "^0.23.1", path = "../gix-mailmap", optional = true } +gix-features = { version = "^0.38.2", path = "../gix-features", features = [ + "progress", + "once_cell", +] } +gix-trace = { version = "^0.1.9", path = "../gix-trace" } + +gix-glob = { version = "^0.16.3", path = "../gix-glob" } +gix-credentials = { version = "^0.24.2", path = "../gix-credentials", optional = true } +gix-prompt = { version = "^0.8.5", path = "../gix-prompt", optional = true } +gix-index = { version = "^0.33.0", path = "../gix-index", optional = true } +gix-attributes = { version = "^0.22.2", path = "../gix-attributes", optional = true } +gix-ignore = { version = "^0.11.2", path = "../gix-ignore", optional = true } +gix-worktree = { version = "^0.34.0", path = "../gix-worktree", optional = true, default-features = false } +gix-worktree-state = { version = "^0.11.0", path = "../gix-worktree-state", optional = true } +gix-hashtable = { version = "^0.5.2", path = "../gix-hashtable" } +gix-commitgraph = { version = "^0.24.2", path = "../gix-commitgraph" } +gix-pathspec = { version = "^0.7.5", path = "../gix-pathspec", optional = true } +gix-submodule = { version = "^0.11.0", path = "../gix-submodule", optional = true } +gix-status = { version = "^0.10.0", path = "../gix-status", optional = true, features = [ + "worktree-rewrites", +] } +gix-command = { version = "^0.3.7", path = "../gix-command", optional = true } + +gix-worktree-stream = { version = "^0.13.0", path = "../gix-worktree-stream", optional = true } +gix-archive = { version = "^0.13.1", path = "../gix-archive", default-features = false, optional = true } # For communication with remotes -gix-protocol = { version = "^0.44.1", path = "../gix-protocol", optional = true } -gix-transport = { version = "^0.41.1", path = "../gix-transport", optional = true } +gix-protocol = { version = "^0.45.1", path = "../gix-protocol", optional = true } +gix-transport = { version = "^0.42.1", path = "../gix-transport", optional = true } # Just to get the progress-tree feature prodash = { workspace = true, optional = true, features = ["progress-tree"] } once_cell = "1.14.0" signal-hook = { version = "0.3.9", default-features = false, optional = true } thiserror = "1.0.26" -serde = { version = "1.0.114", optional = true, default-features = false, features = ["derive"]} +serde = { version = "1.0.114", optional = true, default-features = false, features = [ + "derive", +] } smallvec = "1.9.0" async-std = { version = "1.12.0", optional = true } -# Must match the one in `gix-transport`. -curl-for-configuration-only = { package = "curl", version = "0.4", optional = true } - ## For use in rev-parse, which provides searching commits by running a regex on their message. ## ## If disabled, the text will be search verbatim in any portion of the commit message, similar to ## how a simple unanchored regex of only 'normal' characters would work. -regex = { version = "1.6.0", optional = true, default-features = false, features = ["std"] } - -# For internal use to allow pure-Rust builds without openssl. -reqwest-for-configuration-only = { package = "reqwest", version = "0.11.13", default-features = false, optional = true } +regex = { version = "1.6.0", optional = true, default-features = false, features = [ + "std", +] } # for `interrupt` module parking_lot = "0.12.1" @@ -323,12 +377,19 @@ parking_lot = "0.12.1" document-features = { version = "0.2.0", optional = true } [dev-dependencies] +pretty_assertions = "1.4.0" gix-testtools = { path = "../tests/tools" } is_ci = "1.1.1" anyhow = "1" walkdir = "2.3.2" -serial_test = { version = "2.0.0", default-features = false } +serial_test = { version = "3.1.0", default-features = false } async-std = { version = "1.12.0", features = ["attributes"] } [package.metadata.docs.rs] -features = ["document-features", "max-performance", "blocking-network-client", "blocking-http-transport-curl", "serde"] +features = [ + "document-features", + "max-performance", + "blocking-network-client", + "blocking-http-transport-curl", + "serde", +] diff --git a/gix/src/attribute_stack.rs b/gix/src/attribute_stack.rs index 66497def6ae..bf9a1cafb18 100644 --- a/gix/src/attribute_stack.rs +++ b/gix/src/attribute_stack.rs @@ -33,33 +33,29 @@ impl DerefMut for AttributeStack<'_> { /// Platform retrieval impl<'repo> AttributeStack<'repo> { - /// Append the `relative` path to the root directory of the cache and efficiently create leading directories, while assuring that no - /// symlinks are in that path. - /// Unless `is_dir` is known with `Some(…)`, then `relative` points to a directory itself in which case the entire resulting - /// path is created as directory. If it's not known it is assumed to be a file. + /// Append the `relative` path to the root directory of the cache and load all attribute or ignore files on the way as needed. + /// Use `mode` to specify what kind of item lives at `relative` - directories may match against rules specifically. + /// If `mode` is `None`, the item at `relative` is assumed to be a file. /// - /// Provide access to cached information for that `relative` path via the returned platform. + /// The returned platform may be used to access the actual attribute or ignore information. + #[doc(alias = "is_path_ignored", alias = "git2")] pub fn at_path( &mut self, relative: impl AsRef, - is_dir: Option, + mode: Option, ) -> std::io::Result> { - self.inner.at_path(relative, is_dir, &self.repo.objects) + self.inner.at_path(relative, mode, &self.repo.objects) } - /// Obtain a platform for lookups from a repo-`relative` path, typically obtained from an index entry. `is_dir` should reflect - /// whether it's a directory or not, or left at `None` if unknown. + /// Obtain a platform for attribute or ignore lookups from a repo-`relative` path, typically obtained from an index entry. + /// `mode` should reflect whether it's a directory or not, or left at `None` if unknown. /// - /// If `relative` ends with `/` and `is_dir` is `None`, it is automatically assumed to be a directory. - /// - /// ### Panics - /// - /// - on illformed UTF8 in `relative` + /// If `relative` ends with `/` and `mode` is `None`, it is automatically assumed to be a directory. pub fn at_entry<'r>( &mut self, relative: impl Into<&'r BStr>, - is_dir: Option, + mode: Option, ) -> std::io::Result> { - self.inner.at_entry(relative, is_dir, &self.repo.objects) + self.inner.at_entry(relative, mode, &self.repo.objects) } } diff --git a/gix/src/clone/access.rs b/gix/src/clone/access.rs index 966c54b6671..ceada84472b 100644 --- a/gix/src/clone/access.rs +++ b/gix/src/clone/access.rs @@ -11,7 +11,7 @@ impl PrepareFetch { /// /// It can also be used to configure additional options, like those for fetching tags. Note that /// [`with_fetch_tags()`](crate::Remote::with_fetch_tags()) should be called here to configure the clone as desired. - /// Otherwise a clone is configured to be complete and fetches all tags, not only those reachable from all branches. + /// Otherwise, a clone is configured to be complete and fetches all tags, not only those reachable from all branches. pub fn configure_remote( mut self, f: impl FnMut(crate::Remote<'_>) -> Result, Box> + 'static, @@ -42,6 +42,19 @@ impl PrepareFetch { self.config_overrides = values.into_iter().map(Into::into).collect(); self } + + /// Set the `name` of the reference to check out, instead of the remote `HEAD`. + /// If `None`, the `HEAD` will be used, which is the default. + /// + /// Note that `name` should be a partial name like `main` or `feat/one`, but can be a full ref name. + /// If a branch on the remote matches, it will automatically be retrieved even without a refspec. + pub fn with_ref_name<'a, Name, E>(mut self, name: Option) -> Result + where + Name: TryInto<&'a gix_ref::PartialNameRef, Error = E>, + { + self.ref_name = name.map(TryInto::try_into).transpose()?.map(ToOwned::to_owned); + Ok(self) + } } /// Consumption diff --git a/gix/src/clone/checkout.rs b/gix/src/clone/checkout.rs index 84a3fedbddb..d980e02a873 100644 --- a/gix/src/clone/checkout.rs +++ b/gix/src/clone/checkout.rs @@ -1,6 +1,7 @@ use crate::{clone::PrepareCheckout, Repository}; /// +#[allow(clippy::empty_docs)] pub mod main_worktree { use std::{path::PathBuf, sync::atomic::AtomicBool}; @@ -17,14 +18,18 @@ pub mod main_worktree { #[error("Could not create index from tree at {id}")] IndexFromTree { id: gix_hash::ObjectId, - source: gix_traverse::tree::breadthfirst::Error, + source: gix_index::init::from_tree::Error, }, + #[error("Couldn't obtain configuration for core.protect*")] + BooleanConfig(#[from] crate::config::boolean::Error), #[error(transparent)] WriteIndex(#[from] gix_index::file::write::Error), #[error(transparent)] CheckoutOptions(#[from] crate::config::checkout_options::Error), #[error(transparent)] IndexCheckout(#[from] gix_worktree_state::checkout::Error), + #[error(transparent)] + Peel(#[from] crate::reference::peel::Error), #[error("Failed to reopen object database as Arc (only if thread-safety wasn't compiled in)")] OpenArcOdb(#[from] std::io::Error), #[error("The HEAD reference could not be located")] @@ -59,7 +64,13 @@ pub mod main_worktree { /// on thread per logical core. /// /// Note that this is a no-op if the remote was empty, leaving this repository empty as well. This can be validated by checking - /// if the `head()` of the returned repository is not unborn. + /// if the `head()` of the returned repository is *not* unborn. + /// + /// # Panics + /// + /// If called after it was successful. The reason here is that it auto-deletes the contained repository, + /// and keeps track of this by means of keeping just one repository instance, which is passed to the user + /// after success. pub fn main_worktree

( &mut self, mut progress: P, @@ -81,23 +92,31 @@ pub mod main_worktree { let repo = self .repo .as_ref() - .expect("still present as we never succeeded the worktree checkout yet"); + .expect("BUG: this method may only be called until it is successful"); let workdir = repo.work_dir().ok_or_else(|| Error::BareRepository { git_dir: repo.git_dir().to_owned(), })?; - let root_tree = match repo.head()?.try_peel_to_id_in_place()? { + + let root_tree_id = match &self.ref_name { + Some(reference_val) => Some(repo.find_reference(reference_val)?.peel_to_id_in_place()?), + None => repo.head()?.try_peel_to_id_in_place()?, + }; + + let root_tree = match root_tree_id { Some(id) => id.object().expect("downloaded from remote").peel_to_tree()?.id, None => { return Ok(( self.repo.take().expect("still present"), gix_worktree_state::checkout::Outcome::default(), - )) + )); } }; - let index = gix_index::State::from_tree(&root_tree, &repo.objects).map_err(|err| Error::IndexFromTree { - id: root_tree, - source: err, - })?; + + let index = gix_index::State::from_tree(&root_tree, &repo.objects, repo.config.protect_options()?) + .map_err(|err| Error::IndexFromTree { + id: root_tree, + source: err, + })?; let mut index = gix_index::File::from_state(index, repo.index_path()); let mut opts = repo @@ -125,7 +144,7 @@ pub mod main_worktree { bytes.show_throughput(start); index.write(Default::default())?; - Ok((self.repo.take().expect("still present"), outcome)) + Ok((self.repo.take().expect("still present").clone(), outcome)) } } } diff --git a/gix/src/clone/fetch/mod.rs b/gix/src/clone/fetch/mod.rs index d75648657ae..1fe50ceff8a 100644 --- a/gix/src/clone/fetch/mod.rs +++ b/gix/src/clone/fetch/mod.rs @@ -1,3 +1,5 @@ +use crate::bstr::BString; +use crate::bstr::ByteSlice; use crate::clone::PrepareFetch; /// The error returned by [`PrepareFetch::fetch_only()`]. @@ -35,6 +37,13 @@ pub enum Error { }, #[error("Failed to update HEAD with values from remote")] HeadUpdate(#[from] crate::reference::edit::Error), + #[error("The remote didn't have any ref that matched '{}'", wanted.as_ref().as_bstr())] + RefNameMissing { wanted: gix_ref::PartialName }, + #[error("The remote has {} refs for '{}', try to use a specific name: {}", candidates.len(), wanted.as_ref().as_bstr(), candidates.iter().filter_map(|n| n.to_str().ok()).collect::>().join(", "))] + RefNameAmbiguous { + wanted: gix_ref::PartialName, + candidates: Vec, + }, } /// Modification @@ -117,7 +126,7 @@ impl PrepareFetch { remote = remote.with_fetch_tags(fetch_tags); } - // Add HEAD after the remote was written to config, we need it to know what to checkout later, and assure + // Add HEAD after the remote was written to config, we need it to know what to check out later, and assure // the ref that HEAD points to is present no matter what. let head_refspec = gix_refspec::parse( format!("HEAD:refs/remotes/{remote_name}/HEAD").as_str().into(), @@ -136,10 +145,22 @@ impl PrepareFetch { if !opts.extra_refspecs.contains(&head_refspec) { opts.extra_refspecs.push(head_refspec) } + if let Some(ref_name) = &self.ref_name { + opts.extra_refspecs.push( + gix_refspec::parse(ref_name.as_ref().as_bstr(), gix_refspec::parse::Operation::Fetch) + .expect("partial names are valid refspecs") + .to_owned(), + ); + } opts }) .await? }; + + // Assure problems with custom branch names fail early, not after getting the pack or during negotiation. + if let Some(ref_name) = &self.ref_name { + util::find_custom_refname(pending_pack.ref_map(), ref_name)?; + } if pending_pack.ref_map().object_hash != repo.object_hash() { unimplemented!("configure repository to expect a different object hash as advertised by the server") } @@ -160,9 +181,10 @@ impl PrepareFetch { util::append_config_to_repo_config(repo, config); util::update_head( repo, - &outcome.ref_map.remote_refs, + &outcome.ref_map, reflog_message.as_ref(), remote_name.as_ref(), + self.ref_name.as_ref(), )?; Ok((self.repo.take().expect("still present"), outcome)) @@ -180,7 +202,13 @@ impl PrepareFetch { P::SubProgress: 'static, { let (repo, fetch_outcome) = self.fetch_only(progress, should_interrupt)?; - Ok((crate::clone::PrepareCheckout { repo: repo.into() }, fetch_outcome)) + Ok(( + crate::clone::PrepareCheckout { + repo: repo.into(), + ref_name: self.ref_name.clone(), + }, + fetch_outcome, + )) } } diff --git a/gix/src/clone/fetch/util.rs b/gix/src/clone/fetch/util.rs index 627201301de..fde5241edcd 100644 --- a/gix/src/clone/fetch/util.rs +++ b/gix/src/clone/fetch/util.rs @@ -1,8 +1,8 @@ -use std::{borrow::Cow, convert::TryInto, io::Write}; +use std::{borrow::Cow, io::Write}; use gix_ref::{ transaction::{LogChange, RefLog}, - FullNameRef, + FullNameRef, PartialName, }; use super::Error; @@ -60,35 +60,40 @@ pub fn append_config_to_repo_config(repo: &mut Repository, config: gix_config::F /// HEAD cannot be written by means of refspec by design, so we have to do it manually here. Also create the pointed-to ref /// if we have to, as it might not have been naturally included in the ref-specs. +/// Lastly, use `ref_name` if it was provided instead, and let `HEAD` point to it. pub fn update_head( repo: &mut Repository, - remote_refs: &[gix_protocol::handshake::Ref], + ref_map: &crate::remote::fetch::RefMap, reflog_message: &BStr, remote_name: &BStr, + ref_name: Option<&PartialName>, ) -> Result<(), Error> { use gix_ref::{ transaction::{PreviousValue, RefEdit}, Target, }; - let (head_peeled_id, head_ref) = match remote_refs.iter().find_map(|r| { - Some(match r { - gix_protocol::handshake::Ref::Symbolic { - full_ref_name, - target, - tag: _, - object, - } if full_ref_name == "HEAD" => (Some(object.as_ref()), Some(target)), - gix_protocol::handshake::Ref::Direct { full_ref_name, object } if full_ref_name == "HEAD" => { - (Some(object.as_ref()), None) - } - gix_protocol::handshake::Ref::Unborn { full_ref_name, target } if full_ref_name == "HEAD" => { - (None, Some(target)) - } - _ => return None, - }) - }) { - Some(t) => t, - None => return Ok(()), + let head_info = match ref_name { + Some(ref_name) => Some(find_custom_refname(ref_map, ref_name)?), + None => ref_map.remote_refs.iter().find_map(|r| { + Some(match r { + gix_protocol::handshake::Ref::Symbolic { + full_ref_name, + target, + tag: _, + object, + } if full_ref_name == "HEAD" => (Some(object.as_ref()), Some(target.as_bstr())), + gix_protocol::handshake::Ref::Direct { full_ref_name, object } if full_ref_name == "HEAD" => { + (Some(object.as_ref()), None) + } + gix_protocol::handshake::Ref::Unborn { full_ref_name, target } if full_ref_name == "HEAD" => { + (None, Some(target.as_bstr())) + } + _ => return None, + }) + }), + }; + let Some((head_peeled_id, head_ref)) = head_info else { + return Ok(()); }; let head: gix_ref::FullName = "HEAD".try_into().expect("valid"); @@ -178,7 +183,55 @@ pub fn update_head( Ok(()) } -/// Setup the remote configuration for `branch` so that it points to itself, but on the remote, if and only if currently +pub(super) fn find_custom_refname<'a>( + ref_map: &'a crate::remote::fetch::RefMap, + ref_name: &PartialName, +) -> Result<(Option<&'a gix_hash::oid>, Option<&'a BStr>), Error> { + let group = gix_refspec::MatchGroup::from_fetch_specs(Some( + gix_refspec::parse(ref_name.as_ref().as_bstr(), gix_refspec::parse::Operation::Fetch) + .expect("partial names are valid refs"), + )); + // TODO: to fix ambiguity, implement priority system + let filtered_items: Vec<_> = ref_map + .mappings + .iter() + .filter_map(|m| { + m.remote + .as_name() + .and_then(|name| m.remote.as_id().map(|id| (name, id))) + }) + .map(|(full_ref_name, target)| gix_refspec::match_group::Item { + full_ref_name, + target, + object: None, + }) + .collect(); + let res = group.match_remotes(filtered_items.iter().copied()); + match res.mappings.len() { + 0 => Err(Error::RefNameMissing { + wanted: ref_name.clone(), + }), + 1 => { + let item = filtered_items[res.mappings[0] + .item_index + .expect("we map by name only and have no object-id in refspec")]; + Ok((Some(item.target), Some(item.full_ref_name))) + } + _ => Err(Error::RefNameAmbiguous { + wanted: ref_name.clone(), + candidates: res + .mappings + .iter() + .filter_map(|m| match m.lhs { + gix_refspec::match_group::SourceRef::FullName(name) => Some(name.to_owned()), + gix_refspec::match_group::SourceRef::ObjectId(_) => None, + }) + .collect(), + }), + } +} + +/// Set up the remote configuration for `branch` so that it points to itself, but on the remote, if and only if currently /// saved refspecs are able to match it. /// For that we reload the remote of `remote_name` and use its `ref_specs` for match. fn setup_branch_config( diff --git a/gix/src/clone/mod.rs b/gix/src/clone/mod.rs index 42456e4d747..b8e9c1eaa1c 100644 --- a/gix/src/clone/mod.rs +++ b/gix/src/clone/mod.rs @@ -1,6 +1,4 @@ #![allow(clippy::result_large_err)] -use std::convert::TryInto; - use crate::{bstr::BString, config::tree::gitoxide, remote}; type ConfigureRemoteFn = @@ -36,6 +34,9 @@ pub struct PrepareFetch { /// How to handle shallow clones #[cfg_attr(not(feature = "blocking-network-client"), allow(dead_code))] shallow: remote::fetch::Shallow, + /// The name of the reference to fetch. If `None`, the reference pointed to by `HEAD` will be checked out. + #[cfg_attr(not(feature = "blocking-network-client"), allow(dead_code))] + ref_name: Option, } /// The error returned by [`PrepareFetch::new()`]. @@ -132,17 +133,21 @@ impl PrepareFetch { #[cfg(any(feature = "async-network-client", feature = "blocking-network-client"))] configure_connection: None, shallow: remote::fetch::Shallow::NoChange, + ref_name: None, }) } } -/// A utility to collect configuration on how to perform a checkout into a working tree, and when dropped without checking out successfully -/// the fetched repository will be dropped. +/// A utility to collect configuration on how to perform a checkout into a working tree, +/// and when dropped without checking out successfully the fetched repository will be deleted from disk. #[must_use] #[cfg(feature = "worktree-mutation")] +#[derive(Debug)] pub struct PrepareCheckout { - /// A freshly initialized repository which is owned by us, or `None` if it was handed to the user + /// A freshly initialized repository which is owned by us, or `None` if it was successfully checked out. pub(self) repo: Option, + /// The name of the reference to check out. If `None`, the reference pointed to by `HEAD` will be checked out. + pub(self) ref_name: Option, } // This module encapsulates functionality that works with both feature toggles. Can be combined with `fetch` diff --git a/gix/src/commit.rs b/gix/src/commit.rs index ce5dee4d6f7..a6f4da4488d 100644 --- a/gix/src/commit.rs +++ b/gix/src/commit.rs @@ -1,4 +1,5 @@ //! +#![allow(clippy::empty_docs)] /// An empty array of a type usable with the `gix::easy` API to help declaring no parents should be used pub const NO_PARENT_IDS: [gix_hash::ObjectId; 0] = []; @@ -22,6 +23,7 @@ pub enum Error { } /// +#[allow(clippy::empty_docs)] #[cfg(feature = "revision")] pub mod describe { use std::borrow::Cow; @@ -40,11 +42,32 @@ pub mod describe { } impl<'repo> Resolution<'repo> { - /// Turn this instance into something displayable + /// Turn this instance into something displayable. pub fn format(self) -> Result, Error> { let prefix = self.id.shorten()?; Ok(self.outcome.into_format(prefix.hex_len())) } + + /// Turn this instance into something displayable, possibly with dirty-suffix. + /// + /// If `dirty_suffix` is `Some(suffix)`, a possibly expensive [dirty check](crate::Repository::is_dirty()) will be + /// performed so that the `suffix` is appended to the output. If it is `None`, no check will be performed and + /// there will be no suffix. + /// Note that obtaining the dirty-state of the repository can be expensive. + #[cfg(feature = "status")] + pub fn format_with_dirty_suffix( + self, + dirty_suffix: impl Into>, + ) -> Result, Error> { + let prefix = self.id.shorten()?; + let mut dirty_suffix = dirty_suffix.into(); + if dirty_suffix.is_some() && !self.id.repo.is_dirty()? { + dirty_suffix.take(); + } + let mut format = self.outcome.into_format(prefix.hex_len()); + format.dirty_suffix = dirty_suffix; + Ok(format) + } } /// The error returned by [`try_format()`][Platform::try_format()]. @@ -59,6 +82,9 @@ pub mod describe { RefIter(#[from] crate::reference::iter::Error), #[error(transparent)] RefIterInit(#[from] crate::reference::iter::init::Error), + #[error(transparent)] + #[cfg(feature = "status")] + DetermineIsDirty(#[from] crate::status::is_dirty::Error), } /// A selector to choose what kind of references should contribute to names. @@ -179,9 +205,7 @@ pub mod describe { } /// Try to find a name for the configured commit id using all prior configuration, returning `Some(describe::Format)` - /// if one was found. - /// - /// Note that there will always be `Some(format)` + /// if one was found, or `None` if that wasn't the case. pub fn try_format(&self) -> Result>, Error> { self.try_resolve()?.map(Resolution::format).transpose() } @@ -193,10 +217,9 @@ pub mod describe { /// /// # Performance /// - /// It is greatly recommended to [assure an object cache is set][crate::Repository::object_cache_size_if_unset()] + /// It is greatly recommended to [assure an object cache is set](crate::Repository::object_cache_size_if_unset()) /// to save ~40% of time. pub fn try_resolve(&self) -> Result>, Error> { - // TODO: dirty suffix with respective dirty-detection let mut graph = gix_revwalk::Graph::new( &self.repo.objects, gix_commitgraph::Graph::from_info_dir(self.repo.objects.store_ref().path().join("info").as_ref()).ok(), @@ -218,7 +241,7 @@ pub mod describe { })) } - /// Like [`try_format()`][Platform::try_format()], but turns `id_as_fallback()` on to always produce a format. + /// Like [`try_format()`](Self::try_format()), but turns `id_as_fallback()` on to always produce a format. pub fn format(&mut self) -> Result, Error> { self.id_as_fallback = true; Ok(self.try_format()?.expect("BUG: fallback must always produce a format")) diff --git a/gix/src/config/cache/access.rs b/gix/src/config/cache/access.rs index 4d7f7298962..da588e01e38 100644 --- a/gix/src/config/cache/access.rs +++ b/gix/src/config/cache/access.rs @@ -111,7 +111,7 @@ impl Cache { } #[cfg(feature = "blob-diff")] - pub(crate) fn diff_renames(&self) -> Result, crate::diff::new_rewrites::Error> { + pub(crate) fn diff_renames(&self) -> Result, crate::diff::new_rewrites::Error> { self.diff_renames .get_or_try_init(|| crate::diff::new_rewrites(&self.resolved, self.lenient_config)) .copied() @@ -216,6 +216,13 @@ impl Cache { .resolved .path_filter(key, &mut self.filter_config_section.clone())?; + if self.lenient_config && path.is_empty() { + gix_trace::info!( + "Ignored empty path at {section_name}.{subsection_name:?}.{key} due to lenient configuration" + ); + return None; + } + let install_dir = crate::path::install_dir().ok(); let home = self.home_dir(); let ctx = config::cache::interpolate_context(install_dir.as_deref(), home.as_deref()); @@ -252,6 +259,36 @@ impl Cache { }) } + #[cfg(feature = "index")] + pub(crate) fn protect_options(&self) -> Result { + const IS_WINDOWS: bool = cfg!(windows); + const IS_MACOS: bool = cfg!(target_os = "macos"); + const ALWAYS_ON_FOR_SAFETY: bool = true; + Ok(gix_validate::path::component::Options { + protect_windows: config::tree::gitoxide::Core::PROTECT_WINDOWS + .enrich_error( + self.resolved + .boolean(config::tree::gitoxide::Core::PROTECT_WINDOWS) + .unwrap_or(Ok(IS_WINDOWS)), + ) + .with_lenient_default_value(self.lenient_config, IS_WINDOWS)?, + protect_hfs: config::tree::Core::PROTECT_HFS + .enrich_error( + self.resolved + .boolean(config::tree::Core::PROTECT_HFS) + .unwrap_or(Ok(IS_MACOS)), + ) + .with_lenient_default_value(self.lenient_config, IS_MACOS)?, + protect_ntfs: config::tree::Core::PROTECT_NTFS + .enrich_error( + self.resolved + .boolean(config::tree::Core::PROTECT_NTFS) + .unwrap_or(Ok(ALWAYS_ON_FOR_SAFETY)), + ) + .with_lenient_default_value(self.lenient_config, ALWAYS_ON_FOR_SAFETY)?, + }) + } + /// Collect everything needed to checkout files into a worktree. /// Note that some of the options being returned will be defaulted so safe settings, the caller might have to override them /// depending on the use-case. @@ -291,6 +328,7 @@ impl Cache { }; Ok(gix_worktree_state::checkout::Options { filter_process_delay, + validate: self.protect_options()?, filters, attributes: self .assemble_attribute_globals(git_dir, attributes_source, self.attributes)? diff --git a/gix/src/config/cache/incubate.rs b/gix/src/config/cache/incubate.rs index 926cbf8cd78..7b4dc6d41b8 100644 --- a/gix/src/config/cache/incubate.rs +++ b/gix/src/config/cache/incubate.rs @@ -1,8 +1,8 @@ #![allow(clippy::result_large_err)] use super::{util, Error}; -use crate::config::cache::util::ApplyLeniency; -use crate::config::tree::{Core, Extensions}; +use crate::config::cache::util::{ApplyLeniency, ApplyLeniencyDefaultValue}; +use crate::config::tree::{gitoxide, Core, Extensions}; /// A utility to deal with the cyclic dependency between the ref store and the configuration. The ref-store needs the /// object hash kind, and the configuration needs the current branch name to resolve conditional includes with `onbranch`. @@ -15,6 +15,7 @@ pub(crate) struct StageOne { pub object_hash: gix_hash::Kind, pub reflog: Option, pub precompose_unicode: bool, + pub protect_windows: bool, } /// Initialization @@ -80,6 +81,15 @@ impl StageOne { .map_err(Error::ConfigBoolean)? .unwrap_or_default(); + const IS_WINDOWS: bool = cfg!(windows); + let protect_windows = gitoxide::Core::PROTECT_WINDOWS + .enrich_error( + config + .boolean(gitoxide::Core::PROTECT_WINDOWS) + .unwrap_or(Ok(IS_WINDOWS)), + ) + .with_lenient_default_value(lenient, IS_WINDOWS)?; + let reflog = util::query_refupdates(&config, lenient)?; Ok(StageOne { git_dir_config: config, @@ -89,6 +99,7 @@ impl StageOne { object_hash, reflog, precompose_unicode, + protect_windows, }) } } diff --git a/gix/src/config/cache/init.rs b/gix/src/config/cache/init.rs index 449eb1611e3..56eb50e7059 100644 --- a/gix/src/config/cache/init.rs +++ b/gix/src/config/cache/init.rs @@ -28,6 +28,7 @@ impl Cache { object_hash, reflog: _, precompose_unicode: _, + protect_windows: _, }: StageOne, git_dir: &std::path::Path, branch_name: Option<&gix_ref::FullNameRef>, @@ -303,7 +304,7 @@ impl crate::Repository { fn apply_changed_values(&mut self) { self.refs.write_reflog = util::reflog_or_default(self.config.reflog, self.work_dir().is_some()); - self.refs.namespace = self.config.refs_namespace.clone(); + self.refs.namespace.clone_from(&self.config.refs_namespace); } } diff --git a/gix/src/config/cache/util.rs b/gix/src/config/cache/util.rs index 0fa7493baeb..75e3310a9de 100644 --- a/gix/src/config/cache/util.rs +++ b/gix/src/config/cache/util.rs @@ -132,10 +132,6 @@ pub trait ApplyLeniency { fn with_leniency(self, is_lenient: bool) -> Self; } -pub trait IgnoreEmptyPath { - fn ignore_empty(self) -> Self; -} - pub trait ApplyLeniencyDefault { fn with_lenient_default(self, is_lenient: bool) -> Self; } @@ -154,16 +150,6 @@ impl ApplyLeniency for Result, E> { } } -impl IgnoreEmptyPath for Result>, gix_config::path::interpolate::Error> { - fn ignore_empty(self) -> Self { - match self { - Ok(maybe_path) => Ok(maybe_path), - Err(gix_config::path::interpolate::Error::Missing { .. }) => Ok(None), - Err(err) => Err(err), - } - } -} - impl ApplyLeniencyDefault for Result where T: Default, diff --git a/gix/src/config/mod.rs b/gix/src/config/mod.rs index ad23a309752..f7b8860d1fd 100644 --- a/gix/src/config/mod.rs +++ b/gix/src/config/mod.rs @@ -9,6 +9,7 @@ mod snapshot; pub use snapshot::credential_helpers; /// +#[allow(clippy::empty_docs)] pub mod overrides; pub mod tree; @@ -48,6 +49,7 @@ pub(crate) mod section { } /// +#[allow(clippy::empty_docs)] pub mod set_value { /// The error produced when calling [`SnapshotMut::set(_subsection)?_value()`][crate::config::SnapshotMut::set_value()] #[derive(Debug, thiserror::Error)] @@ -107,8 +109,10 @@ pub enum Error { } /// +#[allow(clippy::empty_docs)] pub mod diff { /// + #[allow(clippy::empty_docs)] pub mod algorithm { use crate::bstr::BString; @@ -124,6 +128,7 @@ pub mod diff { } /// + #[allow(clippy::empty_docs)] pub mod pipeline_options { /// The error produced when obtaining options needed to fill in [gix_diff::blob::pipeline::Options]. #[derive(Debug, thiserror::Error)] @@ -137,6 +142,7 @@ pub mod diff { } /// + #[allow(clippy::empty_docs)] pub mod drivers { use crate::bstr::BString; @@ -155,6 +161,7 @@ pub mod diff { } /// +#[allow(clippy::empty_docs)] pub mod stat_options { /// The error produced when collecting stat information, and returned by [Repository::stat_options()](crate::Repository::stat_options()). #[derive(Debug, thiserror::Error)] @@ -207,6 +214,7 @@ pub mod command_context { } /// +#[allow(clippy::empty_docs)] pub mod exclude_stack { use std::path::PathBuf; @@ -224,6 +232,7 @@ pub mod exclude_stack { } /// +#[allow(clippy::empty_docs)] pub mod attribute_stack { /// The error produced when setting up the attribute stack to query `gitattributes`. #[derive(Debug, thiserror::Error)] @@ -237,8 +246,10 @@ pub mod attribute_stack { } /// +#[allow(clippy::empty_docs)] pub mod protocol { /// + #[allow(clippy::empty_docs)] pub mod allow { use crate::bstr::BString; @@ -254,6 +265,7 @@ pub mod protocol { } /// +#[allow(clippy::empty_docs)] pub mod ssh_connect_options { /// The error produced when obtaining ssh connection configuration. #[derive(Debug, thiserror::Error)] @@ -263,6 +275,7 @@ pub mod ssh_connect_options { } /// +#[allow(clippy::empty_docs)] pub mod key { use crate::bstr::BString; @@ -363,6 +376,7 @@ pub mod key { } /// +#[allow(clippy::empty_docs)] pub mod encoding { use crate::bstr::BString; @@ -380,8 +394,10 @@ pub mod encoding { } /// +#[allow(clippy::empty_docs)] pub mod checkout { /// + #[allow(clippy::empty_docs)] pub mod workers { use crate::config; @@ -391,6 +407,7 @@ pub mod checkout { } /// +#[allow(clippy::empty_docs)] pub mod abbrev { use crate::bstr::BString; @@ -406,8 +423,10 @@ pub mod abbrev { } /// +#[allow(clippy::empty_docs)] pub mod remote { /// + #[allow(clippy::empty_docs)] pub mod symbolic_name { /// The error produced when failing to produce a symbolic remote name from configuration. pub type Error = super::super::key::Error; @@ -415,66 +434,77 @@ pub mod remote { } /// +#[allow(clippy::empty_docs)] pub mod time { /// The error produced when failing to parse time from configuration. pub type Error = super::key::Error; } /// +#[allow(clippy::empty_docs)] pub mod lock_timeout { /// The error produced when failing to parse timeout for locks. pub type Error = super::key::Error; } /// +#[allow(clippy::empty_docs)] pub mod duration { /// The error produced when failing to parse durations (in milliseconds). pub type Error = super::key::Error; } /// +#[allow(clippy::empty_docs)] pub mod boolean { /// The error produced when failing to parse time from configuration. pub type Error = super::key::Error; } /// +#[allow(clippy::empty_docs)] pub mod unsigned_integer { /// The error produced when failing to parse a signed integer from configuration. pub type Error = super::key::Error; } /// +#[allow(clippy::empty_docs)] pub mod url { /// The error produced when failing to parse a url from the configuration. pub type Error = super::key::Error; } /// +#[allow(clippy::empty_docs)] pub mod string { /// The error produced when failing to interpret configuration as UTF-8 encoded string. pub type Error = super::key::Error; } /// +#[allow(clippy::empty_docs)] pub mod refspec { /// The error produced when failing to parse a refspec from the configuration. pub type Error = super::key::Error; } /// +#[allow(clippy::empty_docs)] pub mod refs_namespace { /// The error produced when failing to parse a refspec from the configuration. pub type Error = super::key::Error; } /// +#[allow(clippy::empty_docs)] pub mod ssl_version { /// The error produced when failing to parse a refspec from the configuration. pub type Error = super::key::Error; } /// +#[allow(clippy::empty_docs)] pub mod transport { use std::borrow::Cow; @@ -514,6 +544,7 @@ pub mod transport { } /// + #[allow(clippy::empty_docs)] pub mod http { use std::borrow::Cow; diff --git a/gix/src/config/snapshot/credential_helpers.rs b/gix/src/config/snapshot/credential_helpers.rs index 54499a1c34a..f84efa896e8 100644 --- a/gix/src/config/snapshot/credential_helpers.rs +++ b/gix/src/config/snapshot/credential_helpers.rs @@ -1,4 +1,4 @@ -use std::{borrow::Cow, convert::TryFrom}; +use std::borrow::Cow; pub use error::Error; @@ -6,7 +6,6 @@ use crate::config::cache::util::ApplyLeniency; use crate::{ bstr::{ByteSlice, ByteVec}, config::{ - cache::util::IgnoreEmptyPath, tree::{credential, gitoxide::Credentials, Core, Credential, Key}, Snapshot, }, @@ -197,3 +196,17 @@ fn normalize(url: &mut gix_url::Url) { url.path.pop(); } } + +trait IgnoreEmptyPath { + fn ignore_empty(self) -> Self; +} + +impl IgnoreEmptyPath for Result>, gix_config::path::interpolate::Error> { + fn ignore_empty(self) -> Self { + match self { + Ok(maybe_path) => Ok(maybe_path), + Err(gix_config::path::interpolate::Error::Missing { .. }) => Ok(None), + Err(err) => Err(err), + } + } +} diff --git a/gix/src/config/tree/mod.rs b/gix/src/config/tree/mod.rs index 6610a091fb3..7340da3dd1b 100644 --- a/gix/src/config/tree/mod.rs +++ b/gix/src/config/tree/mod.rs @@ -59,6 +59,9 @@ pub(crate) mod root { pub const SAFE: sections::Safe = sections::Safe; /// The `ssh` section. pub const SSH: sections::Ssh = sections::Ssh; + /// The `status` section. + #[cfg(feature = "status")] + pub const STATUS: sections::Status = sections::Status; /// The `user` section. pub const USER: sections::User = sections::User; /// The `url` section. @@ -89,6 +92,8 @@ pub(crate) mod root { &Self::REMOTE, &Self::SAFE, &Self::SSH, + #[cfg(feature = "status")] + &Self::STATUS, &Self::USER, &Self::URL, ] @@ -104,13 +109,17 @@ pub use sections::{ }; #[cfg(feature = "blob-diff")] pub use sections::{diff, Diff}; +#[cfg(feature = "status")] +pub use sections::{status, Status}; /// Generic value implementations for static instantiation. pub mod keys; /// +#[allow(clippy::empty_docs)] pub mod key { /// + #[allow(clippy::empty_docs)] pub mod validate { /// The error returned by [`Key::validate()`][crate::config::tree::Key::validate()]. #[derive(Debug, thiserror::Error)] @@ -122,6 +131,7 @@ pub mod key { } } /// + #[allow(clippy::empty_docs)] pub mod validate_assignment { /// The error returned by [`Key::validated_assignment`*()][crate::config::tree::Key::validated_assignment_fmt()]. #[derive(Debug, thiserror::Error)] diff --git a/gix/src/config/tree/sections/branch.rs b/gix/src/config/tree/sections/branch.rs index 8e1e0a4b862..e63227fc90f 100644 --- a/gix/src/config/tree/sections/branch.rs +++ b/gix/src/config/tree/sections/branch.rs @@ -49,6 +49,7 @@ mod merge { } /// +#[allow(clippy::empty_docs)] pub mod validate { use crate::{ bstr::BStr, diff --git a/gix/src/config/tree/sections/checkout.rs b/gix/src/config/tree/sections/checkout.rs index 27f31ee841f..03d274b918c 100644 --- a/gix/src/config/tree/sections/checkout.rs +++ b/gix/src/config/tree/sections/checkout.rs @@ -42,6 +42,7 @@ mod workers { } /// +#[allow(clippy::empty_docs)] pub mod validate { use crate::{bstr::BStr, config::tree::keys}; diff --git a/gix/src/config/tree/sections/core.rs b/gix/src/config/tree/sections/core.rs index 5a63020b11c..d847b4ed75f 100644 --- a/gix/src/config/tree/sections/core.rs +++ b/gix/src/config/tree/sections/core.rs @@ -44,6 +44,10 @@ impl Core { /// Needs application to use [`env::args_os`][crate::env::args_os()] to conform all input paths before they are used. pub const PRECOMPOSE_UNICODE: keys::Boolean = keys::Boolean::new_boolean("precomposeUnicode", &config::Tree::CORE) .with_note("application needs to conform all program input by using gix::env::args_os()"); + /// The `core.protectHFS` key. + pub const PROTECT_HFS: keys::Boolean = keys::Boolean::new_boolean("protectHFS", &config::Tree::CORE); + /// The `core.protectNTFS` key. + pub const PROTECT_NTFS: keys::Boolean = keys::Boolean::new_boolean("protectNTFS", &config::Tree::CORE); /// The `core.repositoryFormatVersion` key. pub const REPOSITORY_FORMAT_VERSION: keys::UnsignedInteger = keys::UnsignedInteger::new_unsigned_integer("repositoryFormatVersion", &config::Tree::CORE); @@ -116,6 +120,8 @@ impl Section for Core { &Self::SYMLINKS, &Self::TRUST_C_TIME, &Self::WORKTREE, + &Self::PROTECT_HFS, + &Self::PROTECT_NTFS, &Self::ASKPASS, &Self::EXCLUDES_FILE, &Self::ATTRIBUTES_FILE, diff --git a/gix/src/config/tree/sections/gitoxide.rs b/gix/src/config/tree/sections/gitoxide.rs index a3b05441263..37c706af0e6 100644 --- a/gix/src/config/tree/sections/gitoxide.rs +++ b/gix/src/config/tree/sections/gitoxide.rs @@ -103,6 +103,10 @@ mod subsections { pub const USE_STDEV: keys::Boolean = keys::Boolean::new_boolean("useStdev", &Gitoxide::CORE) .with_note("A runtime version of the USE_STDEV build flag."); + /// The `gitoxide.core.protectWindows` key. + pub const PROTECT_WINDOWS: keys::Boolean = keys::Boolean::new_boolean("protectWindows", &Gitoxide::CORE) + .with_note("enable protections that are enabled by default on Windows"); + /// The `gitoxide.core.shallowFile` key. pub const SHALLOW_FILE: keys::Path = keys::Path::new_path("shallowFile", &Gitoxide::CORE) .with_environment_override("GIT_SHALLOW_FILE") @@ -142,6 +146,7 @@ mod subsections { &Self::USE_NSEC, &Self::USE_STDEV, &Self::SHALLOW_FILE, + &Self::PROTECT_WINDOWS, &Self::FILTER_PROCESS_DELAY, &Self::EXTERNAL_COMMAND_STDERR, &Self::REFS_NAMESPACE, diff --git a/gix/src/config/tree/sections/mod.rs b/gix/src/config/tree/sections/mod.rs index de6b44d4c1d..ab2b9542a2c 100644 --- a/gix/src/config/tree/sections/mod.rs +++ b/gix/src/config/tree/sections/mod.rs @@ -106,6 +106,13 @@ mod safe; pub struct Ssh; pub mod ssh; +/// The `status` top-level section. +#[derive(Copy, Clone, Default)] +#[cfg(feature = "status")] +pub struct Status; +#[cfg(feature = "status")] +pub mod status; + /// The `user` top-level section. #[derive(Copy, Clone, Default)] pub struct User; diff --git a/gix/src/config/tree/sections/status.rs b/gix/src/config/tree/sections/status.rs new file mode 100644 index 00000000000..f60600e214b --- /dev/null +++ b/gix/src/config/tree/sections/status.rs @@ -0,0 +1,58 @@ +use crate::config; +use crate::config::tree::sections::Status; +use crate::config::tree::{keys, Key, Section}; + +impl Status { + /// The `status.showUntrackedFiles` key + pub const SHOW_UNTRACKED_FILES: ShowUntrackedFiles = ShowUntrackedFiles::new_with_validate( + "showUntrackedFiles", + &config::Tree::STATUS, + validate::ShowUntrackedFiles, + ); +} + +/// The `status.showUntrackedFiles` key. +pub type ShowUntrackedFiles = keys::Any; + +mod show_untracked_files { + use std::borrow::Cow; + + use crate::{bstr::BStr, config, config::tree::status::ShowUntrackedFiles, status}; + + impl ShowUntrackedFiles { + pub fn try_into_show_untracked_files( + &'static self, + value: Cow<'_, BStr>, + ) -> Result { + use crate::bstr::ByteSlice; + Ok(match value.as_ref().as_bytes() { + b"no" => status::UntrackedFiles::None, + b"normal" => status::UntrackedFiles::Collapsed, + b"all" => status::UntrackedFiles::Files, + _ => return Err(config::key::GenericErrorWithValue::from_value(self, value.into_owned())), + }) + } + } +} + +impl Section for Status { + fn name(&self) -> &str { + "status" + } + + fn keys(&self) -> &[&dyn Key] { + &[&Self::SHOW_UNTRACKED_FILES] + } +} + +mod validate { + use crate::{bstr::BStr, config::tree::keys}; + + pub struct ShowUntrackedFiles; + impl keys::Validate for ShowUntrackedFiles { + fn validate(&self, value: &BStr) -> Result<(), Box> { + super::Status::SHOW_UNTRACKED_FILES.try_into_show_untracked_files(value.into())?; + Ok(()) + } + } +} diff --git a/gix/src/create.rs b/gix/src/create.rs index b03045cddf7..f2e97aac57b 100644 --- a/gix/src/create.rs +++ b/gix/src/create.rs @@ -1,5 +1,4 @@ use std::{ - convert::TryFrom, fs::{self, OpenOptions}, io::Write, path::{Path, PathBuf}, @@ -89,6 +88,7 @@ fn write_file(data: &[u8], path: &Path) -> Result<(), Error> { let mut file = OpenOptions::new() .write(true) .create(true) + .truncate(true) .append(false) .open(path) .map_err(|e| Error::IoOpen { diff --git a/gix/src/diff.rs b/gix/src/diff.rs index 6f8c994488b..2f26e1b114c 100644 --- a/gix/src/diff.rs +++ b/gix/src/diff.rs @@ -1,6 +1,7 @@ pub use gix_diff::*; /// +#[allow(clippy::empty_docs)] pub mod rename { /// Determine how to do rename tracking. #[derive(Debug, Copy, Clone, Eq, PartialEq)] @@ -28,6 +29,7 @@ mod utils { }; /// + #[allow(clippy::empty_docs)] pub mod new_rewrites { /// The error returned by [`new_rewrites()`](super::new_rewrites()). #[derive(Debug, thiserror::Error)] @@ -41,6 +43,7 @@ mod utils { } /// + #[allow(clippy::empty_docs)] pub mod resource_cache { /// The error returned by [`resource_cache()`](super::resource_cache()). #[derive(Debug, thiserror::Error)] @@ -56,8 +59,6 @@ mod utils { DiffPipelineOptions(#[from] crate::config::diff::pipeline_options::Error), #[error(transparent)] CommandContext(#[from] crate::config::command_context::Error), - #[error(transparent)] - AttributeStack(#[from] crate::config::attribute_stack::Error), } } @@ -99,21 +100,19 @@ mod utils { .into()) } - /// Return a low-level utility to efficiently prepare a the blob-level diff operation between two resources, + /// Return a low-level utility to efficiently prepare a blob-level diff operation between two resources, /// and cache these diffable versions so that matrix-like MxN diffs are efficient. /// - /// `repo` is used to obtain the needed configuration values, and `index` is used to potentially read `.gitattributes` - /// files from which may affect the diff operation. + /// `repo` is used to obtain the needed configuration values. /// `mode` determines how the diffable files will look like, and also how fast, in average, these conversions are. - /// `attribute_source` controls where `.gitattributes` will be read from, and it's typically adjusted based on the + /// `attr_stack` is for accessing `.gitattributes` for knowing how to apply filters. Know that it's typically adjusted based on the /// `roots` - if there are no worktree roots, `.gitattributes` are also not usually read from worktrees. /// `roots` provide information about where to get diffable data from, so source and destination can either be sourced from /// a worktree, or from the object database, or both. pub fn resource_cache( repo: &Repository, - index: &gix_index::State, mode: gix_diff::blob::pipeline::Mode, - attribute_source: gix_worktree::stack::state::attributes::Source, + attr_stack: gix_worktree::Stack, roots: gix_diff::blob::pipeline::WorktreeRoots, ) -> Result { let diff_algo = repo.config.diff_algorithm()?; @@ -129,13 +128,7 @@ mod utils { repo.config.diff_pipeline_options()?, ), mode, - repo.attributes_only( - // TODO(perf): this could benefit from not having to build an intermediate index, - // and traverse the a tree directly. - index, - attribute_source, - )? - .inner, + attr_stack, ); Ok(diff_cache) } diff --git a/gix/src/dirwalk/iter.rs b/gix/src/dirwalk/iter.rs new file mode 100644 index 00000000000..6bfde0ef8f7 --- /dev/null +++ b/gix/src/dirwalk/iter.rs @@ -0,0 +1,188 @@ +use super::Iter; +use crate::bstr::BString; +use crate::util::OwnedOrStaticAtomicBool; +use crate::worktree::IndexPersistedOrInMemory; +use crate::{dirwalk, PathspecDetached, Repository}; +use std::path::PathBuf; + +/// An entry of the directory walk as returned by the [iterator](Iter). +pub struct Item { + /// The directory entry. + pub entry: gix_dir::Entry, + /// `collapsed_directory_status` is `Some(dir_status)` if this entry was part of a directory with the given + /// `dir_status` that wasn't the same as the one of `entry` and if [gix_dir::walk::Options::emit_collapsed] was + /// [gix_dir::walk::CollapsedEntriesEmissionMode::OnStatusMismatch]. It will also be `Some(dir_status)` if that option + /// was [gix_dir::walk::CollapsedEntriesEmissionMode::All]. + pub collapsed_directory_status: Option, +} + +impl Item { + fn new(entry: gix_dir::EntryRef<'_>, collapsed_directory_status: Option) -> Self { + Item { + entry: entry.to_owned(), + collapsed_directory_status, + } + } +} + +/// The outcome of fully consumed [dirwalk iterator](Iter). +pub struct Outcome { + /// The index originally passed in to create the iterator. + pub index: IndexPersistedOrInMemory, + /// The excludes stack used for the dirwalk, for access of `.gitignore` information. + pub excludes: gix_worktree::Stack, + /// The pathspecs used to guide the operation, + pub pathspec: PathspecDetached, + /// The root actually being used for the traversal, and useful to transform the paths returned for the user. + /// It's always within the [`work-dir`](Repository::work_dir). + pub traversal_root: PathBuf, + /// The actual result of the dirwalk. + pub dirwalk: gix_dir::walk::Outcome, +} + +/// The error returned by [Repository::dirwalk_iter()]. +#[derive(Debug, thiserror::Error)] +#[allow(missing_docs)] +pub enum Error { + #[error("Failed to spawn producer thread")] + #[cfg(feature = "parallel")] + SpawnThread(#[from] std::io::Error), + #[error(transparent)] + #[cfg(not(feature = "parallel"))] + Dirwalk(#[from] dirwalk::Error), + #[error(transparent)] + #[cfg(not(feature = "parallel"))] + DetachPathSpec(#[from] std::io::Error), +} + +/// Lifecycle +impl Iter { + pub(crate) fn new( + repo: &Repository, + index: IndexPersistedOrInMemory, + patterns: Vec, + should_interrupt: OwnedOrStaticAtomicBool, + options: dirwalk::Options, + ) -> Result { + #[cfg(feature = "parallel")] + { + let repo = repo.clone().into_sync(); + let (tx, rx) = std::sync::mpsc::channel(); + let handle = std::thread::Builder::new() + .name("gix::dirwalk::iter::producer".into()) + .spawn({ + let should_interrupt = should_interrupt.clone(); + move || -> Result { + let repo: Repository = repo.into(); + let mut collect = Collect { tx }; + let out = repo.dirwalk(&index, patterns, &should_interrupt, options, &mut collect)?; + Ok(Outcome { + index, + excludes: out.excludes.detach(), + pathspec: out.pathspec.detach().map_err(|err| { + dirwalk::Error::Walk(gix_dir::walk::Error::ReadDir { + path: repo.git_dir().to_owned(), + source: err, + }) + })?, + traversal_root: out.traversal_root, + dirwalk: out.dirwalk, + }) + } + })?; + + Ok(Iter { + rx_and_join: Some((rx, handle)), + should_interrupt, + out: None, + }) + } + #[cfg(not(feature = "parallel"))] + { + let mut collect = Collect { items: Vec::new() }; + let out = repo.dirwalk(&index, patterns, &should_interrupt, options, &mut collect)?; + let out = Outcome { + index, + excludes: out.excludes.detach(), + pathspec: out.pathspec.detach()?, + traversal_root: out.traversal_root, + dirwalk: out.dirwalk, + }; + + Ok(Iter { + items: collect.items.into_iter(), + out: Some(out), + }) + } + } +} + +/// Access +impl Iter { + /// Return the outcome of the iteration, or `None` if the iterator isn't fully consumed. + pub fn outcome_mut(&mut self) -> Option<&mut Outcome> { + self.out.as_mut() + } + + /// Turn the iterator into the iteration outcome, which is `None` on error or if the iteration + /// isn't complete. + pub fn into_outcome(mut self) -> Option { + self.out.take() + } +} + +impl Iterator for Iter { + type Item = Result; + + fn next(&mut self) -> Option { + #[cfg(feature = "parallel")] + { + let (rx, _join) = self.rx_and_join.as_ref()?; + match rx.recv().ok() { + Some(item) => Some(Ok(item)), + None => { + let (_rx, handle) = self.rx_and_join.take()?; + match handle.join().expect("no panic") { + Ok(out) => { + self.out = Some(out); + None + } + Err(err) => Some(Err(err)), + } + } + } + } + #[cfg(not(feature = "parallel"))] + self.items.next().map(Ok) + } +} + +#[cfg(feature = "parallel")] +impl Drop for Iter { + fn drop(&mut self) { + crate::util::parallel_iter_drop(self.rx_and_join.take(), &self.should_interrupt); + } +} + +struct Collect { + #[cfg(feature = "parallel")] + tx: std::sync::mpsc::Sender, + #[cfg(not(feature = "parallel"))] + items: Vec, +} + +impl gix_dir::walk::Delegate for Collect { + fn emit( + &mut self, + entry: gix_dir::EntryRef<'_>, + collapsed_directory_status: Option, + ) -> gix_dir::walk::Action { + // NOTE: we assume that the receiver triggers interruption so the operation will stop if the receiver is down. + let item = Item::new(entry, collapsed_directory_status); + #[cfg(feature = "parallel")] + self.tx.send(item).ok(); + #[cfg(not(feature = "parallel"))] + self.items.push(item); + gix_dir::walk::Action::Continue + } +} diff --git a/gix/src/dirwalk/mod.rs b/gix/src/dirwalk/mod.rs new file mode 100644 index 00000000000..4d590c3f36c --- /dev/null +++ b/gix/src/dirwalk/mod.rs @@ -0,0 +1,91 @@ +use gix_dir::walk::{CollapsedEntriesEmissionMode, EmissionMode, ForDeletionMode}; + +use crate::{config, AttributeStack, Pathspec}; +use std::path::PathBuf; + +mod options; + +/// +#[allow(clippy::empty_docs)] +pub mod iter; + +/// An iterator for entries in a directory walk. +/// +/// ### Parallel Operation +/// +/// Note that without the `parallel` feature, the iterator becomes 'serial', which means that all entries will be traversed +/// in advance and it cannot be interrupted unless the interrupt flag is set from another thread. +/// +/// It's a crutch that is just there to make single-threaded applications possible at all, as it's not really an iterator +/// anymore. If this matters, better run [Repository::dirwalk()](crate::Repository::dirwalk) by hand as it provides all +/// control one would need, just not as an iterator. +/// +/// Also, even with `parallel` set, the first call to `next()` will block until there is an item available, without a chance +/// to interrupt unless the interrupt flag is set from another thread. +pub struct Iter { + #[cfg(feature = "parallel")] + #[allow(clippy::type_complexity)] + rx_and_join: Option<( + std::sync::mpsc::Receiver, + std::thread::JoinHandle>, + )>, + #[cfg(feature = "parallel")] + should_interrupt: crate::util::OwnedOrStaticAtomicBool, + /// Without parallelization, the iterator has to buffer all changes in advance. + #[cfg(not(feature = "parallel"))] + items: std::vec::IntoIter, + /// The outcome of the operation, only available once the operation has ended. + out: Option, +} + +/// The error returned by [dirwalk()](crate::Repository::dirwalk()). +#[derive(Debug, thiserror::Error)] +#[allow(missing_docs)] +pub enum Error { + #[error(transparent)] + Walk(#[from] gix_dir::walk::Error), + #[error("A working tree is required to perform a directory walk")] + MissingWorkDir, + #[error(transparent)] + Excludes(#[from] config::exclude_stack::Error), + #[error(transparent)] + Pathspec(#[from] crate::pathspec::init::Error), + #[error(transparent)] + Prefix(#[from] gix_path::realpath::Error), + #[error(transparent)] + FilesystemOptions(#[from] config::boolean::Error), +} + +/// The outcome of the [dirwalk()](crate::Repository::dirwalk). +pub struct Outcome<'repo> { + /// The excludes stack used for the dirwalk, for access of `.gitignore` information. + pub excludes: AttributeStack<'repo>, + /// The pathspecs used to guide the operation, + pub pathspec: Pathspec<'repo>, + /// The root actually being used for the traversal, and useful to transform the paths returned for the user. + /// It's always within the [`work-dir`](crate::Repository::work_dir). + pub traversal_root: PathBuf, + /// The actual result of the dirwalk. + pub dirwalk: gix_dir::walk::Outcome, +} + +/// Options for use in the [`Repository::dirwalk()`](crate::Repository::dirwalk()) function. +/// +/// Note that all values start out disabled. +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Ord, PartialOrd)] +pub struct Options { + precompose_unicode: bool, + ignore_case: bool, + + recurse_repositories: bool, + emit_pruned: bool, + emit_ignored: Option, + for_deletion: Option, + emit_tracked: bool, + emit_untracked: EmissionMode, + emit_empty_directories: bool, + classify_untracked_bare_repositories: bool, + emit_collapsed: Option, + symlinks_to_directories_are_ignored_like_directories: bool, + pub(crate) empty_patterns_match_prefix: bool, +} diff --git a/gix/src/dirwalk.rs b/gix/src/dirwalk/options.rs similarity index 57% rename from gix/src/dirwalk.rs rename to gix/src/dirwalk/options.rs index 59357662c63..1ce6a262c53 100644 --- a/gix/src/dirwalk.rs +++ b/gix/src/dirwalk/options.rs @@ -1,25 +1,6 @@ +use crate::dirwalk::Options; use gix_dir::walk::{CollapsedEntriesEmissionMode, EmissionMode, ForDeletionMode}; -/// Options for use in the [`Repository::dirwalk()`](crate::Repository::dirwalk()) function. -/// -/// Note that all values start out disabled. -#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Ord, PartialOrd)] -pub struct Options { - precompose_unicode: bool, - ignore_case: bool, - - recurse_repositories: bool, - emit_pruned: bool, - emit_ignored: Option, - for_deletion: Option, - emit_tracked: bool, - emit_untracked: EmissionMode, - emit_empty_directories: bool, - classify_untracked_bare_repositories: bool, - emit_collapsed: Option, - pub(crate) empty_patterns_match_prefix: bool, -} - /// Construction impl Options { pub(crate) fn from_fs_caps(caps: gix_fs::Capabilities) -> Self { @@ -36,6 +17,7 @@ impl Options { classify_untracked_bare_repositories: false, emit_collapsed: None, empty_patterns_match_prefix: false, + symlinks_to_directories_are_ignored_like_directories: false, } } } @@ -54,6 +36,8 @@ impl From for gix_dir::walk::Options { emit_empty_directories: v.emit_empty_directories, classify_untracked_bare_repositories: v.classify_untracked_bare_repositories, emit_collapsed: v.emit_collapsed, + symlinks_to_directories_are_ignored_like_directories: v + .symlinks_to_directories_are_ignored_like_directories, } } } @@ -68,23 +52,43 @@ impl Options { self.empty_patterns_match_prefix = toggle; self } + /// Like [`empty_patterns_match_prefix()`](Self::empty_patterns_match_prefix), but only requires a mutably borrowed instance. + pub fn set_empty_patterns_match_prefix(&mut self, toggle: bool) -> &mut Self { + self.empty_patterns_match_prefix = toggle; + self + } /// If `toggle` is `true`, we will stop figuring out if any directory that is a candidate for recursion is also a nested repository, /// which saves time but leads to recurse into it. If `false`, nested repositories will not be traversed. pub fn recurse_repositories(mut self, toggle: bool) -> Self { self.recurse_repositories = toggle; self } + /// Like [`recurse_repositories()`](Self::recurse_repositories), but only requires a mutably borrowed instance. + pub fn set_recurse_repositories(&mut self, toggle: bool) -> &mut Self { + self.recurse_repositories = toggle; + self + } /// If `toggle` is `true`, entries that are pruned and whose [Kind](gix_dir::entry::Kind) is known will be emitted. pub fn emit_pruned(mut self, toggle: bool) -> Self { self.emit_pruned = toggle; self } + /// Like [`emit_pruned()`](Self::emit_pruned), but only requires a mutably borrowed instance. + pub fn set_emit_pruned(&mut self, toggle: bool) -> &mut Self { + self.emit_pruned = toggle; + self + } /// If `value` is `Some(mode)`, entries that are ignored will be emitted according to the given `mode`. /// If `None`, ignored entries will not be emitted at all. pub fn emit_ignored(mut self, value: Option) -> Self { self.emit_ignored = value; self } + /// Like [`emit_ignored()`](Self::emit_ignored), but only requires a mutably borrowed instance. + pub fn set_emit_ignored(&mut self, value: Option) -> &mut Self { + self.emit_ignored = value; + self + } /// When the walk is for deletion, `value` must be `Some(_)` to assure we don't collapse directories that have precious files in /// them, and otherwise assure that no entries are observable that shouldn't be deleted. /// If `None`, precious files are treated like expendable files, which is usually what you want when displaying them @@ -93,17 +97,32 @@ impl Options { self.for_deletion = value; self } + /// Like [`for_deletion()`](Self::for_deletion), but only requires a mutably borrowed instance. + pub fn set_for_deletion(&mut self, value: Option) -> &mut Self { + self.for_deletion = value; + self + } /// If `toggle` is `true`, we will also emit entries for tracked items. Otherwise these will remain 'hidden', /// even if a pathspec directly refers to it. pub fn emit_tracked(mut self, toggle: bool) -> Self { self.emit_tracked = toggle; self } + /// Like [`emit_tracked()`](Self::emit_tracked), but only requires a mutably borrowed instance. + pub fn set_emit_tracked(&mut self, toggle: bool) -> &mut Self { + self.emit_tracked = toggle; + self + } /// Controls the way untracked files are emitted. By default, this is happening immediately and without any simplification. pub fn emit_untracked(mut self, toggle: EmissionMode) -> Self { self.emit_untracked = toggle; self } + /// Like [`emit_untracked()`](Self::emit_untracked), but only requires a mutably borrowed instance. + pub fn set_emit_untracked(&mut self, toggle: EmissionMode) -> &mut Self { + self.emit_untracked = toggle; + self + } /// If `toggle` is `true`, emit empty directories as well. Note that a directory also counts as empty if it has any /// amount or depth of nested subdirectories, as long as none of them includes a file. /// Thus, this makes leaf-level empty directories visible, as those don't have any content. @@ -112,6 +131,12 @@ impl Options { self } + /// Like [`emit_empty_directories()`](Self::emit_empty_directories), but only requires a mutably borrowed instance. + pub fn set_emit_empty_directories(&mut self, toggle: bool) -> &mut Self { + self.emit_empty_directories = toggle; + self + } + /// If `toggle` is `true`, we will not only find non-bare repositories in untracked directories, but also bare ones. /// /// Note that this is very costly, but without it, bare repositories will appear like untracked directories when collapsed, @@ -121,10 +146,41 @@ impl Options { self } + /// Like [`classify_untracked_bare_repositories()`](Self::classify_untracked_bare_repositories), but only requires a mutably borrowed instance. + pub fn set_classify_untracked_bare_repositories(&mut self, toggle: bool) -> &mut Self { + self.classify_untracked_bare_repositories = toggle; + self + } + /// Control whether entries that are in an about-to-be collapsed directory will be emitted. The default is `None`, /// so entries in a collapsed directory are not observable. pub fn emit_collapsed(mut self, value: Option) -> Self { self.emit_collapsed = value; self } + + /// Like [`emit_collapsed()`](Self::emit_collapsed), but only requires a mutably borrowed instance. + pub fn set_emit_collapsed(&mut self, value: Option) -> &mut Self { + self.emit_collapsed = value; + self + } + + /// This is a `libgit2` compatibility flag, and if enabled, symlinks that point to directories will be considered a directory + /// when checking for exclusion. + /// + /// This is relevant if `src2` points to `src`, and is excluded with `src2/`. If `false`, `src2` will not be excluded, + /// if `true` it will be excluded as the symlink is considered a directory. + /// + /// In other words, for Git compatibility this flag should be `false`, the default, for `git2` compatibility it should be `true`. + pub fn symlinks_to_directories_are_ignored_like_directories(&mut self, toggle: bool) -> &mut Self { + self.symlinks_to_directories_are_ignored_like_directories = toggle; + self + } + + /// Like [`symlinks_to_directories_are_ignored_like_directories()`](Self::symlinks_to_directories_are_ignored_like_directories), + /// but only requires a mutably borrowed instance. + pub fn set_symlinks_to_directories_are_ignored_like_directories(&mut self, value: bool) -> &mut Self { + self.symlinks_to_directories_are_ignored_like_directories = value; + self + } } diff --git a/gix/src/env.rs b/gix/src/env.rs index eddc5f71656..307138b8fb3 100644 --- a/gix/src/env.rs +++ b/gix/src/env.rs @@ -50,6 +50,7 @@ pub fn os_str_to_bstring(input: &OsStr) -> Option { pub mod collate { /// + #[allow(clippy::empty_docs)] pub mod fetch { /// An error which combines all possible errors when opening a repository, finding remotes and using them to fetch. /// diff --git a/gix/src/ext/object_id.rs b/gix/src/ext/object_id.rs index d4d9467664b..d7f91164170 100644 --- a/gix/src/ext/object_id.rs +++ b/gix/src/ext/object_id.rs @@ -1,9 +1,8 @@ use gix_hash::ObjectId; -use gix_traverse::commit::{ancestors, Ancestors}; pub trait Sealed {} -pub type AncestorsIter = Ancestors bool, ancestors::State>; +pub type AncestorsIter = gix_traverse::commit::Simple bool>; /// An extension trait to add functionality to [`ObjectId`]s. pub trait ObjectIdExt: Sealed { @@ -23,7 +22,7 @@ impl ObjectIdExt for ObjectId { where Find: gix_object::Find, { - Ancestors::new(Some(self), ancestors::State::default(), find) + gix_traverse::commit::Simple::new(Some(self), find) } fn attach(self, repo: &crate::Repository) -> crate::Id<'_> { diff --git a/gix/src/ext/reference.rs b/gix/src/ext/reference.rs index 57e4e4fe7c9..c479fa2ec8f 100644 --- a/gix/src/ext/reference.rs +++ b/gix/src/ext/reference.rs @@ -1,7 +1,3 @@ -pub trait Sealed {} - -impl Sealed for gix_ref::Reference {} - /// Extensions for [references][gix_ref::Reference]. pub trait ReferenceExt { /// Attach [`Repository`][crate::Repository] to the given reference. It can be detached later with [`detach()]`. diff --git a/gix/src/ext/rev_spec.rs b/gix/src/ext/rev_spec.rs index caa58e2c77f..f9b38e29cb4 100644 --- a/gix/src/ext/rev_spec.rs +++ b/gix/src/ext/rev_spec.rs @@ -1,7 +1,3 @@ -pub trait Sealed {} - -impl Sealed for gix_ref::Reference {} - /// Extensions for [revision specifications][gix_revision::Spec]. pub trait RevSpecExt { /// Attach [`Repository`][crate::Repository] to the given rev-spec. diff --git a/gix/src/filter.rs b/gix/src/filter.rs index de0ce6f2fb3..128b4cd45de 100644 --- a/gix/src/filter.rs +++ b/gix/src/filter.rs @@ -14,8 +14,10 @@ use crate::{ }; /// +#[allow(clippy::empty_docs)] pub mod pipeline { /// + #[allow(clippy::empty_docs)] pub mod options { use crate::{bstr::BString, config}; @@ -38,6 +40,7 @@ pub mod pipeline { } /// + #[allow(clippy::empty_docs)] pub mod convert_to_git { /// The error returned by [Pipeline::convert_to_git()][crate::filter::Pipeline::convert_to_git()]. #[derive(Debug, thiserror::Error)] @@ -51,6 +54,7 @@ pub mod pipeline { } /// + #[allow(clippy::empty_docs)] pub mod convert_to_worktree { /// The error returned by [Pipeline::convert_to_worktree()][crate::filter::Pipeline::convert_to_worktree()]. #[derive(Debug, thiserror::Error)] @@ -131,7 +135,7 @@ impl<'repo> Pipeline<'repo> { impl<'repo> Pipeline<'repo> { /// Convert a `src` stream (to be found at `rela_path`, a repo-relative path) to a representation suitable for storage in `git` /// by using all attributes at `rela_path` and configuration of the repository to know exactly which filters apply. - /// `index` is used in particularly rare cases where the CRLF filter in auto-mode tries to determine whether or not to apply itself, + /// `index` is used in particularly rare cases where the CRLF filter in auto-mode tries to determine whether to apply itself, /// and it should match the state used when [instantiating this instance][Self::new()]. /// Note that the return-type implements [`std::io::Read`]. pub fn convert_to_git( @@ -143,7 +147,7 @@ impl<'repo> Pipeline<'repo> { where R: std::io::Read, { - let entry = self.cache.at_path(rela_path, Some(false), &self.repo.objects)?; + let entry = self.cache.at_path(rela_path, None, &self.repo.objects)?; Ok(self.inner.convert_to_git( src, rela_path, @@ -175,7 +179,7 @@ impl<'repo> Pipeline<'repo> { can_delay: gix_filter::driver::apply::Delay, ) -> Result, pipeline::convert_to_worktree::Error> { - let entry = self.cache.at_entry(rela_path, Some(false), &self.repo.objects)?; + let entry = self.cache.at_entry(rela_path, None, &self.repo.objects)?; Ok(self.inner.convert_to_worktree( src, rela_path, diff --git a/gix/src/head/log.rs b/gix/src/head/log.rs index 6aa7ed1d39e..88d90da890e 100644 --- a/gix/src/head/log.rs +++ b/gix/src/head/log.rs @@ -1,5 +1,3 @@ -use std::convert::TryInto; - use gix_hash::ObjectId; use crate::{ diff --git a/gix/src/head/mod.rs b/gix/src/head/mod.rs index 399b872ba95..8740a932959 100644 --- a/gix/src/head/mod.rs +++ b/gix/src/head/mod.rs @@ -1,6 +1,5 @@ //! -use std::convert::TryInto; - +#![allow(clippy::empty_docs)] use gix_hash::ObjectId; use gix_ref::FullNameRef; @@ -118,7 +117,9 @@ mod remote { } /// +#[allow(clippy::empty_docs)] pub mod log; /// +#[allow(clippy::empty_docs)] pub mod peel; diff --git a/gix/src/head/peel.rs b/gix/src/head/peel.rs index 4ee116ed0db..2f16b8c6cf6 100644 --- a/gix/src/head/peel.rs +++ b/gix/src/head/peel.rs @@ -22,6 +22,7 @@ mod error { pub use error::Error; /// +#[allow(clippy::empty_docs)] pub mod into_id { use crate::object; @@ -39,6 +40,7 @@ pub mod into_id { } /// +#[allow(clippy::empty_docs)] pub mod to_commit { use crate::object; @@ -54,6 +56,7 @@ pub mod to_commit { } /// +#[allow(clippy::empty_docs)] pub mod to_object { /// The error returned by [`Head::peel_to_object_in_place()`](super::Head::peel_to_object_in_place()). #[derive(Debug, thiserror::Error)] diff --git a/gix/src/id.rs b/gix/src/id.rs index 7214ec320da..2cbe8a72e28 100644 --- a/gix/src/id.rs +++ b/gix/src/id.rs @@ -1,4 +1,5 @@ //! +#![allow(clippy::empty_docs)] use std::ops::Deref; use gix_hash::{oid, ObjectId}; @@ -68,6 +69,7 @@ fn calculate_auto_hex_len(num_packed_objects: u64) -> usize { } /// +#[allow(clippy::empty_docs)] pub mod shorten { /// Returned by [`Id::prefix()`][super::Id::shorten()]. #[derive(Debug, thiserror::Error)] diff --git a/gix/src/init.rs b/gix/src/init.rs index 9416f7e3700..bc968adfbd5 100644 --- a/gix/src/init.rs +++ b/gix/src/init.rs @@ -1,5 +1,5 @@ #![allow(clippy::result_large_err)] -use std::{borrow::Cow, convert::TryInto, path::Path}; +use std::{borrow::Cow, path::Path}; use gix_macros::momo; use gix_ref::{ diff --git a/gix/src/lib.rs b/gix/src/lib.rs index 8c2124270e8..f193ecd74aa 100644 --- a/gix/src/lib.rs +++ b/gix/src/lib.rs @@ -72,7 +72,7 @@ //! //! What follows is a list of methods you might be missing, along with workarounds if available. //! * [`git2::Repository::open_bare()`](https://docs.rs/git2/*/git2/struct.Repository.html#method.open_bare) ➡ ❌ - use [`open()`] and discard if it is not bare. -//! * [`git2::build::CheckoutBuilder::disable_filters()](https://docs.rs/git2/*/git2/build/struct.CheckoutBuilder.html#method.disable_filters) ➡ ❌ *(filters are always applied during checkouts)* +//! * [`git2::build::CheckoutBuilder::disable_filters()`](https://docs.rs/git2/*/git2/build/struct.CheckoutBuilder.html#method.disable_filters) ➡ ❌ *(filters are always applied during checkouts)* //! * [`git2::Repository::submodule_status()`](https://docs.rs/git2/*/git2/struct.Repository.html#method.submodule_status) ➡ [`Submodule::state()`] - status provides more information and conveniences though, and an actual worktree status isn't performed. //! //! #### Integrity checks @@ -133,8 +133,6 @@ pub use gix_ref as refs; pub use gix_refspec as refspec; pub use gix_revwalk as revwalk; pub use gix_sec as sec; -#[cfg(feature = "status")] -pub use gix_status as status; pub use gix_tempfile as tempfile; pub use gix_trace as trace; pub use gix_traverse as traverse; @@ -148,12 +146,14 @@ pub mod interrupt; mod ext; /// +#[allow(clippy::empty_docs)] pub mod prelude; #[cfg(feature = "excludes")] mod attribute_stack; /// +#[allow(clippy::empty_docs)] pub mod path; /// The standard type for a store to handle git references. @@ -174,10 +174,12 @@ pub use types::{ pub use types::{Pathspec, PathspecDetached, Submodule}; /// +#[allow(clippy::empty_docs)] pub mod clone; pub mod commit; -#[cfg(feature = "dirwalk")] /// +#[cfg(feature = "dirwalk")] +#[allow(clippy::empty_docs)] pub mod dirwalk; pub mod head; pub mod id; @@ -189,13 +191,18 @@ pub mod repository; #[cfg(feature = "attributes")] pub mod submodule; pub mod tag; +#[cfg(any(feature = "dirwalk", feature = "status"))] +pub(crate) mod util; /// +#[allow(clippy::empty_docs)] pub mod progress; /// +#[allow(clippy::empty_docs)] pub mod push; /// +#[allow(clippy::empty_docs)] pub mod diff; /// See [`ThreadSafeRepository::discover()`], but returns a [`Repository`] instead. @@ -288,19 +295,24 @@ pub fn open_opts(directory: impl Into, options: open::Option } /// +#[allow(clippy::empty_docs)] pub mod create; /// +#[allow(clippy::empty_docs)] pub mod open; /// +#[allow(clippy::empty_docs)] pub mod config; /// +#[allow(clippy::empty_docs)] #[cfg(feature = "mailmap")] pub mod mailmap; /// +#[allow(clippy::empty_docs)] pub mod worktree; pub mod revision; @@ -309,18 +321,36 @@ pub mod revision; pub mod filter; /// +#[allow(clippy::empty_docs)] pub mod remote; /// +#[allow(clippy::empty_docs)] pub mod init; /// Not to be confused with 'status'. pub mod state; /// +#[allow(clippy::empty_docs)] +#[cfg(feature = "status")] +pub mod status; + +/// +#[allow(clippy::empty_docs)] pub mod shallow; /// +#[allow(clippy::empty_docs)] pub mod discover; pub mod env; + +#[cfg(feature = "attributes")] +fn is_dir_to_mode(is_dir: bool) -> gix_index::entry::Mode { + if is_dir { + gix_index::entry::Mode::DIR + } else { + gix_index::entry::Mode::FILE + } +} diff --git a/gix/src/mailmap.rs b/gix/src/mailmap.rs index fdf7ee948da..b7244549546 100644 --- a/gix/src/mailmap.rs +++ b/gix/src/mailmap.rs @@ -1,6 +1,7 @@ pub use gix_mailmap::*; /// +#[allow(clippy::empty_docs)] pub mod load { /// The error returned by [`crate::Repository::open_mailmap_into()`]. #[derive(Debug, thiserror::Error)] diff --git a/gix/src/object/blob.rs b/gix/src/object/blob.rs index 59629727f3a..8925685b018 100644 --- a/gix/src/object/blob.rs +++ b/gix/src/object/blob.rs @@ -20,6 +20,7 @@ pub mod diff { } /// + #[allow(clippy::empty_docs)] pub mod init { /// The error returned by [`Platform::from_tree_change()`][super::Platform::from_tree_change()]. pub type Error = gix_diff::blob::platform::set_resource::Error; @@ -115,6 +116,7 @@ pub mod diff { } /// + #[allow(clippy::empty_docs)] pub mod lines { use crate::bstr::BStr; diff --git a/gix/src/object/errors.rs b/gix/src/object/errors.rs index db81daacb23..4331c795d81 100644 --- a/gix/src/object/errors.rs +++ b/gix/src/object/errors.rs @@ -1,4 +1,5 @@ /// +#[allow(clippy::empty_docs)] pub mod conversion { /// The error returned by [`crate::object::try_to_()`][crate::Object::try_to_commit_ref()]. @@ -16,6 +17,7 @@ pub mod conversion { } /// +#[allow(clippy::empty_docs)] pub mod find { /// Indicate that an error occurred when trying to find an object. #[derive(Debug, thiserror::Error)] @@ -23,6 +25,7 @@ pub mod find { pub struct Error(#[from] pub gix_object::find::Error); /// + #[allow(clippy::empty_docs)] pub mod existing { /// An object could not be found in the database, or an error occurred when trying to obtain it. pub type Error = gix_object::find::existing::Error; @@ -30,6 +33,7 @@ pub mod find { } /// +#[allow(clippy::empty_docs)] pub mod write { /// An error to indicate writing to the loose object store failed. #[derive(Debug, thiserror::Error)] diff --git a/gix/src/object/impls.rs b/gix/src/object/impls.rs index 5d2ad81604b..58e068e402d 100644 --- a/gix/src/object/impls.rs +++ b/gix/src/object/impls.rs @@ -1,5 +1,3 @@ -use std::convert::TryFrom; - use crate::{object, Blob, Commit, Object, ObjectDetached, Tag, Tree}; impl<'repo> From> for ObjectDetached { diff --git a/gix/src/object/mod.rs b/gix/src/object/mod.rs index cd047c4d3a4..c0815a19250 100644 --- a/gix/src/object/mod.rs +++ b/gix/src/object/mod.rs @@ -1,6 +1,5 @@ //! -use std::convert::TryInto; - +#![allow(clippy::empty_docs)] use gix_hash::ObjectId; pub use gix_object::Kind; @@ -12,16 +11,20 @@ pub(crate) mod cache { } pub use errors::{conversion, find, write}; /// +#[allow(clippy::empty_docs)] pub mod blob; /// +#[allow(clippy::empty_docs)] pub mod commit; mod impls; pub mod peel; mod tag; /// +#[allow(clippy::empty_docs)] pub mod tree; /// +#[allow(clippy::empty_docs)] pub mod try_into { #[derive(thiserror::Error, Debug)] #[allow(missing_docs)] diff --git a/gix/src/object/peel.rs b/gix/src/object/peel.rs index c906c0c7500..6ac77726927 100644 --- a/gix/src/object/peel.rs +++ b/gix/src/object/peel.rs @@ -1,4 +1,5 @@ //! +#![allow(clippy::empty_docs)] use crate::{ object, object::{peel, Kind}, @@ -6,6 +7,7 @@ use crate::{ }; /// +#[allow(clippy::empty_docs)] pub mod to_kind { mod error { diff --git a/gix/src/object/tree/diff/mod.rs b/gix/src/object/tree/diff/mod.rs index 85877561083..299075781fa 100644 --- a/gix/src/object/tree/diff/mod.rs +++ b/gix/src/object/tree/diff/mod.rs @@ -24,6 +24,7 @@ pub struct Change<'a, 'old, 'new> { } /// +#[allow(clippy::empty_docs)] pub mod change; /// Diffing @@ -86,4 +87,5 @@ impl<'a, 'repo> Platform<'a, 'repo> { } /// +#[allow(clippy::empty_docs)] pub mod for_each; diff --git a/gix/src/object/tree/mod.rs b/gix/src/object/tree/mod.rs index 0523477c9ff..0a1ed46d66d 100644 --- a/gix/src/object/tree/mod.rs +++ b/gix/src/object/tree/mod.rs @@ -174,9 +174,11 @@ impl<'repo> Tree<'repo> { pub mod diff; /// +#[allow(clippy::empty_docs)] pub mod traverse; /// +#[allow(clippy::empty_docs)] mod iter; pub use iter::EntryRef; diff --git a/gix/src/open/repository.rs b/gix/src/open/repository.rs index 6e9a9ac9904..639b7dd9353 100644 --- a/gix/src/open/repository.rs +++ b/gix/src/open/repository.rs @@ -216,15 +216,17 @@ impl ThreadSafeRepository { let mut refs = { let reflog = repo_config.reflog.unwrap_or(gix_ref::store::WriteReflog::Disable); let object_hash = repo_config.object_hash; + let ref_store_init_opts = gix_ref::store::init::Options { + write_reflog: reflog, + object_hash, + precompose_unicode: repo_config.precompose_unicode, + prohibit_windows_device_names: repo_config.protect_windows, + }; match &common_dir { - Some(common_dir) => crate::RefStore::for_linked_worktree( - git_dir.to_owned(), - common_dir.into(), - reflog, - object_hash, - repo_config.precompose_unicode, - ), - None => crate::RefStore::at(git_dir.to_owned(), reflog, object_hash, repo_config.precompose_unicode), + Some(common_dir) => { + crate::RefStore::for_linked_worktree(git_dir.to_owned(), common_dir.into(), ref_store_init_opts) + } + None => crate::RefStore::at(git_dir.to_owned(), ref_store_init_opts), } }; let head = refs.find("HEAD").ok(); @@ -296,7 +298,7 @@ impl ThreadSafeRepository { } refs.write_reflog = config::cache::util::reflog_or_default(config.reflog, worktree_dir.is_some()); - refs.namespace = config.refs_namespace.clone(); + refs.namespace.clone_from(&config.refs_namespace); let replacements = replacement_objects_refs_prefix(&config.resolved, lenient_config, filter_config_section)? .and_then(|prefix| { let _span = gix_trace::detail!("find replacement objects"); diff --git a/gix/src/pathspec.rs b/gix/src/pathspec.rs index 08ac5e5230a..4ea3ac7375f 100644 --- a/gix/src/pathspec.rs +++ b/gix/src/pathspec.rs @@ -5,6 +5,7 @@ pub use gix_pathspec::*; use crate::{bstr::BStr, AttributeStack, Pathspec, PathspecDetached, Repository}; /// +#[allow(clippy::empty_docs)] pub mod init { /// The error returned by [`Pathspec::new()`](super::Pathspec::new()). #[derive(Debug, thiserror::Error)] @@ -67,6 +68,13 @@ impl<'repo> Pathspec<'repo> { )?, )?; let cache = needs_cache.then(make_attributes).transpose()?; + + gix_trace::debug!( + longest_prefix = ?search.longest_common_directory(), + prefix_dir = ?search.prefix_directory(), + patterns = ?search.patterns().map(gix_pathspec::Pattern::path).collect::>() + ); + Ok(Self { repo, search, @@ -129,7 +137,7 @@ impl<'repo> Pathspec<'repo> { let stack = self.stack.as_mut().expect("initialized in advance"); stack .set_case(case) - .at_entry(relative_path, Some(is_dir), &self.repo.objects) + .at_entry(relative_path, Some(is_dir_to_mode(is_dir)), &self.repo.objects) .map_or(false, |platform| platform.matching_attributes(out)) }, ) @@ -185,7 +193,7 @@ impl PathspecDetached { let stack = self.stack.as_mut().expect("initialized in advance"); stack .set_case(case) - .at_entry(relative_path, Some(is_dir), &self.odb) + .at_entry(relative_path, Some(is_dir_to_mode(is_dir)), &self.odb) .map_or(false, |platform| platform.matching_attributes(out)) }, ) @@ -199,3 +207,11 @@ impl PathspecDetached { .map_or(false, |m| !m.is_excluded()) } } + +fn is_dir_to_mode(is_dir: bool) -> gix_index::entry::Mode { + if is_dir { + gix_index::entry::Mode::DIR + } else { + gix_index::entry::Mode::FILE + } +} diff --git a/gix/src/reference/edits.rs b/gix/src/reference/edits.rs index cba652630ce..4d3d773ce68 100644 --- a/gix/src/reference/edits.rs +++ b/gix/src/reference/edits.rs @@ -1,4 +1,5 @@ /// +#[allow(clippy::empty_docs)] pub mod set_target_id { use gix_macros::momo; use gix_ref::{transaction::PreviousValue, Target}; @@ -53,6 +54,7 @@ pub mod set_target_id { } /// +#[allow(clippy::empty_docs)] pub mod delete { use gix_ref::transaction::{Change, PreviousValue, RefEdit, RefLog}; diff --git a/gix/src/reference/errors.rs b/gix/src/reference/errors.rs index d5b09f78e0b..92714ec790c 100644 --- a/gix/src/reference/errors.rs +++ b/gix/src/reference/errors.rs @@ -1,4 +1,5 @@ /// +#[allow(clippy::empty_docs)] pub mod edit { use crate::config; @@ -21,6 +22,7 @@ pub mod edit { } /// +#[allow(clippy::empty_docs)] pub mod peel { /// The error returned by [`Reference::peel_to_id_in_place(…)`](crate::Reference::peel_to_id_in_place()) and /// [`Reference::into_fully_peeled_id(…)`](crate::Reference::into_fully_peeled_id()). @@ -35,6 +37,7 @@ pub mod peel { } /// +#[allow(clippy::empty_docs)] pub mod head_id { /// The error returned by [`Repository::head_id(…)`](crate::Repository::head_id()). #[derive(Debug, thiserror::Error)] @@ -48,6 +51,7 @@ pub mod head_id { } /// +#[allow(clippy::empty_docs)] pub mod head_commit { /// The error returned by [`Repository::head_commit`(…)](crate::Repository::head_commit()). #[derive(Debug, thiserror::Error)] @@ -61,6 +65,7 @@ pub mod head_commit { } /// +#[allow(clippy::empty_docs)] pub mod head_tree_id { /// The error returned by [`Repository::head_tree_id`(…)](crate::Repository::head_tree_id()). #[derive(Debug, thiserror::Error)] @@ -74,8 +79,10 @@ pub mod head_tree_id { } /// +#[allow(clippy::empty_docs)] pub mod find { /// + #[allow(clippy::empty_docs)] pub mod existing { /// The error returned by [`find_reference(…)`][crate::Repository::find_reference()], and others. #[derive(Debug, thiserror::Error)] diff --git a/gix/src/reference/iter.rs b/gix/src/reference/iter.rs index 604a8ac4b05..995284a4824 100644 --- a/gix/src/reference/iter.rs +++ b/gix/src/reference/iter.rs @@ -1,4 +1,5 @@ //! +#![allow(clippy::empty_docs)] use std::path::Path; use gix_macros::momo; @@ -109,6 +110,7 @@ impl<'r> Iterator for Iter<'r> { } /// +#[allow(clippy::empty_docs)] pub mod init { /// The error returned by [`Platform::all()`][super::Platform::all()] or [`Platform::prefixed()`][super::Platform::prefixed()]. #[derive(Debug, thiserror::Error)] diff --git a/gix/src/reference/log.rs b/gix/src/reference/log.rs index 2fea1782cc4..275eaf86850 100644 --- a/gix/src/reference/log.rs +++ b/gix/src/reference/log.rs @@ -1,4 +1,5 @@ //! +#![allow(clippy::empty_docs)] use gix_object::commit::MessageRef; use gix_ref::file::ReferenceExt; diff --git a/gix/src/reference/mod.rs b/gix/src/reference/mod.rs index ebdbf66a717..b145509cf92 100644 --- a/gix/src/reference/mod.rs +++ b/gix/src/reference/mod.rs @@ -1,4 +1,5 @@ //! +#![allow(clippy::empty_docs)] use gix_ref::file::ReferenceExt; @@ -6,6 +7,7 @@ use crate::{Id, Reference}; pub mod iter; /// +#[allow(clippy::empty_docs)] pub mod remote; mod errors; diff --git a/gix/src/remote/build.rs b/gix/src/remote/build.rs index 452da66a006..16243900d9b 100644 --- a/gix/src/remote/build.rs +++ b/gix/src/remote/build.rs @@ -1,5 +1,3 @@ -use std::convert::TryInto; - use crate::{bstr::BStr, remote, Remote}; /// Builder methods diff --git a/gix/src/remote/connection/fetch/error.rs b/gix/src/remote/connection/fetch/error.rs index 5034dcb5d4e..129a2e6d345 100644 --- a/gix/src/remote/connection/fetch/error.rs +++ b/gix/src/remote/connection/fetch/error.rs @@ -45,6 +45,13 @@ pub enum Error { RejectShallowRemote, #[error(transparent)] NegotiationAlgorithmConfig(#[from] config::key::GenericErrorWithValue), + #[error("Failed to read remaining bytes in stream")] + ReadRemainingBytes(#[source] std::io::Error), + #[error("None of the refspec(s) {} matched any of the {num_remote_refs} refs on the remote", refspecs.iter().map(|r| r.to_ref().instruction().to_bstring().to_string()).collect::>().join(", "))] + NoMapping { + refspecs: Vec, + num_remote_refs: usize, + }, } impl gix_protocol::transport::IsSpuriousError for Error { diff --git a/gix/src/remote/connection/fetch/mod.rs b/gix/src/remote/connection/fetch/mod.rs index d4afd10234d..213a41bf7e4 100644 --- a/gix/src/remote/connection/fetch/mod.rs +++ b/gix/src/remote/connection/fetch/mod.rs @@ -90,6 +90,7 @@ pub mod outcome { } /// + #[allow(clippy::empty_docs)] pub mod negotiate { /// Key information about each round in the pack-negotiation. #[derive(Debug, Clone)] @@ -138,6 +139,7 @@ impl From for gix_features::progress::Id { pub(crate) mod negotiate; /// +#[allow(clippy::empty_docs)] pub mod prepare { /// The error returned by [`prepare_fetch()`][super::Connection::prepare_fetch()]. #[derive(Debug, thiserror::Error)] diff --git a/gix/src/remote/connection/fetch/negotiate.rs b/gix/src/remote/connection/fetch/negotiate.rs index f5b6a031c4c..d9e73fe7dbc 100644 --- a/gix/src/remote/connection/fetch/negotiate.rs +++ b/gix/src/remote/connection/fetch/negotiate.rs @@ -38,7 +38,7 @@ pub(crate) enum Action { SkipToRefUpdate, /// We can't know for sure if fetching *is not* needed, so we go ahead and negotiate. MustNegotiate { - /// Each `ref_map.mapping` has a slot here which is `true` if we have the object the remote ref points to locally. + /// Each `ref_map.mapping` has a slot here which is `true` if we have the object the remote ref points to, locally. remote_ref_target_known: Vec, }, } @@ -61,7 +61,7 @@ pub(crate) enum Action { /// Finally, we also mark tips in the `negotiator` in one go to avoid traversing all refs twice, since we naturally encounter all tips during /// our own walk. /// -/// Return whether or not we should negotiate, along with a queue for later use. +/// Return whether we should negotiate, along with a queue for later use. pub(crate) fn mark_complete_and_common_ref( repo: &crate::Repository, negotiator: &mut dyn gix_negotiate::Negotiator, @@ -71,6 +71,9 @@ pub(crate) fn mark_complete_and_common_ref( mapping_is_ignored: impl Fn(&fetch::Mapping) -> bool, ) -> Result { let _span = gix_trace::detail!("mark_complete_and_common_ref", mappings = ref_map.mappings.len()); + if ref_map.mappings.is_empty() { + return Ok(Action::NoChange); + } if let fetch::Shallow::Deepen(0) = shallow { // Avoid deepening (relative) with zero as it seems to upset the server. Git also doesn't actually // perform the negotiation for some reason (couldn't find it in code). @@ -218,7 +221,7 @@ pub(crate) fn add_wants( shallow: &fetch::Shallow, mapping_is_ignored: impl Fn(&fetch::Mapping) -> bool, ) { - // When using shallow, we can't exclude `wants` as the remote won't send anything then. Thus we have to resend everything + // When using shallow, we can't exclude `wants` as the remote won't send anything then. Thus, we have to resend everything // we have as want instead to get exactly the same graph, but possibly deepened. let is_shallow = !matches!(shallow, fetch::Shallow::NoChange); let wants = ref_map diff --git a/gix/src/remote/connection/fetch/receive_pack.rs b/gix/src/remote/connection/fetch/receive_pack.rs index 4508b18e43a..5501c75356c 100644 --- a/gix/src/remote/connection/fetch/receive_pack.rs +++ b/gix/src/remote/connection/fetch/receive_pack.rs @@ -91,6 +91,15 @@ where let _span = gix_trace::coarse!("fetch::Prepare::receive()"); let mut con = self.con.take().expect("receive() can only be called once"); + if self.ref_map.mappings.is_empty() && !self.ref_map.remote_refs.is_empty() { + let mut specs = con.remote.fetch_specs.clone(); + specs.extend(self.ref_map.extra_refspecs.clone()); + return Err(Error::NoMapping { + refspecs: specs, + num_remote_refs: self.ref_map.remote_refs.len(), + }); + } + let handshake = &self.ref_map.handshake; let protocol_version = handshake.server_protocol_version; @@ -105,7 +114,7 @@ where gix_protocol::fetch::Response::check_required_features(protocol_version, &fetch_features)?; let sideband_all = fetch_features.iter().any(|(n, _)| *n == "sideband-all"); let mut arguments = gix_protocol::fetch::Arguments::new(protocol_version, fetch_features, con.trace); - if matches!(con.remote.fetch_tags, crate::remote::fetch::Tags::Included) { + if matches!(con.remote.fetch_tags, fetch::Tags::Included) { if !arguments.can_use_include_tag() { return Err(Error::MissingServerFeature { feature: "include-tag", @@ -287,7 +296,7 @@ where #[cfg(not(feature = "async-network-client"))] let has_read_to_end = { rd.stopped_at().is_some() }; if !has_read_to_end { - std::io::copy(&mut rd, &mut std::io::sink()).unwrap(); + std::io::copy(&mut rd, &mut std::io::sink()).map_err(Error::ReadRemainingBytes)?; } #[cfg(feature = "async-network-client")] { diff --git a/gix/src/remote/connection/fetch/update_refs/mod.rs b/gix/src/remote/connection/fetch/update_refs/mod.rs index c487e7f5c2c..e5d3eac1b2d 100644 --- a/gix/src/remote/connection/fetch/update_refs/mod.rs +++ b/gix/src/remote/connection/fetch/update_refs/mod.rs @@ -1,5 +1,5 @@ #![allow(clippy::result_large_err)] -use std::{collections::BTreeMap, convert::TryInto, path::PathBuf}; +use std::{collections::BTreeMap, path::PathBuf}; use gix_object::Exists; use gix_ref::{ @@ -20,6 +20,7 @@ use crate::{ }; /// +#[allow(clippy::empty_docs)] pub mod update; /// Information about the update of a single reference, corresponding the respective entry in [`RefMap::mappings`][crate::remote::fetch::RefMap::mappings]. @@ -161,7 +162,7 @@ pub(crate) fn update( .to_owned() .ancestors(&repo.objects) .sorting( - gix_traverse::commit::Sorting::ByCommitTimeNewestFirstCutoffOlderThan { + gix_traverse::commit::simple::Sorting::ByCommitTimeNewestFirstCutoffOlderThan { seconds: local_commit_time }, ) diff --git a/gix/src/remote/connection/fetch/update_refs/tests.rs b/gix/src/remote/connection/fetch/update_refs/tests.rs index a56e1218043..fbe5e2fcd17 100644 --- a/gix/src/remote/connection/fetch/update_refs/tests.rs +++ b/gix/src/remote/connection/fetch/update_refs/tests.rs @@ -8,8 +8,6 @@ fn hex_to_id(hex: &str) -> gix_hash::ObjectId { } mod update { - use std::convert::TryInto; - use gix_testtools::Result; use super::hex_to_id; diff --git a/gix/src/remote/connection/mod.rs b/gix/src/remote/connection/mod.rs index f9b8aa7e6d4..78c29344bed 100644 --- a/gix/src/remote/connection/mod.rs +++ b/gix/src/remote/connection/mod.rs @@ -23,7 +23,9 @@ pub struct Connection<'a, 'repo, T> { mod access; /// +#[allow(clippy::empty_docs)] pub mod ref_map; /// +#[allow(clippy::empty_docs)] pub mod fetch; diff --git a/gix/src/remote/connection/ref_map.rs b/gix/src/remote/connection/ref_map.rs index bf53cf35fb0..264984ecb8a 100644 --- a/gix/src/remote/connection/ref_map.rs +++ b/gix/src/remote/connection/ref_map.rs @@ -79,14 +79,14 @@ where /// for _fetching_. /// /// This comes in the form of all matching tips on the remote and the object they point to, along with - /// with the local tracking branch of these tips (if available). + /// the local tracking branch of these tips (if available). /// /// Note that this doesn't fetch the objects mentioned in the tips nor does it make any change to underlying repository. /// /// # Consumption /// - /// Due to management of the transport, it's cleanest to only use it for a single interaction. Thus it's consumed along with - /// the connection. + /// Due to management of the transport, it's cleanest to only use it for a single interaction. Thus, it's consumed + /// along with the connection. /// /// ### Configuration /// @@ -145,6 +145,7 @@ where } })) .validated()?; + let mappings = res.mappings; let mappings = mappings .into_iter() diff --git a/gix/src/remote/errors.rs b/gix/src/remote/errors.rs index 34ed8246b24..37b211d28b2 100644 --- a/gix/src/remote/errors.rs +++ b/gix/src/remote/errors.rs @@ -1,4 +1,5 @@ /// +#[allow(clippy::empty_docs)] pub mod find { use crate::{bstr::BString, config, remote}; @@ -27,6 +28,7 @@ pub mod find { } /// + #[allow(clippy::empty_docs)] pub mod existing { use crate::bstr::BString; @@ -44,6 +46,7 @@ pub mod find { } /// + #[allow(clippy::empty_docs)] pub mod for_fetch { /// The error returned by [`Repository::find_fetch_remote(…)`](crate::Repository::find_fetch_remote()). #[derive(Debug, thiserror::Error)] diff --git a/gix/src/remote/fetch.rs b/gix/src/remote/fetch.rs index 4700201de51..ff483c893d7 100644 --- a/gix/src/remote/fetch.rs +++ b/gix/src/remote/fetch.rs @@ -1,4 +1,5 @@ /// +#[allow(clippy::empty_docs)] pub mod negotiate { #[cfg(feature = "credentials")] pub use gix_negotiate::Algorithm; diff --git a/gix/src/remote/init.rs b/gix/src/remote/init.rs index 13b747eda7c..fd81604b257 100644 --- a/gix/src/remote/init.rs +++ b/gix/src/remote/init.rs @@ -1,5 +1,3 @@ -use std::convert::TryInto; - use gix_refspec::RefSpec; use crate::{config, remote, Remote, Repository}; diff --git a/gix/src/remote/mod.rs b/gix/src/remote/mod.rs index 7937f8275ed..38b1ccf27ad 100644 --- a/gix/src/remote/mod.rs +++ b/gix/src/remote/mod.rs @@ -32,6 +32,7 @@ pub enum Name<'repo> { } /// +#[allow(clippy::empty_docs)] pub mod name; mod build; @@ -40,9 +41,11 @@ mod errors; pub use errors::find; /// +#[allow(clippy::empty_docs)] pub mod init; /// +#[allow(clippy::empty_docs)] pub mod fetch; /// @@ -55,8 +58,10 @@ mod connection; pub use connection::{ref_map, AuthenticateFn, Connection}; /// +#[allow(clippy::empty_docs)] pub mod save; mod access; /// +#[allow(clippy::empty_docs)] pub mod url; diff --git a/gix/src/remote/name.rs b/gix/src/remote/name.rs index 6c6afe745f1..f0081cc30df 100644 --- a/gix/src/remote/name.rs +++ b/gix/src/remote/name.rs @@ -1,4 +1,4 @@ -use std::{borrow::Cow, convert::TryFrom}; +use std::borrow::Cow; use super::Name; use crate::bstr::{BStr, BString, ByteSlice, ByteVec}; diff --git a/gix/src/remote/save.rs b/gix/src/remote/save.rs index 2a91dfa9c1d..63a2de90d71 100644 --- a/gix/src/remote/save.rs +++ b/gix/src/remote/save.rs @@ -1,5 +1,3 @@ -use std::convert::TryInto; - use gix_macros::momo; use crate::{ diff --git a/gix/src/remote/url/scheme_permission.rs b/gix/src/remote/url/scheme_permission.rs index deb8ef9514b..7709537fed7 100644 --- a/gix/src/remote/url/scheme_permission.rs +++ b/gix/src/remote/url/scheme_permission.rs @@ -1,4 +1,4 @@ -use std::{borrow::Cow, collections::BTreeMap, convert::TryFrom}; +use std::{borrow::Cow, collections::BTreeMap}; use crate::{ bstr::{BStr, BString, ByteSlice}, diff --git a/gix/src/repository/attributes.rs b/gix/src/repository/attributes.rs index 7f747f7fd98..55e28e3ff49 100644 --- a/gix/src/repository/attributes.rs +++ b/gix/src/repository/attributes.rs @@ -105,7 +105,7 @@ impl Repository { /// /// When only excludes are desired, this is the most efficient way to obtain them. Otherwise use /// [`Repository::attributes()`] for accessing both attributes and excludes. - // TODO: test + #[doc(alias = "is_path_ignored", alias = "git2")] #[cfg(feature = "excludes")] pub fn excludes( &self, diff --git a/gix/src/repository/config/branch.rs b/gix/src/repository/config/branch.rs index b696f4822df..c134fac58b6 100644 --- a/gix/src/repository/config/branch.rs +++ b/gix/src/repository/config/branch.rs @@ -1,4 +1,4 @@ -use std::{borrow::Cow, collections::BTreeSet, convert::TryInto}; +use std::{borrow::Cow, collections::BTreeSet}; use gix_ref::{FullName, FullNameRef}; diff --git a/gix/src/repository/diff.rs b/gix/src/repository/diff.rs index cb1d070a2f4..87e28d0e9c5 100644 --- a/gix/src/repository/diff.rs +++ b/gix/src/repository/diff.rs @@ -1,6 +1,7 @@ use crate::Repository; /// +#[allow(clippy::empty_docs)] pub mod resource_cache { /// The error returned by [Repository::diff_resource_cache()](super::Repository::diff_resource_cache()). #[derive(Debug, thiserror::Error)] @@ -10,6 +11,8 @@ pub mod resource_cache { ResourceCache(#[from] crate::diff::resource_cache::Error), #[error(transparent)] Index(#[from] crate::repository::index_or_load_from_head::Error), + #[error(transparent)] + AttributeStack(#[from] crate::config::attribute_stack::Error), } } @@ -30,15 +33,19 @@ impl Repository { mode: gix_diff::blob::pipeline::Mode, worktree_roots: gix_diff::blob::pipeline::WorktreeRoots, ) -> Result { + let index = self.index_or_load_from_head()?; Ok(crate::diff::resource_cache( self, - &*self.index_or_load_from_head()?, mode, - if worktree_roots.new_root.is_some() || worktree_roots.old_root.is_some() { - gix_worktree::stack::state::attributes::Source::WorktreeThenIdMapping - } else { - gix_worktree::stack::state::attributes::Source::IdMapping - }, + self.attributes_only( + &index, + if worktree_roots.new_root.is_some() || worktree_roots.old_root.is_some() { + gix_worktree::stack::state::attributes::Source::WorktreeThenIdMapping + } else { + gix_worktree::stack::state::attributes::Source::IdMapping + }, + )? + .inner, worktree_roots, )?) } diff --git a/gix/src/repository/dirwalk.rs b/gix/src/repository/dirwalk.rs index 2495b544925..db64410e42a 100644 --- a/gix/src/repository/dirwalk.rs +++ b/gix/src/repository/dirwalk.rs @@ -1,37 +1,8 @@ -use crate::bstr::BStr; -use crate::{config, dirwalk, AttributeStack, Pathspec, Repository}; -use std::path::PathBuf; - -/// The error returned by [dirwalk()](Repository::dirwalk()). -#[derive(Debug, thiserror::Error)] -#[allow(missing_docs)] -pub enum Error { - #[error(transparent)] - Walk(#[from] gix_dir::walk::Error), - #[error("A working tree is required to perform a directory walk")] - MissinWorkDir, - #[error(transparent)] - Excludes(#[from] config::exclude_stack::Error), - #[error(transparent)] - Pathspec(#[from] crate::pathspec::init::Error), - #[error(transparent)] - Prefix(#[from] gix_path::realpath::Error), - #[error(transparent)] - FilesystemOptions(#[from] config::boolean::Error), -} - -/// The outcome of the [dirwalk()](Repository::dirwalk). -pub struct Outcome<'repo> { - /// The excludes stack used for the dirwalk, for access of `.gitignore` information. - pub excludes: AttributeStack<'repo>, - /// The pathspecs used to guide the operation, - pub pathspec: Pathspec<'repo>, - /// The root actually being used for the traversal, and useful to transform the paths returned for the user. - /// It's always within the [`work-dir`](Repository::work_dir). - pub traversal_root: PathBuf, - /// The actual result of the dirwalk. - pub dirwalk: gix_dir::walk::Outcome, -} +use crate::bstr::{BStr, BString}; +use crate::util::OwnedOrStaticAtomicBool; +use crate::worktree::IndexPersistedOrInMemory; +use crate::{config, dirwalk, is_dir_to_mode, Repository}; +use std::sync::atomic::AtomicBool; impl Repository { /// Return default options suitable for performing a directory walk on this repository. @@ -42,7 +13,8 @@ impl Repository { } /// Perform a directory walk configured with `options` under control of the `delegate`. Use `patterns` to - /// further filter entries. + /// further filter entries. `should_interrupt` is polled to see if an interrupt is requested, causing an + /// error to be returned instead. /// /// The `index` is used to determine if entries are tracked, and for excludes and attributes /// lookup. Note that items will only count as tracked if they have the [`gix_index::entry::Flags::UPTODATE`] @@ -53,11 +25,12 @@ impl Repository { &self, index: &gix_index::State, patterns: impl IntoIterator>, + should_interrupt: &AtomicBool, options: dirwalk::Options, delegate: &mut dyn gix_dir::walk::Delegate, - ) -> Result, Error> { + ) -> Result, dirwalk::Error> { let _span = gix_trace::coarse!("gix::dirwalk"); - let workdir = self.work_dir().ok_or(Error::MissinWorkDir)?; + let workdir = self.work_dir().ok_or(dirwalk::Error::MissingWorkDir)?; let mut excludes = self.excludes( index, None, @@ -70,11 +43,6 @@ impl Repository { index, crate::worktree::stack::state::attributes::Source::WorktreeThenIdMapping, )?; - gix_trace::debug!( - longest_prefix = ?pathspec.search.longest_common_directory(), - prefix_dir = ?pathspec.search.prefix_directory(), - patterns = ?pathspec.search.patterns().map(gix_pathspec::Pattern::path).collect::>() - ); let git_dir_realpath = crate::path::realpath_opts(self.git_dir(), self.current_dir(), crate::path::realpath::MAX_SYMLINKS)?; @@ -83,6 +51,7 @@ impl Repository { let (outcome, traversal_root) = gix_dir::walk( workdir, gix_dir::walk::Context { + should_interrupt: Some(should_interrupt), git_dir_realpath: git_dir_realpath.as_ref(), current_dir: self.current_dir(), index, @@ -95,7 +64,7 @@ impl Repository { .expect("can only be called if attributes are used in patterns"); stack .set_case(case) - .at_entry(relative_path, Some(is_dir), &self.objects) + .at_entry(relative_path, Some(is_dir_to_mode(is_dir)), &self.objects) .map_or(false, |platform| platform.matching_attributes(out)) }, excludes: Some(&mut excludes.inner), @@ -106,11 +75,32 @@ impl Repository { delegate, )?; - Ok(Outcome { + Ok(dirwalk::Outcome { dirwalk: outcome, traversal_root, excludes, pathspec, }) } + + /// Create an iterator over a running traversal, which stops if the iterator is dropped. All arguments + /// are the same as in [`dirwalk()`](Self::dirwalk). + /// + /// `should_interrupt` should be set to `Default::default()` if it is supposed to be unused. + /// Otherwise, it can be created by passing a `&'static AtomicBool`, `&Arc` or `Arc`. + pub fn dirwalk_iter( + &self, + index: impl Into, + patterns: impl IntoIterator>, + should_interrupt: OwnedOrStaticAtomicBool, + options: dirwalk::Options, + ) -> Result { + dirwalk::Iter::new( + self, + index.into(), + patterns.into_iter().map(Into::into).collect(), + should_interrupt, + options, + ) + } } diff --git a/gix/src/repository/filter.rs b/gix/src/repository/filter.rs index 0ad7c0ef609..33ee80177bb 100644 --- a/gix/src/repository/filter.rs +++ b/gix/src/repository/filter.rs @@ -1,6 +1,7 @@ -use crate::{filter, repository::IndexPersistedOrInMemory, Id, Repository}; +use crate::{filter, worktree::IndexPersistedOrInMemory, Id, Repository}; /// +#[allow(clippy::empty_docs)] pub mod pipeline { /// The error returned by [Repository::filter_pipeline()](super::Repository::filter_pipeline()). #[derive(Debug, thiserror::Error)] @@ -11,7 +12,7 @@ pub mod pipeline { #[error(transparent)] DecodeCommit(#[from] gix_object::decode::Error), #[error("Could not create index from tree at HEAD^{{tree}}")] - TreeTraverse(#[from] gix_traverse::tree::breadthfirst::Error), + TreeTraverse(#[from] crate::repository::index_from_tree::Error), #[error(transparent)] BareAttributes(#[from] crate::config::attribute_stack::Error), #[error(transparent)] diff --git a/gix/src/repository/index.rs b/gix/src/repository/index.rs index 2a1c06977e8..03c7f5553e4 100644 --- a/gix/src/repository/index.rs +++ b/gix/src/repository/index.rs @@ -1,7 +1,5 @@ use crate::{ - config::{cache::util::ApplyLeniencyDefault, tree::Index}, - repository::IndexPersistedOrInMemory, - worktree, + config::cache::util::ApplyLeniencyDefault, config::tree::Index, worktree, worktree::IndexPersistedOrInMemory, }; /// Index access @@ -44,6 +42,11 @@ impl crate::Repository { /// Return a shared worktree index which is updated automatically if the in-memory snapshot has become stale as the underlying file /// on disk has changed. /// + /// ### Notes + /// + /// * This will fail if the file doesn't exist, like in a newly initialized repository. If that is the case, use + /// [index_or_empty()](Self::index_or_empty) or [try_index()](Self::try_index) instead. + /// /// The index file is shared across all clones of this repository. pub fn index(&self) -> Result { self.try_index().and_then(|opt| match opt { @@ -110,12 +113,14 @@ impl crate::Repository { /// Create new index-file, which would live at the correct location, in memory from the given `tree`. /// /// Note that this is an expensive operation as it requires recursively traversing the entire tree to unpack it into the index. - pub fn index_from_tree( - &self, - tree: &gix_hash::oid, - ) -> Result { + pub fn index_from_tree(&self, tree: &gix_hash::oid) -> Result { Ok(gix_index::File::from_state( - gix_index::State::from_tree(tree, &self.objects)?, + gix_index::State::from_tree(tree, &self.objects, self.config.protect_options()?).map_err(|err| { + super::index_from_tree::Error::IndexFromTree { + id: tree.into(), + source: err, + } + })?, self.git_dir().join("index"), )) } diff --git a/gix/src/repository/mod.rs b/gix/src/repository/mod.rs index b60a4bc870c..5f3fbc0b215 100644 --- a/gix/src/repository/mod.rs +++ b/gix/src/repository/mod.rs @@ -1,4 +1,5 @@ //! +#![allow(clippy::empty_docs)] /// The kind of repository. #[derive(Debug, Clone, Copy, Eq, PartialEq, Ord, PartialOrd, Hash)] @@ -40,12 +41,15 @@ pub mod attributes; mod cache; mod config; /// +#[allow(clippy::empty_docs)] #[cfg(feature = "blob-diff")] pub mod diff; /// +#[allow(clippy::empty_docs)] #[cfg(feature = "dirwalk")] -pub mod dirwalk; +mod dirwalk; /// +#[allow(clippy::empty_docs)] #[cfg(feature = "attributes")] pub mod filter; mod graph; @@ -73,6 +77,25 @@ mod thread_safe; mod worktree; /// +#[allow(clippy::empty_docs)] +#[cfg(feature = "index")] +pub mod index_from_tree { + /// The error returned by [Repository::index_from_tree()](crate::Repository::index_from_tree). + #[derive(Debug, thiserror::Error)] + #[allow(missing_docs)] + pub enum Error { + #[error("Could not create index from tree at {id}")] + IndexFromTree { + id: gix_hash::ObjectId, + source: gix_index::init::from_tree::Error, + }, + #[error("Couldn't obtain configuration for core.protect*")] + BooleanConfig(#[from] crate::config::boolean::Error), + } +} + +/// +#[allow(clippy::empty_docs)] pub mod branch_remote_ref_name { /// The error returned by [Repository::branch_remote_ref_name()](crate::Repository::branch_remote_ref_name()). @@ -89,6 +112,7 @@ pub mod branch_remote_ref_name { } /// +#[allow(clippy::empty_docs)] pub mod branch_remote_tracking_ref_name { /// The error returned by [Repository::branch_remote_tracking_ref_name()](crate::Repository::branch_remote_tracking_ref_name()). @@ -104,17 +128,6 @@ pub mod branch_remote_tracking_ref_name { } } -/// A type to represent an index which either was loaded from disk as it was persisted there, or created on the fly in memory. -#[cfg(feature = "index")] -pub enum IndexPersistedOrInMemory { - /// The index as loaded from disk, and shared across clones of the owning `Repository`. - Persisted(crate::worktree::Index), - /// A temporary index as created from the `HEAD^{tree}`, with the file path set to the place where it would be stored naturally. - /// - /// Note that unless saved explicitly, it will not persist. - InMemory(gix_index::File), -} - /// #[cfg(feature = "attributes")] pub mod pathspec_defaults_ignore_case { @@ -141,7 +154,7 @@ pub mod index_or_load_from_head { #[error(transparent)] TreeId(#[from] gix_object::decode::Error), #[error(transparent)] - TraverseTree(#[from] gix_traverse::tree::breadthfirst::Error), + TraverseTree(#[from] crate::repository::index_from_tree::Error), #[error(transparent)] OpenIndex(#[from] crate::worktree::open_index::Error), } @@ -157,7 +170,7 @@ pub mod worktree_stream { #[error(transparent)] FindTree(#[from] crate::object::find::existing::Error), #[error(transparent)] - OpenTree(#[from] gix_traverse::tree::breadthfirst::Error), + OpenTree(#[from] crate::repository::index_from_tree::Error), #[error(transparent)] AttributesCache(#[from] crate::config::attribute_stack::Error), #[error(transparent)] diff --git a/gix/src/repository/object.rs b/gix/src/repository/object.rs index 77f188badf8..742a349e04f 100644 --- a/gix/src/repository/object.rs +++ b/gix/src/repository/object.rs @@ -1,5 +1,5 @@ #![allow(clippy::result_large_err)] -use std::{convert::TryInto, ops::DerefMut}; +use std::ops::DerefMut; use gix_hash::ObjectId; use gix_macros::momo; diff --git a/gix/src/repository/reference.rs b/gix/src/repository/reference.rs index b977c6ea872..9897f008012 100644 --- a/gix/src/repository/reference.rs +++ b/gix/src/repository/reference.rs @@ -1,5 +1,3 @@ -use std::convert::TryInto; - use gix_hash::ObjectId; use gix_macros::momo; use gix_ref::{ diff --git a/gix/src/repository/remote.rs b/gix/src/repository/remote.rs index 7f89c4df8e3..99a6c09b216 100644 --- a/gix/src/repository/remote.rs +++ b/gix/src/repository/remote.rs @@ -1,6 +1,4 @@ #![allow(clippy::result_large_err)] -use std::convert::TryInto; - use crate::{bstr::BStr, config, remote, remote::find, Remote}; impl crate::Repository { diff --git a/gix/src/repository/worktree.rs b/gix/src/repository/worktree.rs index 529243896ea..f24673b296e 100644 --- a/gix/src/repository/worktree.rs +++ b/gix/src/repository/worktree.rs @@ -86,7 +86,7 @@ impl crate::Repository { objects.clone(), pipeline, move |path, mode, attrs| -> std::io::Result<()> { - let entry = cache.at_entry(path, Some(mode.is_tree()), &objects)?; + let entry = cache.at_entry(path, Some(mode.into()), &objects)?; entry.matching_attributes(attrs); Ok(()) }, diff --git a/gix/src/revision/mod.rs b/gix/src/revision/mod.rs index 9bd23b994bd..0f45590bd9f 100644 --- a/gix/src/revision/mod.rs +++ b/gix/src/revision/mod.rs @@ -6,11 +6,13 @@ pub use gix_revision as plumbing; /// +#[allow(clippy::empty_docs)] pub mod walk; pub use walk::iter::Walk; /// #[cfg(feature = "revision")] +#[allow(clippy::empty_docs)] pub mod spec; /// The specification of a revision as parsed from a revision specification like `HEAD@{1}` or `v1.2.3...main`. diff --git a/gix/src/revision/spec/mod.rs b/gix/src/revision/spec/mod.rs index af58ecdff51..e7c9f244144 100644 --- a/gix/src/revision/spec/mod.rs +++ b/gix/src/revision/spec/mod.rs @@ -2,6 +2,7 @@ use crate::bstr::BStr; use crate::{ext::ReferenceExt, revision::Spec, Id, Reference}; /// +#[allow(clippy::empty_docs)] pub mod parse; mod impls { diff --git a/gix/src/revision/spec/parse/delegate/navigate.rs b/gix/src/revision/spec/parse/delegate/navigate.rs index 731a24136d3..43f40654f40 100644 --- a/gix/src/revision/spec/parse/delegate/navigate.rs +++ b/gix/src/revision/spec/parse/delegate/navigate.rs @@ -1,11 +1,11 @@ use std::collections::HashSet; use gix_hash::ObjectId; +use gix_index::entry::Stage; use gix_revision::spec::parse::{ delegate, delegate::{PeelTo, Traversal}, }; -use gix_traverse::commit::Sorting; use crate::{ bstr::{BStr, ByteSlice}, @@ -192,7 +192,7 @@ impl<'repo> delegate::Navigate for Delegate<'repo> { match oid .attach(repo) .ancestors() - .sorting(Sorting::ByCommitTimeNewestFirst) + .sorting(gix_traverse::commit::simple::Sorting::ByCommitTimeNewestFirst) .all() { Ok(iter) => { @@ -241,15 +241,10 @@ impl<'repo> delegate::Navigate for Delegate<'repo> { references .peeled() .filter_map(Result::ok) - .filter(|r| { - r.id() - .object() - .ok() - .map_or(false, |obj| obj.kind == gix_object::Kind::Commit) - }) + .filter(|r| r.id().header().ok().map_or(false, |obj| obj.kind().is_commit())) .filter_map(|r| r.detach().peeled), ) - .sorting(Sorting::ByCommitTimeNewestFirst) + .sorting(gix_traverse::commit::simple::Sorting::ByCommitTimeNewestFirst) .all() { Ok(iter) => { @@ -305,9 +300,18 @@ impl<'repo> delegate::Navigate for Delegate<'repo> { } fn index_lookup(&mut self, path: &BStr, stage: u8) -> Option<()> { + let stage = match stage { + 0 => Stage::Unconflicted, + 1 => Stage::Base, + 2 => Stage::Ours, + 3 => Stage::Theirs, + _ => unreachable!( + "BUG: driver will not pass invalid stages (and it uses integer to avoid gix-index as dependency)" + ), + }; self.unset_disambiguate_call(); match self.repo.index() { - Ok(index) => match index.entry_by_path_and_stage(path, stage.into()) { + Ok(index) => match index.entry_by_path_and_stage(path, stage) { Some(entry) => { self.objs[self.idx] .get_or_insert_with(HashSet::default) @@ -323,21 +327,17 @@ impl<'repo> delegate::Navigate for Delegate<'repo> { Some(()) } None => { - let stage_hint = [0, 1, 2] + let stage_hint = [Stage::Unconflicted, Stage::Base, Stage::Ours] .iter() .filter(|our_stage| **our_stage != stage) - .find_map(|stage| { - index - .entry_index_by_path_and_stage(path, (*stage).into()) - .map(|_| (*stage).into()) - }); + .find_map(|stage| index.entry_index_by_path_and_stage(path, *stage).map(|_| *stage)); let exists = self .repo .work_dir() .map_or(false, |root| root.join(gix_path::from_bstr(path)).exists()); self.err.push(Error::IndexLookup { desired_path: path.into(), - desired_stage: stage.into(), + desired_stage: stage, exists, stage_hint, }); diff --git a/gix/src/revision/spec/parse/mod.rs b/gix/src/revision/spec/parse/mod.rs index e4584776363..d9c38276a91 100644 --- a/gix/src/revision/spec/parse/mod.rs +++ b/gix/src/revision/spec/parse/mod.rs @@ -11,6 +11,7 @@ use crate::bstr::BString; pub use types::{Error, ObjectKindHint, Options, RefsHint}; /// +#[allow(clippy::empty_docs)] pub mod single { use crate::bstr::BString; @@ -26,6 +27,7 @@ pub mod single { } /// +#[allow(clippy::empty_docs)] pub mod error; impl<'repo> Spec<'repo> { diff --git a/gix/src/revision/spec/parse/types.rs b/gix/src/revision/spec/parse/types.rs index 297ab5b467b..9629b0b34d4 100644 --- a/gix/src/revision/spec/parse/types.rs +++ b/gix/src/revision/spec/parse/types.rs @@ -98,7 +98,7 @@ pub enum Error { desired: usize, available: usize, }, - #[error("Path {desired_path:?} did not exist in index at stage {desired_stage}{}{}", stage_hint.map(|actual|format!(". It does exist at stage {actual}")).unwrap_or_default(), exists.then(|| ". It exists on disk").unwrap_or(". It does not exist on disk"))] + #[error("Path {desired_path:?} did not exist in index at stage {}{}{}", *desired_stage as u8, stage_hint.map(|actual|format!(". It does exist at stage {}", actual as u8)).unwrap_or_default(), exists.then(|| ". It exists on disk").unwrap_or(". It does not exist on disk"))] IndexLookup { desired_path: BString, desired_stage: gix_index::entry::Stage, @@ -184,7 +184,7 @@ pub enum Error { next: Option>, }, #[error(transparent)] - Traverse(#[from] gix_traverse::commit::ancestors::Error), + Traverse(#[from] gix_traverse::commit::simple::Error), #[error(transparent)] Walk(#[from] crate::revision::walk::Error), #[error("Spec does not contain a single object id")] diff --git a/gix/src/revision/walk.rs b/gix/src/revision/walk.rs index 19d15d569ab..dd7c5a8c1dc 100644 --- a/gix/src/revision/walk.rs +++ b/gix/src/revision/walk.rs @@ -8,7 +8,7 @@ use crate::{ext::ObjectIdExt, revision, Repository}; #[allow(missing_docs)] pub enum Error { #[error(transparent)] - AncestorIter(#[from] gix_traverse::commit::ancestors::Error), + SimpleTraversal(#[from] gix_traverse::commit::simple::Error), #[error(transparent)] ShallowCommits(#[from] crate::shallow::open::Error), #[error(transparent)] @@ -22,8 +22,8 @@ pub struct Info<'repo> { pub id: gix_hash::ObjectId, /// All parent ids we have encountered. Note that these will be at most one if [`Parents::First`][gix_traverse::commit::Parents::First] is enabled. pub parent_ids: gix_traverse::commit::ParentIds, - /// The time at which the commit was created. It's only `Some(_)` if sorting is not [`Sorting::BreadthFirst`][gix_traverse::commit::Sorting::BreadthFirst], - /// as the walk needs to require the commit-date. + /// The time at which the commit was created. It will only be `Some(_)` if the chosen traversal was + /// taking dates into consideration. pub commit_time: Option, repo: &'repo Repository, @@ -91,7 +91,7 @@ impl<'repo> Info<'repo> { pub struct Platform<'repo> { pub(crate) repo: &'repo Repository, pub(crate) tips: Vec, - pub(crate) sorting: gix_traverse::commit::Sorting, + pub(crate) sorting: gix_traverse::commit::simple::Sorting, pub(crate) parents: gix_traverse::commit::Parents, pub(crate) use_commit_graph: Option, pub(crate) commit_graph: Option, @@ -113,7 +113,7 @@ impl<'repo> Platform<'repo> { /// Create-time builder methods impl<'repo> Platform<'repo> { /// Set the sort mode for commits to the given value. The default is to order topologically breadth-first. - pub fn sorting(mut self, sorting: gix_traverse::commit::Sorting) -> Self { + pub fn sorting(mut self, sorting: gix_traverse::commit::simple::Sorting) -> Self { self.sorting = sorting; self } @@ -166,40 +166,35 @@ impl<'repo> Platform<'repo> { Ok(revision::Walk { repo, inner: Box::new( - gix_traverse::commit::Ancestors::filtered( - tips, - gix_traverse::commit::ancestors::State::default(), - &repo.objects, - { - // Note that specific shallow handling for commit-graphs isn't needed as these contain - // all information there is, and exclude shallow parents to be structurally consistent. - let shallow_commits = repo.shallow_commits()?; - let mut grafted_parents_to_skip = Vec::new(); - let mut buf = Vec::new(); - move |id| { - if !filter(id) { - return false; - } - match shallow_commits.as_ref() { - Some(commits) => { - let id = id.to_owned(); - if let Ok(idx) = grafted_parents_to_skip.binary_search(&id) { - grafted_parents_to_skip.remove(idx); - return false; - }; - if commits.binary_search(&id).is_ok() { - if let Ok(commit) = repo.objects.find_commit_iter(&id, &mut buf) { - grafted_parents_to_skip.extend(commit.parent_ids()); - grafted_parents_to_skip.sort(); - } - }; - true - } - None => true, + gix_traverse::commit::Simple::filtered(tips, &repo.objects, { + // Note that specific shallow handling for commit-graphs isn't needed as these contain + // all information there is, and exclude shallow parents to be structurally consistent. + let shallow_commits = repo.shallow_commits()?; + let mut grafted_parents_to_skip = Vec::new(); + let mut buf = Vec::new(); + move |id| { + if !filter(id) { + return false; + } + match shallow_commits.as_ref() { + Some(commits) => { + let id = id.to_owned(); + if let Ok(idx) = grafted_parents_to_skip.binary_search(&id) { + grafted_parents_to_skip.remove(idx); + return false; + }; + if commits.binary_search(&id).is_ok() { + if let Ok(commit) = repo.objects.find_commit_iter(&id, &mut buf) { + grafted_parents_to_skip.extend(commit.parent_ids()); + grafted_parents_to_skip.sort(); + } + }; + true } + None => true, } - }, - ) + } + }) .sorting(sorting)? .parents(parents) .commit_graph( @@ -226,13 +221,12 @@ pub(crate) mod iter { /// The iterator returned by [`crate::revision::walk::Platform::all()`]. pub struct Walk<'repo> { pub(crate) repo: &'repo crate::Repository, - pub(crate) inner: Box< - dyn Iterator> + 'repo, - >, + pub(crate) inner: + Box> + 'repo>, } impl<'repo> Iterator for Walk<'repo> { - type Item = Result, gix_traverse::commit::ancestors::Error>; + type Item = Result, gix_traverse::commit::simple::Error>; fn next(&mut self) -> Option { self.inner diff --git a/gix/src/shallow.rs b/gix/src/shallow.rs index d49653a6506..dfe376283dd 100644 --- a/gix/src/shallow.rs +++ b/gix/src/shallow.rs @@ -79,6 +79,7 @@ pub mod write { pub use write::function::write; /// +#[allow(clippy::empty_docs)] pub mod open { /// The error returned by [`Repository::shallow_commits()`][crate::Repository::shallow_commits()]. #[derive(Debug, thiserror::Error)] diff --git a/gix/src/status/index_worktree.rs b/gix/src/status/index_worktree.rs new file mode 100644 index 00000000000..6bb44054b47 --- /dev/null +++ b/gix/src/status/index_worktree.rs @@ -0,0 +1,834 @@ +use crate::bstr::{BStr, BString}; +use crate::{config, Repository}; +use gix_status::index_as_worktree::traits::{CompareBlobs, SubmoduleStatus}; +use std::sync::atomic::AtomicBool; + +/// The error returned by [Repository::index_worktree_status()]. +#[derive(Debug, thiserror::Error)] +#[allow(missing_docs)] +pub enum Error { + #[error("A working tree is required to perform a directory walk")] + MissingWorkDir, + #[error(transparent)] + AttributesAndExcludes(#[from] crate::repository::attributes::Error), + #[error(transparent)] + Pathspec(#[from] crate::pathspec::init::Error), + #[error(transparent)] + Prefix(#[from] gix_path::realpath::Error), + #[error(transparent)] + FilesystemOptions(#[from] config::boolean::Error), + #[error(transparent)] + IndexAsWorktreeWithRenames(#[from] gix_status::index_as_worktree_with_renames::Error), + #[error(transparent)] + StatOptions(#[from] config::stat_options::Error), + #[error(transparent)] + ResourceCache(#[from] crate::diff::resource_cache::Error), +} + +/// Options for use with [Repository::index_worktree_status()]. +#[derive(Default, Debug, Clone, Copy, PartialEq)] +pub struct Options { + /// The way all output should be sorted. + /// + /// If `None`, and depending on the `rewrites` field, output will be immediate but the output order + /// isn't determined, and may differ between two runs. `rewrites` also depend on the order of entries that + /// are presented to it, hence for deterministic results, sorting needs to be enabled. + /// + /// If `Some(_)`, all entries are collected beforehand, so they can be sorted before outputting any of them + /// to the user. + /// + /// If immediate output of entries in any order is desired, this should be `None`, + /// along with `rewrites` being `None` as well. + pub sorting: Option, + /// If not `None`, the options to configure the directory walk, determining how its results will look like. + /// + /// If `None`, only modification checks are performed. + /// + /// Can be instantiated with [Repository::dirwalk_options()]. + pub dirwalk_options: Option, + /// If `Some(_)`, along with `Some(_)` in `dirwalk_options`, rewrite tracking will be performed between the + /// index and the working tree. + /// Note that there is no git-configuration specific to index-worktree rename tracking. + /// When rewrite tracking is enabled, there will be a delay for some entries as they partake in the rename-analysis. + pub rewrites: Option, + /// If set, don't use more than this amount of threads for the tracked modification check. + /// Otherwise, usually use as many threads as there are logical cores. + /// A value of 0 is interpreted as no-limit + pub thread_limit: Option, +} + +impl Repository { + /// Obtain the status between the index and the worktree, involving modification checks + /// for all tracked files along with information about untracked (and posisbly ignored) files (if configured). + /// + /// * `index` + /// - The index to use for modification checks, and to know which files are tacked when applying the dirwalk. + /// * `patterns` + /// - Optional patterns to use to limit the paths to look at. If empty, all paths are considered. + /// * `delegate` + /// - The sink for receiving all status data. + /// * `compare` + /// - The implementations for fine-grained control over what happens if a hash must be recalculated. + /// * `submodule` + /// - Control what kind of information to retrieve when a submodule is encountered while traversing the index. + /// * `progress` + /// - A progress indication for index modification checks. + /// * `should_interrupt` + /// - A flag to stop the whole operation. + /// * `options` + /// - Additional configuration for all parts of the operation. + /// + /// ### Note + /// + /// This is a lower-level method, prefer the [`status`](Repository::status()) method for greater ease of use. + #[allow(clippy::too_many_arguments)] + pub fn index_worktree_status<'index, T, U, E>( + &self, + index: &'index gix_index::State, + patterns: impl IntoIterator>, + delegate: &mut impl gix_status::index_as_worktree_with_renames::VisitEntry< + 'index, + ContentChange = T, + SubmoduleStatus = U, + >, + compare: impl CompareBlobs + Send + Clone, + submodule: impl SubmoduleStatus + Send + Clone, + progress: &mut dyn gix_features::progress::Progress, + should_interrupt: &AtomicBool, + options: Options, + ) -> Result + where + T: Send + Clone, + U: Send + Clone, + E: std::error::Error + Send + Sync + 'static, + { + let _span = gix_trace::coarse!("gix::index_worktree_status"); + let workdir = self.work_dir().ok_or(Error::MissingWorkDir)?; + let attrs_and_excludes = self.attributes( + index, + crate::worktree::stack::state::attributes::Source::WorktreeThenIdMapping, + crate::worktree::stack::state::ignore::Source::WorktreeThenIdMappingIfNotSkipped, + None, + )?; + let pathspec = crate::Pathspec::new( + self, + options + .dirwalk_options + .as_ref() + .map_or(false, |opts| opts.empty_patterns_match_prefix), + patterns, + true, /* inherit ignore case */ + || Ok(attrs_and_excludes.clone()), + )?; + + let cwd = self.current_dir(); + let git_dir_realpath = crate::path::realpath_opts(self.git_dir(), cwd, crate::path::realpath::MAX_SYMLINKS)?; + let fs_caps = self.filesystem_options()?; + let accelerate_lookup = fs_caps.ignore_case.then(|| index.prepare_icase_backing()); + let resource_cache = crate::diff::resource_cache( + self, + gix_diff::blob::pipeline::Mode::ToGit, + attrs_and_excludes.inner, + gix_diff::blob::pipeline::WorktreeRoots { + old_root: None, + new_root: Some(workdir.to_owned()), + }, + )?; + + let out = gix_status::index_as_worktree_with_renames( + index, + workdir, + delegate, + compare, + submodule, + self.objects.clone().into_arc().expect("arc conversion always works"), + progress, + gix_status::index_as_worktree_with_renames::Context { + pathspec: pathspec.search, + resource_cache, + should_interrupt, + dirwalk: gix_status::index_as_worktree_with_renames::DirwalkContext { + git_dir_realpath: git_dir_realpath.as_path(), + current_dir: cwd, + ignore_case_index_lookup: accelerate_lookup.as_ref(), + }, + }, + gix_status::index_as_worktree_with_renames::Options { + sorting: options.sorting, + object_hash: self.object_hash(), + tracked_file_modifications: gix_status::index_as_worktree::Options { + fs: fs_caps, + thread_limit: options.thread_limit, + stat: self.stat_options()?, + }, + dirwalk: options.dirwalk_options.map(Into::into), + rewrites: options.rewrites, + }, + )?; + Ok(out) + } +} + +/// An implementation of a trait to use with [`Repository::index_worktree_status()`] to compute the submodule status +/// using [Submodule::status()](crate::Submodule::status()). +#[derive(Clone)] +pub struct BuiltinSubmoduleStatus { + mode: crate::status::Submodule, + #[cfg(feature = "parallel")] + repo: crate::ThreadSafeRepository, + #[cfg(not(feature = "parallel"))] + git_dir: std::path::PathBuf, + submodule_paths: Vec, +} + +/// +#[allow(clippy::empty_docs)] +mod submodule_status { + use crate::bstr; + use crate::bstr::BStr; + use crate::status::index_worktree::BuiltinSubmoduleStatus; + use crate::status::Submodule; + use std::borrow::Cow; + + impl BuiltinSubmoduleStatus { + /// Create a new instance from a `repo` and a `mode` to control how the submodule status will be obtained. + pub fn new( + repo: crate::ThreadSafeRepository, + mode: Submodule, + ) -> Result { + let local_repo = repo.to_thread_local(); + let submodule_paths = match local_repo.submodules()? { + Some(sm) => { + let mut v: Vec<_> = sm.filter_map(|sm| sm.path().ok().map(Cow::into_owned)).collect(); + v.sort(); + v + } + None => Vec::new(), + }; + Ok(Self { + mode, + #[cfg(feature = "parallel")] + repo, + #[cfg(not(feature = "parallel"))] + git_dir: local_repo.git_dir().to_owned(), + submodule_paths, + }) + } + } + + /// The error returned submodule status checks. + #[derive(Debug, thiserror::Error)] + #[allow(missing_docs)] + pub enum Error { + #[error(transparent)] + SubmoduleStatus(#[from] crate::submodule::status::Error), + #[error(transparent)] + IgnoreConfig(#[from] crate::submodule::config::Error), + } + + impl gix_status::index_as_worktree::traits::SubmoduleStatus for BuiltinSubmoduleStatus { + type Output = crate::submodule::Status; + type Error = Error; + + fn status(&mut self, _entry: &gix_index::Entry, rela_path: &BStr) -> Result, Self::Error> { + use bstr::ByteSlice; + if self + .submodule_paths + .binary_search_by(|path| path.as_bstr().cmp(rela_path)) + .is_err() + { + return Ok(None); + } + #[cfg(feature = "parallel")] + let repo = self.repo.to_thread_local(); + #[cfg(not(feature = "parallel"))] + let Ok(repo) = crate::open(&self.git_dir) else { + return Ok(None); + }; + let Ok(Some(mut submodules)) = repo.submodules() else { + return Ok(None); + }; + let Some(sm) = submodules.find(|sm| sm.path().map_or(false, |path| path == rela_path)) else { + return Ok(None); + }; + let (ignore, check_dirty) = match self.mode { + Submodule::AsConfigured { check_dirty } => (sm.ignore()?.unwrap_or_default(), check_dirty), + Submodule::Given { ignore, check_dirty } => (ignore, check_dirty), + }; + let status = sm.status(ignore, check_dirty)?; + Ok(status.is_dirty().and_then(|dirty| dirty.then_some(status))) + } + } +} + +/// An iterator for changes between the index and the worktree. +/// +/// Note that depending on the underlying configuration, there might be a significant delay until the first +/// item is received due to the buffering necessary to perform rename tracking and/or sorting. +/// +/// ### Submodules +/// +/// Note that submodules can be set to 'inactive', which will not exclude them from the status operation, similar to +/// how `git status` includes them. +/// +/// ### Index Changes +/// +/// Changes to the index are collected and it's possible to write the index back using [iter::Outcome::write_changes()]. +/// Note that these changes are not observable, they will always be kept. +/// +/// ### Parallel Operation +/// +/// Note that without the `parallel` feature, the iterator becomes 'serial', which means all status will be computed in advance +/// and it's non-interruptible, yielding worse performance for is-dirty checks for instance as interruptions won't happen. +/// It's a crutch that is just there to make single-threaded applications possible at all, as it's not really an iterator +/// anymore. If this matters, better run [Repository::index_worktree_status()] by hand as it provides all control one would need, +/// just not as an iterator. +/// +/// Also, even with `parallel` set, the first call to `next()` will block until there is an item available, without a chance +/// to interrupt unless [`status::Platform::should_interrupt_*()`](crate::status::Platform::should_interrupt_shared()) was +/// configured. +pub struct Iter { + #[cfg(feature = "parallel")] + #[allow(clippy::type_complexity)] + rx_and_join: Option<( + std::sync::mpsc::Receiver, + std::thread::JoinHandle>, + )>, + #[cfg(feature = "parallel")] + should_interrupt: crate::status::OwnedOrStaticAtomicBool, + /// Without parallelization, the iterator has to buffer all changes in advance. + #[cfg(not(feature = "parallel"))] + items: std::vec::IntoIter, + /// The outcome of the operation, only available once the operation has ended. + out: Option, + /// The set of `(entry_index, change)` we extracted in order to potentially write back the index with the changes applied. + changes: Vec<(usize, iter::ApplyChange)>, +} + +/// +#[allow(clippy::empty_docs)] +pub mod iter { + use crate::bstr::{BStr, BString}; + use crate::config::cache::util::ApplyLeniencyDefault; + use crate::status::index_worktree::{iter, BuiltinSubmoduleStatus}; + use crate::status::{index_worktree, Platform}; + use crate::worktree::IndexPersistedOrInMemory; + use gix_status::index_as_worktree::{Change, EntryStatus}; + + pub use gix_status::index_as_worktree_with_renames::Summary; + + pub(super) enum ApplyChange { + SetSizeToZero, + NewStat(crate::index::entry::Stat), + } + + /// The data the thread sends over to the receiving iterator. + pub struct Outcome { + /// The outcome of the index-to-worktree comparison operation. + pub index_worktree: gix_status::index_as_worktree_with_renames::Outcome, + /// The index that was used for the operation. + pub index: crate::worktree::IndexPersistedOrInMemory, + skip_hash: bool, + changes: Option>, + } + + impl Outcome { + /// Returns `true` if the index has received currently unapplied changes that *should* be written back. + /// + /// If they are not written back, subsequent `status` operations will take longer to complete, whereas the + /// additional work can be prevented by writing the changes back to the index. + pub fn has_changes(&self) -> bool { + self.changes.as_ref().map_or(false, |changes| !changes.is_empty()) + } + + /// Write the changes if there are any back to the index file. + /// This can only be done once as the changes are consumed in the process, if there were any. + pub fn write_changes(&mut self) -> Option> { + let _span = gix_features::trace::coarse!("gix::status::index_worktree::iter::Outcome::write_changes()"); + let changes = self.changes.take()?; + let mut index = match &self.index { + IndexPersistedOrInMemory::Persisted(persisted) => (***persisted).clone(), + IndexPersistedOrInMemory::InMemory(index) => index.clone(), + }; + + let entries = index.entries_mut(); + for (entry_index, change) in changes { + let entry = &mut entries[entry_index]; + match change { + ApplyChange::SetSizeToZero => { + entry.stat.size = 0; + } + ApplyChange::NewStat(new_stat) => { + entry.stat = new_stat; + } + } + } + + Some(index.write(crate::index::write::Options { + extensions: Default::default(), + skip_hash: self.skip_hash, + })) + } + } + + /// Either an index entry for renames or another directory entry in case of copies. + #[derive(Clone, PartialEq, Debug)] + pub enum RewriteSource { + /// The source originates in the index and is detected as missing in the working tree. + /// This can also happen for copies. + RewriteFromIndex { + /// The entry that is the source of the rewrite, which means it was removed on disk, + /// equivalent to [Change::Removed]. + /// + /// Note that the [entry-id](gix_index::Entry::id) is the content-id of the source of the rewrite. + source_entry: gix_index::Entry, + /// The index of the `source_entry` for lookup in [`gix_index::State::entries()`] - useful to look at neighbors. + source_entry_index: usize, + /// The repository-relative path of the `source_entry`. + source_rela_path: BString, + /// The computed status of the `source_entry`. + source_status: gix_status::index_as_worktree::EntryStatus<(), crate::submodule::Status>, + }, + /// This source originates in the directory tree and is always the source of copies. + CopyFromDirectoryEntry { + /// The source of the copy operation, which is also an entry of the directory walk. + /// + /// Note that its [`rela_path`](gix_dir::EntryRef::rela_path) is the source of the rewrite. + source_dirwalk_entry: gix_dir::Entry, + /// `collapsed_directory_status` is `Some(dir_status)` if this `source_dirwalk_entry` was part of a directory with the given + /// `dir_status` that wasn't the same as the one of `source_dirwalk_entry` and + /// if [gix_dir::walk::Options::emit_collapsed] was [CollapsedEntriesEmissionMode::OnStatusMismatch](gix_dir::walk::CollapsedEntriesEmissionMode::OnStatusMismatch). + /// It will also be `Some(dir_status)` if that option was [CollapsedEntriesEmissionMode::All](gix_dir::walk::CollapsedEntriesEmissionMode::All). + source_dirwalk_entry_collapsed_directory_status: Option, + /// The object id as it would appear if the entry was written to the object database. + /// It's the same as [`dirwalk_entry_id`](Item::Rewrite), or `diff` is `Some(_)` to indicate that the copy + /// was determined by similarity, not by content equality. + source_dirwalk_entry_id: gix_hash::ObjectId, + }, + } + + /// Access + impl RewriteSource { + /// The repository-relative path of this source. + pub fn rela_path(&self) -> &BStr { + match self { + RewriteSource::RewriteFromIndex { source_rela_path, .. } => source_rela_path.as_ref(), + RewriteSource::CopyFromDirectoryEntry { + source_dirwalk_entry, .. + } => source_dirwalk_entry.rela_path.as_ref(), + } + } + } + + impl<'index> From> + for RewriteSource + { + fn from(value: gix_status::index_as_worktree_with_renames::RewriteSource<'index, (), SubmoduleStatus>) -> Self { + match value { + gix_status::index_as_worktree_with_renames::RewriteSource::RewriteFromIndex { + index_entries: _, + source_entry, + source_entry_index, + source_rela_path, + source_status, + } => RewriteSource::RewriteFromIndex { + source_entry: source_entry.clone(), + source_entry_index, + source_rela_path: source_rela_path.to_owned(), + source_status, + }, + gix_status::index_as_worktree_with_renames::RewriteSource::CopyFromDirectoryEntry { + source_dirwalk_entry, + source_dirwalk_entry_collapsed_directory_status, + source_dirwalk_entry_id, + } => RewriteSource::CopyFromDirectoryEntry { + source_dirwalk_entry, + source_dirwalk_entry_collapsed_directory_status, + source_dirwalk_entry_id, + }, + } + } + } + + /// The item produced by the iterator + #[derive(Clone, PartialEq, Debug)] + pub enum Item { + /// A tracked file was modified, and index-specific information is passed. + Modification { + /// The entry with modifications. + entry: gix_index::Entry, + /// The index of the `entry` for lookup in [`gix_index::State::entries()`] - useful to look at neighbors. + entry_index: usize, + /// The repository-relative path of the entry. + rela_path: BString, + /// The computed status of the entry. + status: gix_status::index_as_worktree::EntryStatus<(), SubmoduleStatus>, + }, + /// An entry returned by the directory walk, without any relation to the index. + /// + /// This can happen if ignored files are returned as well, or if rename-tracking is disabled. + DirectoryContents { + /// The entry found during the disk traversal. + entry: gix_dir::Entry, + /// `collapsed_directory_status` is `Some(dir_status)` if this `entry` was part of a directory with the given + /// `dir_status` that wasn't the same as the one of `entry` and if [gix_dir::walk::Options::emit_collapsed] was + /// [CollapsedEntriesEmissionMode::OnStatusMismatch](gix_dir::walk::CollapsedEntriesEmissionMode::OnStatusMismatch). + /// It will also be `Some(dir_status)` if that option was [CollapsedEntriesEmissionMode::All](gix_dir::walk::CollapsedEntriesEmissionMode::All). + collapsed_directory_status: Option, + }, + /// The rewrite tracking discovered a match between a deleted and added file, and considers them equal enough, + /// depending on the tracker settings. + /// + /// Note that the source of the rewrite is always the index as it detects the absence of entries, something that + /// can't be done during a directory walk. + Rewrite { + /// The source of the rewrite operation. + source: RewriteSource, + /// The untracked entry found during the disk traversal, the destination of the rewrite. + /// + /// Note that its [`rela_path`](gix_dir::EntryRef::rela_path) is the destination of the rewrite, and the current + /// location of the entry. + dirwalk_entry: gix_dir::Entry, + /// `collapsed_directory_status` is `Some(dir_status)` if this `dirwalk_entry` was part of a directory with the given + /// `dir_status` that wasn't the same as the one of `dirwalk_entry` and if [gix_dir::walk::Options::emit_collapsed] was + /// [CollapsedEntriesEmissionMode::OnStatusMismatch](gix_dir::walk::CollapsedEntriesEmissionMode::OnStatusMismatch). + /// It will also be `Some(dir_status)` if that option was [CollapsedEntriesEmissionMode::All](gix_dir::walk::CollapsedEntriesEmissionMode::All). + dirwalk_entry_collapsed_directory_status: Option, + /// The object id after the rename, specifically hashed in order to determine equality. + dirwalk_entry_id: gix_hash::ObjectId, + /// It's `None` if the 'source.id' is equal to `dirwalk_entry_id`, as identity made an actual diff computation unnecessary. + /// Otherwise, and if enabled, it's `Some(stats)` to indicate how similar both entries were. + diff: Option, + /// If true, this rewrite is created by copy, and 'source.id' is pointing to its source. + /// Otherwise, it's a rename, and 'source.id' points to a deleted object, + /// as renames are tracked as deletions and additions of the same or similar content. + copy: bool, + }, + } + + impl Item { + /// Return a simplified summary of the item as digest of its status, or `None` if this item is + /// created from the directory walk and is *not untracked*, or if it is merely to communicate + /// a needed update to the index entry. + pub fn summary(&self) -> Option

{ + use gix_status::index_as_worktree_with_renames::Summary::*; + Some(match self { + Item::Modification { status, .. } => match status { + EntryStatus::Conflict(_) => Conflict, + EntryStatus::Change(change) => match change { + Change::Removed => Removed, + Change::Type => TypeChange, + Change::Modification { .. } | Change::SubmoduleModification(_) => Modified, + }, + EntryStatus::NeedsUpdate(_) => return None, + EntryStatus::IntentToAdd => IntentToAdd, + }, + Item::DirectoryContents { entry, .. } => { + if matches!(entry.status, gix_dir::entry::Status::Untracked) { + Added + } else { + return None; + } + } + Item::Rewrite { copy, .. } => { + if *copy { + Copied + } else { + Renamed + } + } + }) + } + + /// The repository-relative path of the entry contained in this item. + pub fn rela_path(&self) -> &BStr { + match self { + Item::Modification { rela_path, .. } => rela_path.as_ref(), + Item::DirectoryContents { entry, .. } => entry.rela_path.as_ref(), + Item::Rewrite { dirwalk_entry, .. } => dirwalk_entry.rela_path.as_ref(), + } + } + } + + impl<'index> From> for Item { + fn from(value: gix_status::index_as_worktree_with_renames::Entry<'index, (), SubmoduleStatus>) -> Self { + match value { + gix_status::index_as_worktree_with_renames::Entry::Modification { + entries: _, + entry, + entry_index, + rela_path, + status, + } => Item::Modification { + entry: entry.clone(), + entry_index, + rela_path: rela_path.to_owned(), + status, + }, + gix_status::index_as_worktree_with_renames::Entry::DirectoryContents { + entry, + collapsed_directory_status, + } => Item::DirectoryContents { + entry, + collapsed_directory_status, + }, + gix_status::index_as_worktree_with_renames::Entry::Rewrite { + source, + dirwalk_entry, + dirwalk_entry_collapsed_directory_status, + dirwalk_entry_id, + diff, + copy, + } => Item::Rewrite { + source: source.into(), + dirwalk_entry, + dirwalk_entry_collapsed_directory_status, + dirwalk_entry_id, + diff, + copy, + }, + } + } + } + + type SubmoduleStatus = crate::submodule::Status; + + /// The error returned by [Platform::into_index_worktree_iter()](crate::status::Platform::into_index_worktree_iter()). + #[derive(Debug, thiserror::Error)] + #[allow(missing_docs)] + pub enum Error { + #[error(transparent)] + Index(#[from] crate::worktree::open_index::Error), + #[error("Failed to spawn producer thread")] + #[cfg(feature = "parallel")] + SpawnThread(#[source] std::io::Error), + #[error(transparent)] + #[cfg(not(feature = "parallel"))] + IndexWorktreeStatus(#[from] crate::status::index_worktree::Error), + #[error(transparent)] + ConfigSkipHash(#[from] crate::config::boolean::Error), + #[error(transparent)] + PrepareSubmodules(#[from] crate::submodule::modules::Error), + } + + /// Lifecycle + impl<'repo, Progress> Platform<'repo, Progress> + where + Progress: gix_features::progress::Progress, + { + /// Turn the platform into an iterator for changes between the index and the working tree. + /// + /// * `patterns` + /// - Optional patterns to use to limit the paths to look at. If empty, all paths are considered. + #[doc(alias = "diff_index_to_workdir", alias = "git2")] + pub fn into_index_worktree_iter( + self, + patterns: impl IntoIterator, + ) -> Result { + let index = match self.index { + None => IndexPersistedOrInMemory::Persisted(self.repo.index_or_empty()?), + Some(index) => index, + }; + + let skip_hash = self + .repo + .config + .resolved + .boolean("index", None, "skipHash") + .map(|res| crate::config::tree::Index::SKIP_HASH.enrich_error(res)) + .transpose() + .with_lenient_default(self.repo.config.lenient_config)? + .unwrap_or_default(); + let should_interrupt = self.should_interrupt.clone().unwrap_or_default(); + let submodule = BuiltinSubmoduleStatus::new(self.repo.clone().into_sync(), self.submodules)?; + #[cfg(feature = "parallel")] + { + let (tx, rx) = std::sync::mpsc::channel(); + let mut collect = Collect { tx }; + let patterns: Vec<_> = patterns.into_iter().collect(); + let join = std::thread::Builder::new() + .name("gix::status::index_worktree::iter::producer".into()) + .spawn({ + let repo = self.repo.clone().into_sync(); + let options = self.index_worktree_options; + let should_interrupt = should_interrupt.clone(); + let mut progress = self.progress; + move || -> Result<_, crate::status::index_worktree::Error> { + let repo = repo.to_thread_local(); + let out = repo.index_worktree_status( + &index, + patterns, + &mut collect, + gix_status::index_as_worktree::traits::FastEq, + submodule, + &mut progress, + &should_interrupt, + options, + )?; + Ok(Outcome { + index_worktree: out, + index, + changes: None, + skip_hash, + }) + } + }) + .map_err(Error::SpawnThread)?; + + Ok(super::Iter { + rx_and_join: Some((rx, join)), + should_interrupt, + changes: Vec::new(), + out: None, + }) + } + #[cfg(not(feature = "parallel"))] + { + let mut collect = Collect { items: Vec::new() }; + + let repo = self.repo.clone().into_sync(); + let options = self.index_worktree_options; + let mut progress = self.progress; + let repo = repo.to_thread_local(); + let out = repo.index_worktree_status( + &index, + patterns, + &mut collect, + gix_status::index_as_worktree::traits::FastEq, + submodule, + &mut progress, + &should_interrupt, + options, + )?; + let mut out = Outcome { + index_worktree: out, + index, + changes: None, + skip_hash, + }; + let mut iter = super::Iter { + items: Vec::new().into_iter(), + changes: Vec::new(), + out: None, + }; + let items = collect + .items + .into_iter() + .filter_map(|item| iter.maybe_keep_index_change(item)) + .collect::>(); + out.changes = (!iter.changes.is_empty()).then(|| std::mem::take(&mut iter.changes)); + iter.items = items.into_iter(); + iter.out = Some(out); + Ok(iter) + } + } + } + + impl Iterator for super::Iter { + type Item = Result; + + fn next(&mut self) -> Option { + #[cfg(feature = "parallel")] + loop { + let (rx, _join) = self.rx_and_join.as_ref()?; + match rx.recv().ok() { + Some(item) => { + if let Some(item) = self.maybe_keep_index_change(item) { + break Some(Ok(item)); + } + continue; + } + None => { + let (_rx, handle) = self.rx_and_join.take()?; + break match handle.join().expect("no panic") { + Ok(mut out) => { + out.changes = Some(std::mem::take(&mut self.changes)); + self.out = Some(out); + None + } + Err(err) => Some(Err(err)), + }; + } + } + } + #[cfg(not(feature = "parallel"))] + self.items.next().map(Ok) + } + } + + /// Access + impl super::Iter { + /// Return the outcome of the iteration, or `None` if the iterator isn't fully consumed. + pub fn outcome_mut(&mut self) -> Option<&mut Outcome> { + self.out.as_mut() + } + + /// Turn the iterator into the iteration outcome, which is `None` on error or if the iteration + /// isn't complete. + pub fn into_outcome(mut self) -> Option { + self.out.take() + } + } + + impl super::Iter { + fn maybe_keep_index_change(&mut self, item: Item) -> Option { + let change = match item { + Item::Modification { + status: gix_status::index_as_worktree::EntryStatus::NeedsUpdate(stat), + entry_index, + .. + } => (entry_index, ApplyChange::NewStat(stat)), + Item::Modification { + status: + gix_status::index_as_worktree::EntryStatus::Change( + gix_status::index_as_worktree::Change::Modification { + set_entry_stat_size_zero, + .. + }, + ), + entry_index, + .. + } if set_entry_stat_size_zero => (entry_index, ApplyChange::SetSizeToZero), + _ => return Some(item), + }; + + self.changes.push(change); + None + } + } + + #[cfg(feature = "parallel")] + impl Drop for super::Iter { + fn drop(&mut self) { + crate::util::parallel_iter_drop(self.rx_and_join.take(), &self.should_interrupt); + } + } + + struct Collect { + #[cfg(feature = "parallel")] + tx: std::sync::mpsc::Sender, + #[cfg(not(feature = "parallel"))] + items: Vec, + } + + impl<'index> gix_status::index_as_worktree_with_renames::VisitEntry<'index> for Collect { + type ContentChange = ::Output; + type SubmoduleStatus = + ::Output; + + fn visit_entry( + &mut self, + entry: gix_status::index_as_worktree_with_renames::Entry< + 'index, + Self::ContentChange, + Self::SubmoduleStatus, + >, + ) { + // NOTE: we assume that the receiver triggers interruption so the operation will stop if the receiver is down. + #[cfg(feature = "parallel")] + self.tx.send(entry.into()).ok(); + #[cfg(not(feature = "parallel"))] + self.items.push(entry.into()); + } + } +} diff --git a/gix/src/status/mod.rs b/gix/src/status/mod.rs new file mode 100644 index 00000000000..f22340fa1ca --- /dev/null +++ b/gix/src/status/mod.rs @@ -0,0 +1,180 @@ +use crate::config::cache::util::ApplyLeniencyDefault; +use crate::util::OwnedOrStaticAtomicBool; +use crate::{config, Repository}; +pub use gix_status as plumbing; + +/// A structure to hold options configuring the status request, which can then be turned into an iterator. +pub struct Platform<'repo, Progress> +where + Progress: gix_features::progress::Progress + 'static, +{ + repo: &'repo Repository, + progress: Progress, + index: Option, + submodules: Submodule, + index_worktree_options: index_worktree::Options, + should_interrupt: Option, +} + +/// How to obtain a submodule's status. +#[derive(Copy, Clone, Debug, Ord, PartialOrd, Eq, PartialEq, Hash)] +pub enum Submodule { + /// Use the ['ignore' value](crate::Submodule::ignore) to determine which submodules + /// participate in the status query, and to which extent. + AsConfigured { + /// If `true`, default `false`, the computation will stop once the first in a ladder operations + /// ordered from cheap to expensive shows that the submodule is dirty. + /// Thus, submodules that are clean will still impose the complete set of computation, as configured. + check_dirty: bool, + }, + /// Instead of the configuration, use the given ['ignore' value](crate::submodule::config::Ignore). + /// This makes it possible to fine-tune the amount of work invested in this status, while allowing + /// to turn off all submodule status information. + Given { + /// The portion of the submodule status to ignore. + ignore: crate::submodule::config::Ignore, + /// If `true`, default `false`, the computation will stop once the first in a ladder operations + /// ordered from cheap to expensive shows that the submodule is dirty. + /// Thus, submodules that are clean will still impose the complete set of computation, as given. + check_dirty: bool, + }, +} + +/// How untracked files should be handled. +#[derive(Default, Copy, Clone, Debug, Ord, PartialOrd, Eq, PartialEq, Hash)] +pub enum UntrackedFiles { + /// Do not show any untracked files. + /// + /// This can mean no directory walk is performed. + None, + /// If possible, collapse files into their parent folders to reduce the amount of + /// emitted untracked files. + #[default] + Collapsed, + /// Show each individual untracked file or directory (if empty directories are emitted) that the dirwalk encountered . + Files, +} + +impl Default for Submodule { + fn default() -> Self { + Submodule::AsConfigured { check_dirty: false } + } +} + +/// The error returned by [status()](Repository::status). +#[derive(Debug, thiserror::Error)] +#[allow(missing_docs)] +pub enum Error { + #[error(transparent)] + DirwalkOptions(#[from] config::boolean::Error), + #[error(transparent)] + ConfigureUntrackedFiles(#[from] config::key::GenericErrorWithValue), +} + +/// Status +impl Repository { + /// Obtain a platform for configuring iterators for traversing git repository status information. + /// + /// By default, this is set to the fastest and most immediate way of obtaining a status, + /// which is most similar to + /// + /// `git status --ignored=no` + /// + /// which implies that submodule information is provided by default. + /// + /// Note that `status.showUntrackedFiles` is respected, which leads to untracked files being + /// collapsed by default. If that needs to be controlled, + /// [configure the directory walk explicitly](Platform::dirwalk_options) or more [implicitly](Platform::untracked_files). + /// + /// Pass `progress` to receive progress information on file modifications on this repository. + /// Use [`progress::Discard`](crate::progress::Discard) to discard all progress information. + /// + /// ### Deviation + /// + /// Whereas Git runs the index-modified check before the directory walk to set entries + /// as up-to-date to (potentially) safe some disk-access, we run both in parallel which + /// ultimately is much faster. + pub fn status

(&self, progress: P) -> Result, Error> + where + P: gix_features::progress::Progress + 'static, + { + let platform = Platform { + repo: self, + progress, + index: None, + submodules: Submodule::default(), + should_interrupt: None, + index_worktree_options: index_worktree::Options { + sorting: None, + dirwalk_options: Some(self.dirwalk_options()?), + rewrites: None, + thread_limit: None, + }, + }; + + let untracked = self + .config + .resolved + .string(config::tree::Status::SHOW_UNTRACKED_FILES) + .map(|value| { + config::tree::Status::SHOW_UNTRACKED_FILES + .try_into_show_untracked_files(value) + .with_lenient_default(self.config.lenient_config) + }) + .transpose()? + .unwrap_or_default(); + Ok(platform.untracked_files(untracked)) + } +} + +/// +#[allow(clippy::empty_docs)] +pub mod is_dirty { + use crate::Repository; + + /// The error returned by [Repository::is_dirty()]. + #[derive(Debug, thiserror::Error)] + #[allow(missing_docs)] + pub enum Error { + #[error(transparent)] + StatusPlatform(#[from] crate::status::Error), + #[error(transparent)] + CreateStatusIterator(#[from] crate::status::index_worktree::iter::Error), + } + + impl Repository { + /// Returns `true` if the repository is dirty. + /// This means it's changed in one of the following ways: + /// + /// * the index was changed in comparison to its working tree + /// * the working tree was changed in comparison to the index + /// * submodules are taken in consideration, along with their `ignore` and `isActive` configuration + /// + /// Note that *untracked files* do *not* affect this flag. + /// + /// ### Incomplete Implementation Warning + /// + /// Currently, this does not compute changes between the head and the index. + // TODO: use iterator which also tests for head->index changes. + pub fn is_dirty(&self) -> Result { + let is_dirty = self + .status(gix_features::progress::Discard)? + .index_worktree_rewrites(None) + .index_worktree_submodules(crate::status::Submodule::AsConfigured { check_dirty: true }) + .index_worktree_options_mut(|opts| { + opts.dirwalk_options = None; + }) + .into_index_worktree_iter(Vec::new())? + .take_while(Result::is_ok) + .next() + .is_some(); + Ok(is_dirty) + } + } +} + +mod platform; + +/// +#[allow(clippy::empty_docs)] +pub mod index_worktree; diff --git a/gix/src/status/platform.rs b/gix/src/status/platform.rs new file mode 100644 index 00000000000..2b73ae172a3 --- /dev/null +++ b/gix/src/status/platform.rs @@ -0,0 +1,110 @@ +use crate::status::{index_worktree, OwnedOrStaticAtomicBool, Platform, Submodule, UntrackedFiles}; +use std::sync::atomic::AtomicBool; + +/// Builder +impl<'repo, Progress> Platform<'repo, Progress> +where + Progress: gix_features::progress::Progress, +{ + /// Call `cb` on dirwalk options if these are set (which is the default when created through [`Repository::status()`](crate::Repository::status())). + /// The directory walk is used to find untracked files or ignored files. + /// + /// `cb` will be able to run builder-methods on the passed dirwalk options. + pub fn dirwalk_options(mut self, cb: impl FnOnce(crate::dirwalk::Options) -> crate::dirwalk::Options) -> Self { + if let Some(opts) = self.index_worktree_options.dirwalk_options.take() { + self.index_worktree_options.dirwalk_options = Some(cb(opts)); + } + self + } + /// Like [dirwalk_options()](Self::dirwalk_options), but taking a mutable instance instead. + pub fn dirwalk_options_mut(&mut self, cb: impl FnOnce(&mut crate::dirwalk::Options)) -> &mut Self { + if let Some(opts) = self.index_worktree_options.dirwalk_options.as_mut() { + cb(opts); + } + self + } + /// A simple way to explicitly set the desired way of listing `untracked_files`, overriding any value + /// set by the git configuration. + /// + /// Note that if [`None`](UntrackedFiles::None) is used, the directory walk will be disabled entirely + /// after this call. Further, if no dirwalk options are present anymore, this call has no effect. + pub fn untracked_files(mut self, untracked_files: UntrackedFiles) -> Self { + let mode = match untracked_files { + UntrackedFiles::None => { + self.index_worktree_options.dirwalk_options.take(); + return self; + } + UntrackedFiles::Collapsed => gix_dir::walk::EmissionMode::CollapseDirectory, + UntrackedFiles::Files => gix_dir::walk::EmissionMode::Matching, + }; + self.dirwalk_options(|cb| cb.emit_untracked(mode)) + } + + /// Set the interrupt flag to `should_interrupt`, which typically is an application-wide flag + /// that is ultimately controlled by user interrupts. + /// + /// If it is `true`, the iteration will stop immediately. + pub fn should_interrupt_shared(mut self, should_interrupt: &'static AtomicBool) -> Self { + self.should_interrupt = Some(OwnedOrStaticAtomicBool::Shared(should_interrupt)); + self + } + + /// Set the interrupt flag to `should_interrupt`, as controlled by the caller. + /// + /// If it is `true`, the iteration will stop immediately. + pub fn should_interrupt_owned(mut self, should_interrupt: std::sync::Arc) -> Self { + self.should_interrupt = Some(OwnedOrStaticAtomicBool::Owned { + flag: should_interrupt, + private: false, + }); + self + } + + /// Configure how the `submodule_status` is obtained when looking at submodules that are still mentioned in the index. + // If `None` is given, no submodule status check is performed. + pub fn index_worktree_submodules(mut self, submodules: impl Into>) -> Self { + let submodules = submodules.into(); + self.submodules = match submodules { + None => Submodule::Given { + ignore: crate::submodule::config::Ignore::All, + check_dirty: false, + }, + Some(status) => status, + }; + self + } + + /// Set the `index` to use when making comparisons to the worktree and the head revision. + /// + /// Defaults to the current index, or an empty one if it doesn't exist (yet). + pub fn index(mut self, index: crate::worktree::IndexPersistedOrInMemory) -> Self { + self.index = Some(index); + self + } + + /// Configure the index-to-worktree rename tracking with `rewrites`, which is `None` by default. + /// + /// Note that Git does not have configuration related to rename tracking of changes between the index + /// and the worktree. The closest there is can be obtained using [`crate::diff::new_rewrites()`], which refers + /// to rename tracking between trees. + /// + /// Also note that if `rewrites` are `Some()`, [`sorting`](index_worktree::Options::sorting) will automatically be + /// configured to assure deterministic outcomes for rewrite solutions. + pub fn index_worktree_rewrites(mut self, rewrites: impl Into>) -> Self { + let rewrites = rewrites.into(); + self.index_worktree_options.rewrites = rewrites; + if rewrites.is_some() && self.index_worktree_options.sorting.is_none() { + self.index_worktree_options.sorting = + Some(gix_status::index_as_worktree_with_renames::Sorting::ByPathCaseSensitive); + } + self + } + + /// Adjust all options related to the index-worktree status. + /// This is a catch-all in case there are no more specific methods that could be used instead to change + /// the respective option. + pub fn index_worktree_options_mut(mut self, cb: impl FnOnce(&mut index_worktree::Options)) -> Self { + cb(&mut self.index_worktree_options); + self + } +} diff --git a/gix/src/submodule/errors.rs b/gix/src/submodule/errors.rs index 4e41337de45..a27b02a7541 100644 --- a/gix/src/submodule/errors.rs +++ b/gix/src/submodule/errors.rs @@ -1,4 +1,5 @@ /// +#[allow(clippy::empty_docs)] pub mod open_modules_file { /// The error returned by [Repository::open_modules_file()](crate::Repository::open_modules_file()). #[derive(Debug, thiserror::Error)] @@ -12,6 +13,7 @@ pub mod open_modules_file { } /// +#[allow(clippy::empty_docs)] pub mod modules { /// The error returned by [Repository::modules()](crate::Repository::modules()). #[derive(Debug, thiserror::Error)] @@ -31,6 +33,7 @@ pub mod modules { } /// +#[allow(clippy::empty_docs)] pub mod is_active { /// The error returned by [Submodule::is_active()](crate::Submodule::is_active()). #[derive(Debug, thiserror::Error)] @@ -50,6 +53,7 @@ pub mod is_active { } /// +#[allow(clippy::empty_docs)] pub mod fetch_recurse { /// The error returned by [Submodule::fetch_recurse()](crate::Submodule::fetch_recurse()). #[derive(Debug, thiserror::Error)] @@ -63,6 +67,7 @@ pub mod fetch_recurse { } /// +#[allow(clippy::empty_docs)] pub mod open { /// The error returned by [Submodule::open()](crate::Submodule::open()). #[derive(Debug, thiserror::Error)] @@ -76,6 +81,7 @@ pub mod open { } /// +#[allow(clippy::empty_docs)] pub mod index_id { /// The error returned by [Submodule::index_id()](crate::Submodule::index_id()). #[derive(Debug, thiserror::Error)] @@ -89,6 +95,7 @@ pub mod index_id { } /// +#[allow(clippy::empty_docs)] pub mod head_id { /// The error returned by [Submodule::head_id()](crate::Submodule::head_id()). #[derive(Debug, thiserror::Error)] diff --git a/gix/src/submodule/mod.rs b/gix/src/submodule/mod.rs index fbc8a334039..5cdc6fc1be4 100644 --- a/gix/src/submodule/mod.rs +++ b/gix/src/submodule/mod.rs @@ -9,7 +9,7 @@ use std::{ pub use gix_submodule::*; -use crate::{bstr::BStr, repository::IndexPersistedOrInMemory, Repository, Submodule}; +use crate::{bstr::BStr, is_dir_to_mode, worktree::IndexPersistedOrInMemory, Repository, Submodule}; pub(crate) type ModulesFileStorage = gix_features::threading::OwnShared>; /// A lazily loaded and auto-updated worktree index. @@ -96,16 +96,22 @@ impl<'repo> Submodule<'repo> { } /// Return the url from which to clone or update the submodule. + /// + /// This method takes into consideration submodule configuration overrides. pub fn url(&self) -> Result { self.state.modules.url(self.name()) } /// Return the `update` field from this submodule's configuration, if present, or `None`. + /// + /// This method takes into consideration submodule configuration overrides. pub fn update(&self) -> Result, config::update::Error> { self.state.modules.update(self.name()) } /// Return the `branch` field from this submodule's configuration, if present, or `None`. + /// + /// This method takes into consideration submodule configuration overrides. pub fn branch(&self) -> Result, config::branch::Error> { self.state.modules.branch(self.name()) } @@ -126,6 +132,8 @@ impl<'repo> Submodule<'repo> { } /// Return the `ignore` field from this submodule's configuration, if present, or `None`. + /// + /// This method takes into consideration submodule configuration overrides. pub fn ignore(&self) -> Result, config::Error> { self.state.modules.ignore(self.name()) } @@ -146,7 +154,7 @@ impl<'repo> Submodule<'repo> { &mut |relative_path, case, is_dir, out| { attributes .set_case(case) - .at_entry(relative_path, Some(is_dir), &self.state.repo.objects) + .at_entry(relative_path, Some(is_dir_to_mode(is_dir)), &self.state.repo.objects) .map_or(false, |platform| platform.matching_attributes(out)) } })?; @@ -267,6 +275,183 @@ impl<'repo> Submodule<'repo> { } } +/// +#[allow(clippy::empty_docs)] +#[cfg(feature = "status")] +pub mod status { + use super::{head_id, index_id, open, Status}; + use crate::Submodule; + use gix_submodule::config; + + /// The error returned by [Submodule::status()]. + #[derive(Debug, thiserror::Error)] + #[allow(missing_docs)] + pub enum Error { + #[error(transparent)] + State(#[from] config::path::Error), + #[error(transparent)] + HeadId(#[from] head_id::Error), + #[error(transparent)] + IndexId(#[from] index_id::Error), + #[error(transparent)] + OpenRepository(#[from] open::Error), + #[error(transparent)] + IgnoreConfiguration(#[from] config::Error), + #[error(transparent)] + StatusPlatform(#[from] crate::status::Error), + #[error(transparent)] + Status(#[from] crate::status::index_worktree::iter::Error), + #[error(transparent)] + IndexWorktreeStatus(#[from] crate::status::index_worktree::Error), + } + + impl<'repo> Submodule<'repo> { + /// Return the status of the submodule. + /// + /// Use `ignore` to control the portion of the submodule status to ignore. It can be obtained from + /// submodule configuration using the [`ignore()`](Submodule::ignore()) method. + /// If `check_dirty` is `true`, the computation will stop once the first in a ladder operations + /// ordered from cheap to expensive shows that the submodule is dirty. + /// Thus, submodules that are clean will still impose the complete set of computation, as given. + #[doc(alias = "submodule_status", alias = "git2")] + pub fn status( + &self, + ignore: config::Ignore, + check_dirty: bool, + ) -> Result { + self.status_opts(ignore, check_dirty, &mut |s| s) + } + /// Return the status of the submodule, just like [`status`](Self::status), but allows to adjust options + /// for more control over how the status is performed. + /// + /// Use `&mut std::convert::identity` for `adjust_options` if no specific options are desired. + /// A reason to change them might be to enable sorting to enjoy deterministic order of changes. + /// + /// The status allows to easily determine if a submodule [has changes](Status::is_dirty). + /// + /// ### Incomplete Implementation Warning + /// + /// Currently, changes between the head and the index aren't computed. + // TODO: Run the full status, including tree->index once available. + #[doc(alias = "submodule_status", alias = "git2")] + pub fn status_opts( + &self, + ignore: config::Ignore, + check_dirty: bool, + adjust_options: &mut dyn for<'a> FnMut( + crate::status::Platform<'a, gix_features::progress::Discard>, + ) + -> crate::status::Platform<'a, gix_features::progress::Discard>, + ) -> Result { + let mut state = self.state()?; + if ignore == config::Ignore::All { + return Ok(Status { + state, + ..Default::default() + }); + } + + let index_id = self.index_id()?; + if !state.repository_exists { + return Ok(Status { + state, + index_id, + ..Default::default() + }); + } + let sm_repo = match self.open()? { + None => { + state.repository_exists = false; + return Ok(Status { + state, + index_id, + ..Default::default() + }); + } + Some(repo) => repo, + }; + + let checked_out_head_id = sm_repo.head_id().ok().map(crate::Id::detach); + let mut status = Status { + state, + index_id, + checked_out_head_id, + ..Default::default() + }; + if ignore == config::Ignore::Dirty || check_dirty && status.is_dirty() == Some(true) { + return Ok(status); + } + + if !state.worktree_checkout { + return Ok(status); + } + let statuses = adjust_options(sm_repo.status(gix_features::progress::Discard)?) + .index_worktree_options_mut(|opts| { + if ignore == config::Ignore::Untracked { + opts.dirwalk_options = None; + } + }) + .into_index_worktree_iter(Vec::new())?; + let mut changes = Vec::new(); + for change in statuses { + changes.push(change?); + } + status.changes = Some(changes); + Ok(status) + } + } + + impl Status { + /// Return `Some(true)` if the submodule status could be determined sufficiently and + /// if there are changes that would render this submodule dirty. + /// + /// Return `Some(false)` if the submodule status could be determined and it has no changes + /// at all. + /// + /// Return `None` if the repository clone or the worktree are missing entirely, which would leave + /// it to the caller to determine if that's considered dirty or not. + pub fn is_dirty(&self) -> Option { + if !self.state.worktree_checkout || !self.state.repository_exists { + return None; + } + let is_dirty = + self.checked_out_head_id != self.index_id || self.changes.as_ref().map_or(false, |c| !c.is_empty()); + Some(is_dirty) + } + } + + pub(super) mod types { + use crate::submodule::State; + + /// A simplified status of the Submodule. + /// + /// As opposed to the similar-sounding [`State`], it is more exhaustive and potentially expensive to compute, + /// particularly for submodules without changes. + /// + /// It's produced by [Submodule::status()](crate::Submodule::status()). + #[derive(Default, Clone, PartialEq, Debug)] + pub struct Status { + /// The cheapest part of the status that is always performed, to learn if the repository is cloned + /// and if there is a worktree checkout. + pub state: State, + /// The commit at which the submodule is supposed to be according to the super-project's index. + /// `None` means the computation wasn't performed, or the submodule didn't exist in the super-project's index anymore. + pub index_id: Option, + /// The commit-id of the `HEAD` at which the submodule is currently checked out. + /// `None` if the computation wasn't performed as it was skipped early, or if no repository was available or + /// if the HEAD could not be obtained or wasn't born. + pub checked_out_head_id: Option, + /// The set of changes obtained from running something akin to `git status` in the submodule working tree. + /// + /// `None` if the computation wasn't performed as the computation was skipped early, or if no working tree was + /// available or repository was available. + pub changes: Option>, + } + } +} +#[cfg(feature = "status")] +pub use status::types::Status; + /// A summary of the state of all parts forming a submodule, which allows to answer various questions about it. /// /// Note that expensive questions about its presence in the `HEAD` or the `index` are left to the caller. diff --git a/gix/src/tag.rs b/gix/src/tag.rs index 84af3b43a7c..b22af2334cf 100644 --- a/gix/src/tag.rs +++ b/gix/src/tag.rs @@ -1,4 +1,5 @@ //! +#![allow(clippy::empty_docs)] mod error { /// The error returned by [`tag(…)`][crate::Repository::tag()]. diff --git a/gix/src/util.rs b/gix/src/util.rs new file mode 100644 index 00000000000..e9d1e10f8e9 --- /dev/null +++ b/gix/src/util.rs @@ -0,0 +1,81 @@ +use std::ops::Deref; +use std::sync::atomic::AtomicBool; +use std::sync::Arc; + +#[derive(Clone)] +pub enum OwnedOrStaticAtomicBool { + Owned { + flag: Arc, + #[cfg_attr(not(feature = "parallel"), allow(dead_code))] + private: bool, + }, + Shared(&'static AtomicBool), +} + +impl Default for OwnedOrStaticAtomicBool { + fn default() -> Self { + OwnedOrStaticAtomicBool::Owned { + flag: Arc::new(AtomicBool::default()), + private: true, + } + } +} + +impl Deref for OwnedOrStaticAtomicBool { + type Target = std::sync::atomic::AtomicBool; + + fn deref(&self) -> &Self::Target { + match self { + OwnedOrStaticAtomicBool::Owned { flag, .. } => flag, + OwnedOrStaticAtomicBool::Shared(flag) => flag, + } + } +} + +impl From<&'static AtomicBool> for OwnedOrStaticAtomicBool { + fn from(value: &'static AtomicBool) -> Self { + OwnedOrStaticAtomicBool::Shared(value) + } +} + +impl<'a> From<&'a Arc> for OwnedOrStaticAtomicBool { + fn from(value: &'a Arc) -> Self { + OwnedOrStaticAtomicBool::Owned { + flag: value.clone(), + private: false, + } + } +} + +impl From> for OwnedOrStaticAtomicBool { + fn from(flag: Arc) -> Self { + OwnedOrStaticAtomicBool::Owned { flag, private: false } + } +} +#[cfg(feature = "parallel")] +pub fn parallel_iter_drop( + mut rx_and_join: Option<(std::sync::mpsc::Receiver, std::thread::JoinHandle)>, + should_interrupt: &OwnedOrStaticAtomicBool, +) { + let Some((rx, handle)) = rx_and_join.take() else { + return; + }; + let prev = should_interrupt.swap(true, std::sync::atomic::Ordering::Relaxed); + let undo = match &should_interrupt { + OwnedOrStaticAtomicBool::Shared(flag) => *flag, + OwnedOrStaticAtomicBool::Owned { flag, private: false } => flag.as_ref(), + OwnedOrStaticAtomicBool::Owned { private: true, .. } => { + // Leak the handle to let it shut down in the background, so drop returns more quickly. + drop((rx, handle)); + return; + } + }; + // Wait until there is time to respond before we undo the change. + handle.join().ok(); + undo.fetch_update( + std::sync::atomic::Ordering::SeqCst, + std::sync::atomic::Ordering::SeqCst, + |current| current.then_some(prev), + ) + .ok(); +} diff --git a/gix/src/worktree/mod.rs b/gix/src/worktree/mod.rs index 20f500a61c4..dee5401e61f 100644 --- a/gix/src/worktree/mod.rs +++ b/gix/src/worktree/mod.rs @@ -20,6 +20,31 @@ pub(crate) type IndexStorage = gix_features::threading::OwnShared; +/// A type to represent an index which either was loaded from disk as it was persisted there, or created on the fly in memory. +#[cfg(feature = "index")] +pub enum IndexPersistedOrInMemory { + /// The index as loaded from disk, and shared across clones of the owning `Repository`. + Persisted(Index), + /// A temporary index as created from the `HEAD^{tree}`, with the file path set to the place where it would be stored naturally. + /// + /// Note that unless saved explicitly, it will not persist. + InMemory(gix_index::File), +} + +#[cfg(feature = "index")] +impl From for IndexPersistedOrInMemory { + fn from(value: Index) -> Self { + IndexPersistedOrInMemory::Persisted(value) + } +} + +#[cfg(feature = "index")] +impl From for IndexPersistedOrInMemory { + fn from(value: gix_index::File) -> Self { + IndexPersistedOrInMemory::InMemory(value) + } +} + /// A stand-in to a worktree as result of a worktree iteration. /// /// It provides access to typical worktree state, but may not actually point to a valid checkout as the latter has been moved or @@ -83,6 +108,7 @@ pub(crate) fn id(git_dir: &std::path::Path, has_common_dir: bool) -> Option<&BSt } /// +#[allow(clippy::empty_docs)] pub mod proxy; /// diff --git a/gix/tests/clone/mod.rs b/gix/tests/clone/mod.rs index e35b71e5dbe..3c3c4741d77 100644 --- a/gix/tests/clone/mod.rs +++ b/gix/tests/clone/mod.rs @@ -1,7 +1,8 @@ use crate::{remote, util::restricted}; -#[cfg(feature = "blocking-network-client")] +#[cfg(all(feature = "worktree-mutation", feature = "blocking-network-client"))] mod blocking_io { + use std::path::Path; use std::{borrow::Cow, sync::atomic::AtomicBool}; use gix::{ @@ -500,12 +501,128 @@ mod blocking_io { let index = repo.index()?; assert_eq!(index.entries().len(), 1, "All entries are known as per HEAD tree"); - let work_dir = repo.work_dir().expect("non-bare"); + assure_index_entries_on_disk(&index, repo.work_dir().expect("non-bare")); + Ok(()) + } + #[test] + fn fetch_and_checkout_specific_ref() -> crate::Result { + let tmp = gix_testtools::tempfile::TempDir::new()?; + let remote_repo = remote::repo("base"); + let ref_to_checkout = "a"; + let mut prepare = gix::clone::PrepareFetch::new( + remote_repo.path(), + tmp.path(), + gix::create::Kind::WithWorktree, + Default::default(), + restricted(), + )? + .with_ref_name(Some(ref_to_checkout))?; + let (mut checkout, _out) = + prepare.fetch_then_checkout(gix::progress::Discard, &std::sync::atomic::AtomicBool::default())?; + + let (repo, _) = checkout.main_worktree(gix::progress::Discard, &std::sync::atomic::AtomicBool::default())?; + + assert_eq!( + repo.references()?.all()?.count() - 2, + remote_repo.references()?.all()?.count(), + "all references have been cloned, + remote HEAD + remote main (not listed in remote_repo)" + ); + let checked_out_ref = repo.head_ref()?.expect("head points to ref"); + let remote_ref_name = format!("refs/heads/{ref_to_checkout}"); + assert_eq!( + checked_out_ref.name().as_bstr(), + remote_ref_name, + "it's possible to checkout anything with that name, but here we have an ordinary branch" + ); + + assert_eq!( + checked_out_ref + .remote_ref_name(gix::remote::Direction::Fetch) + .transpose()? + .unwrap() + .as_bstr(), + remote_ref_name, + "the merge configuration is using the given name" + ); + + let index = repo.index()?; + assert_eq!(index.entries().len(), 1, "All entries are known as per HEAD tree"); + + assure_index_entries_on_disk(&index, repo.work_dir().expect("non-bare")); + Ok(()) + } + + #[test] + fn fetch_and_checkout_specific_non_existing() -> crate::Result { + let tmp = gix_testtools::tempfile::TempDir::new()?; + let remote_repo = remote::repo("base"); + let ref_to_checkout = "does-not-exist"; + let mut prepare = gix::clone::PrepareFetch::new( + remote_repo.path(), + tmp.path(), + gix::create::Kind::WithWorktree, + Default::default(), + restricted(), + )? + .with_ref_name(Some(ref_to_checkout))?; + + let err = prepare + .fetch_then_checkout(gix::progress::Discard, &std::sync::atomic::AtomicBool::default()) + .unwrap_err(); + assert_eq!( + err.to_string(), + "The remote didn't have any ref that matched 'does-not-exist'", + "we don't test this, but it's important that it determines this before receiving a pack" + ); + Ok(()) + } + + #[test] + fn fetch_and_checkout_specific_annotated_tag() -> crate::Result { + let tmp = gix_testtools::tempfile::TempDir::new()?; + let remote_repo = remote::repo("base"); + let ref_to_checkout = "annotated-detached-tag"; + let mut prepare = gix::clone::PrepareFetch::new( + remote_repo.path(), + tmp.path(), + gix::create::Kind::WithWorktree, + Default::default(), + restricted(), + )? + .with_ref_name(Some(ref_to_checkout))?; + let (mut checkout, _out) = + prepare.fetch_then_checkout(gix::progress::Discard, &std::sync::atomic::AtomicBool::default())?; + + let (repo, _) = checkout.main_worktree(gix::progress::Discard, &std::sync::atomic::AtomicBool::default())?; + + assert_eq!( + repo.references()?.all()?.count() - 1, + remote_repo.references()?.all()?.count(), + "all references have been cloned, + remote HEAD (not listed in remote_repo)" + ); + let checked_out_ref = repo.head_ref()?.expect("head points to ref"); + let remote_ref_name = format!("refs/tags/{ref_to_checkout}"); + assert_eq!( + checked_out_ref.name().as_bstr(), + remote_ref_name, + "it also works with tags" + ); + + assert_eq!( + checked_out_ref + .remote_ref_name(gix::remote::Direction::Fetch) + .transpose()?, + None, + "there is no merge configuration for tags" + ); + Ok(()) + } + + fn assure_index_entries_on_disk(index: &gix::worktree::Index, work_dir: &Path) { for entry in index.entries() { - let entry_path = work_dir.join(gix_path::from_bstr(entry.path(&index))); + let entry_path = work_dir.join(gix_path::from_bstr(entry.path(index))); assert!(entry_path.is_file(), "{entry_path:?} not found on disk") } - Ok(()) } #[test] diff --git a/gix/tests/commit/mod.rs b/gix/tests/commit/mod.rs index fd2440bf8ce..3daf74852db 100644 --- a/gix/tests/commit/mod.rs +++ b/gix/tests/commit/mod.rs @@ -4,6 +4,48 @@ mod describe { use crate::named_repo; + #[cfg(feature = "status")] + mod with_dirty_suffix { + use crate::util::named_subrepo_opts; + use gix::commit::describe::SelectRef; + + #[test] + fn dirty_suffix_applies_automatically_if_dirty() -> crate::Result { + let repo = named_subrepo_opts( + "make_submodules.sh", + "submodule-head-changed", + gix::open::Options::isolated(), + )?; + + let actual = repo + .head_commit()? + .describe() + .names(SelectRef::AllRefs) + .try_resolve()? + .expect("resolution") + .format_with_dirty_suffix("dirty".to_owned())? + .to_string(); + assert_eq!(actual, "main-dirty"); + Ok(()) + } + + #[test] + fn dirty_suffix_does_not_apply_if_not_dirty() -> crate::Result { + let repo = named_subrepo_opts("make_submodules.sh", "module1", gix::open::Options::isolated())?; + + let actual = repo + .head_commit()? + .describe() + .names(SelectRef::AllRefs) + .try_resolve()? + .expect("resolution") + .format_with_dirty_suffix("dirty".to_owned())? + .to_string(); + assert_eq!(actual, "main"); + Ok(()) + } + } + #[test] fn tags_are_sorted_by_date_and_lexicographically() -> crate::Result { let repo = named_repo("make_commit_describe_multiple_tags.sh")?; diff --git a/gix/tests/config/tree.rs b/gix/tests/config/tree.rs index e8ab78d3643..8305fb20698 100644 --- a/gix/tests/config/tree.rs +++ b/gix/tests/config/tree.rs @@ -145,6 +145,37 @@ mod ssh { } } +#[cfg(feature = "status")] +mod status { + use crate::config::tree::bcow; + use gix::config::tree::Status; + use gix::status::UntrackedFiles; + + #[test] + fn default() -> crate::Result { + for (actual, expected) in [ + ("no", UntrackedFiles::None), + ("normal", UntrackedFiles::Collapsed), + ("all", UntrackedFiles::Files), + ] { + assert_eq!( + Status::SHOW_UNTRACKED_FILES.try_into_show_untracked_files(bcow(actual))?, + expected + ); + } + + assert_eq!( + Status::SHOW_UNTRACKED_FILES + .try_into_show_untracked_files(bcow("NO")) + .unwrap_err() + .to_string(), + "The key \"status.showUntrackedFiles=NO\" was invalid", + "case-sensitive comparisons" + ); + Ok(()) + } +} + mod push { use crate::config::tree::bcow; use gix::config::tree::Push; diff --git a/gix/tests/diff/mod.rs b/gix/tests/diff/mod.rs index 2fa681ae981..2b9fa79401b 100644 --- a/gix/tests/diff/mod.rs +++ b/gix/tests/diff/mod.rs @@ -5,11 +5,12 @@ use crate::util::named_repo; #[test] fn resource_cache() -> crate::Result { let repo = named_repo("make_diff_repo.sh")?; + let index = repo.index()?; let cache = gix::diff::resource_cache( &repo, - &*repo.index()?, gix::diff::blob::pipeline::Mode::ToWorktreeAndBinaryToText, - gix_worktree::stack::state::attributes::Source::IdMapping, + repo.attributes_only(&index, gix_worktree::stack::state::attributes::Source::IdMapping)? + .detach(), Default::default(), )?; assert_eq!( diff --git a/gix/tests/fixtures/generated-archives/make_am_repo.tar.xz b/gix/tests/fixtures/generated-archives/make_am_repo.tar.xz index 6e925a2d80e..bd553de8cf3 100644 Binary files a/gix/tests/fixtures/generated-archives/make_am_repo.tar.xz and b/gix/tests/fixtures/generated-archives/make_am_repo.tar.xz differ diff --git a/gix/tests/fixtures/generated-archives/make_basic_repo.tar.xz b/gix/tests/fixtures/generated-archives/make_basic_repo.tar.xz index dc37073d9cd..c235a270b22 100644 Binary files a/gix/tests/fixtures/generated-archives/make_basic_repo.tar.xz and b/gix/tests/fixtures/generated-archives/make_basic_repo.tar.xz differ diff --git a/gix/tests/fixtures/generated-archives/make_bisect_repo.tar.xz b/gix/tests/fixtures/generated-archives/make_bisect_repo.tar.xz index 1c2f2e1d0db..3cdac406f9a 100644 Binary files a/gix/tests/fixtures/generated-archives/make_bisect_repo.tar.xz and b/gix/tests/fixtures/generated-archives/make_bisect_repo.tar.xz differ diff --git a/gix/tests/fixtures/generated-archives/make_cherry_pick_repo.tar.xz b/gix/tests/fixtures/generated-archives/make_cherry_pick_repo.tar.xz index f7837f48157..3d6c9da34ce 100644 Binary files a/gix/tests/fixtures/generated-archives/make_cherry_pick_repo.tar.xz and b/gix/tests/fixtures/generated-archives/make_cherry_pick_repo.tar.xz differ diff --git a/gix/tests/fixtures/generated-archives/make_cherry_pick_sequence_repo.tar.xz b/gix/tests/fixtures/generated-archives/make_cherry_pick_sequence_repo.tar.xz index 149d9a2686f..0169ff12b6e 100644 Binary files a/gix/tests/fixtures/generated-archives/make_cherry_pick_sequence_repo.tar.xz and b/gix/tests/fixtures/generated-archives/make_cherry_pick_sequence_repo.tar.xz differ diff --git a/gix/tests/fixtures/generated-archives/make_commit_describe_multiple_tags.tar.xz b/gix/tests/fixtures/generated-archives/make_commit_describe_multiple_tags.tar.xz index 6505312fed5..f71e9bda776 100644 Binary files a/gix/tests/fixtures/generated-archives/make_commit_describe_multiple_tags.tar.xz and b/gix/tests/fixtures/generated-archives/make_commit_describe_multiple_tags.tar.xz differ diff --git a/gix/tests/fixtures/generated-archives/make_config_repo.tar.xz b/gix/tests/fixtures/generated-archives/make_config_repo.tar.xz index f9323bf12f5..4d0d9445652 100644 Binary files a/gix/tests/fixtures/generated-archives/make_config_repo.tar.xz and b/gix/tests/fixtures/generated-archives/make_config_repo.tar.xz differ diff --git a/gix/tests/fixtures/generated-archives/make_config_repos.tar.xz b/gix/tests/fixtures/generated-archives/make_config_repos.tar.xz index bc4e0c76ddf..5922f04c591 100644 Binary files a/gix/tests/fixtures/generated-archives/make_config_repos.tar.xz and b/gix/tests/fixtures/generated-archives/make_config_repos.tar.xz differ diff --git a/gix/tests/fixtures/generated-archives/make_diff_repo.tar.xz b/gix/tests/fixtures/generated-archives/make_diff_repo.tar.xz index 84ebf7da0e0..7f86701b50a 100644 Binary files a/gix/tests/fixtures/generated-archives/make_diff_repo.tar.xz and b/gix/tests/fixtures/generated-archives/make_diff_repo.tar.xz differ diff --git a/gix/tests/fixtures/generated-archives/make_empty_repo.tar.xz b/gix/tests/fixtures/generated-archives/make_empty_repo.tar.xz index 22cf73d603d..a189b77505b 100644 Binary files a/gix/tests/fixtures/generated-archives/make_empty_repo.tar.xz and b/gix/tests/fixtures/generated-archives/make_empty_repo.tar.xz differ diff --git a/gix/tests/fixtures/generated-archives/make_head_repos.tar.xz b/gix/tests/fixtures/generated-archives/make_head_repos.tar.xz index f11682ead6d..b9aa30f5dc8 100644 Binary files a/gix/tests/fixtures/generated-archives/make_head_repos.tar.xz and b/gix/tests/fixtures/generated-archives/make_head_repos.tar.xz differ diff --git a/gix/tests/fixtures/generated-archives/make_merge_repo.tar.xz b/gix/tests/fixtures/generated-archives/make_merge_repo.tar.xz index 9decda4547f..2a807cd9d64 100644 Binary files a/gix/tests/fixtures/generated-archives/make_merge_repo.tar.xz and b/gix/tests/fixtures/generated-archives/make_merge_repo.tar.xz differ diff --git a/gix/tests/fixtures/generated-archives/make_packed_and_loose.tar.xz b/gix/tests/fixtures/generated-archives/make_packed_and_loose.tar.xz index 24e6f4561fd..06a86d86e78 100644 Binary files a/gix/tests/fixtures/generated-archives/make_packed_and_loose.tar.xz and b/gix/tests/fixtures/generated-archives/make_packed_and_loose.tar.xz differ diff --git a/gix/tests/fixtures/generated-archives/make_pre_epoch_repo.tar.xz b/gix/tests/fixtures/generated-archives/make_pre_epoch_repo.tar.xz index c0dcc03f935..0090f6a1544 100644 Binary files a/gix/tests/fixtures/generated-archives/make_pre_epoch_repo.tar.xz and b/gix/tests/fixtures/generated-archives/make_pre_epoch_repo.tar.xz differ diff --git a/gix/tests/fixtures/generated-archives/make_rebase_i_repo.tar.xz b/gix/tests/fixtures/generated-archives/make_rebase_i_repo.tar.xz index 426cb1ef74b..17c9b3100cc 100644 Binary files a/gix/tests/fixtures/generated-archives/make_rebase_i_repo.tar.xz and b/gix/tests/fixtures/generated-archives/make_rebase_i_repo.tar.xz differ diff --git a/gix/tests/fixtures/generated-archives/make_references_repo.tar.xz b/gix/tests/fixtures/generated-archives/make_references_repo.tar.xz index 9712e27bf13..b6b0a26f118 100644 Binary files a/gix/tests/fixtures/generated-archives/make_references_repo.tar.xz and b/gix/tests/fixtures/generated-archives/make_references_repo.tar.xz differ diff --git a/gix/tests/fixtures/generated-archives/make_remote_config_repos.tar.xz b/gix/tests/fixtures/generated-archives/make_remote_config_repos.tar.xz index d822d7c5be5..019cbbdcd02 100644 Binary files a/gix/tests/fixtures/generated-archives/make_remote_config_repos.tar.xz and b/gix/tests/fixtures/generated-archives/make_remote_config_repos.tar.xz differ diff --git a/gix/tests/fixtures/generated-archives/make_repo_with_fork_and_dates.tar.xz b/gix/tests/fixtures/generated-archives/make_repo_with_fork_and_dates.tar.xz index 9d9f4ebb758..68862b8c1cc 100644 Binary files a/gix/tests/fixtures/generated-archives/make_repo_with_fork_and_dates.tar.xz and b/gix/tests/fixtures/generated-archives/make_repo_with_fork_and_dates.tar.xz differ diff --git a/gix/tests/fixtures/generated-archives/make_rev_spec_parse_repos.tar.xz b/gix/tests/fixtures/generated-archives/make_rev_spec_parse_repos.tar.xz index 312f50fef55..dd1e1da550b 100644 Binary files a/gix/tests/fixtures/generated-archives/make_rev_spec_parse_repos.tar.xz and b/gix/tests/fixtures/generated-archives/make_rev_spec_parse_repos.tar.xz differ diff --git a/gix/tests/fixtures/generated-archives/make_revert_repo.tar.xz b/gix/tests/fixtures/generated-archives/make_revert_repo.tar.xz index 736f7355bdb..8a84d263f70 100644 Binary files a/gix/tests/fixtures/generated-archives/make_revert_repo.tar.xz and b/gix/tests/fixtures/generated-archives/make_revert_repo.tar.xz differ diff --git a/gix/tests/fixtures/generated-archives/make_revert_sequence_repo.tar.xz b/gix/tests/fixtures/generated-archives/make_revert_sequence_repo.tar.xz index 64972d2a4d4..47049051a58 100644 Binary files a/gix/tests/fixtures/generated-archives/make_revert_sequence_repo.tar.xz and b/gix/tests/fixtures/generated-archives/make_revert_sequence_repo.tar.xz differ diff --git a/gix/tests/fixtures/generated-archives/make_shallow_repo.tar.xz b/gix/tests/fixtures/generated-archives/make_shallow_repo.tar.xz index 008a81beed7..6433d0d654a 100644 Binary files a/gix/tests/fixtures/generated-archives/make_shallow_repo.tar.xz and b/gix/tests/fixtures/generated-archives/make_shallow_repo.tar.xz differ diff --git a/gix/tests/fixtures/generated-archives/make_status_repos.tar.xz b/gix/tests/fixtures/generated-archives/make_status_repos.tar.xz new file mode 100644 index 00000000000..3724b9a20c9 Binary files /dev/null and b/gix/tests/fixtures/generated-archives/make_status_repos.tar.xz differ diff --git a/gix/tests/fixtures/generated-archives/make_submodules.tar.xz b/gix/tests/fixtures/generated-archives/make_submodules.tar.xz index 18cca667581..caead9bfe3d 100644 Binary files a/gix/tests/fixtures/generated-archives/make_submodules.tar.xz and b/gix/tests/fixtures/generated-archives/make_submodules.tar.xz differ diff --git a/gix/tests/fixtures/make_am_repo.sh b/gix/tests/fixtures/make_am_repo.sh index 23fc5a6ae94..3a8b4814582 100755 --- a/gix/tests/fixtures/make_am_repo.sh +++ b/gix/tests/fixtures/make_am_repo.sh @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash set -eu -o pipefail git init -q diff --git a/gix/tests/fixtures/make_basic_repo.sh b/gix/tests/fixtures/make_basic_repo.sh index ac50aea64cb..45defa67648 100755 --- a/gix/tests/fixtures/make_basic_repo.sh +++ b/gix/tests/fixtures/make_basic_repo.sh @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash set -eu -o pipefail git init -q @@ -31,4 +31,9 @@ git init all-untracked >a mkdir d >d/a +) + +git init empty-core-excludes +(cd empty-core-excludes + echo $'[core]\n\texcludesFile = ' >> .git/config ) \ No newline at end of file diff --git a/gix/tests/fixtures/make_bisect_repo.sh b/gix/tests/fixtures/make_bisect_repo.sh index ce67e12aac4..ab43452a7d2 100755 --- a/gix/tests/fixtures/make_bisect_repo.sh +++ b/gix/tests/fixtures/make_bisect_repo.sh @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash set -eu -o pipefail git init -q diff --git a/gix/tests/fixtures/make_cherry_pick_repo.sh b/gix/tests/fixtures/make_cherry_pick_repo.sh index 5f8575bb0cb..32321fef066 100755 --- a/gix/tests/fixtures/make_cherry_pick_repo.sh +++ b/gix/tests/fixtures/make_cherry_pick_repo.sh @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash set -eu -o pipefail git init -q diff --git a/gix/tests/fixtures/make_cherry_pick_sequence_repo.sh b/gix/tests/fixtures/make_cherry_pick_sequence_repo.sh index d7295a3cd73..77dcd06ed67 100755 --- a/gix/tests/fixtures/make_cherry_pick_sequence_repo.sh +++ b/gix/tests/fixtures/make_cherry_pick_sequence_repo.sh @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash set -eu -o pipefail git init -q diff --git a/gix/tests/fixtures/make_commit_describe_multiple_tags.sh b/gix/tests/fixtures/make_commit_describe_multiple_tags.sh index db27f0acb8b..2a73b4a3d67 100755 --- a/gix/tests/fixtures/make_commit_describe_multiple_tags.sh +++ b/gix/tests/fixtures/make_commit_describe_multiple_tags.sh @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash set -eu -o pipefail git init -q diff --git a/gix/tests/fixtures/make_complex_shallow_repo.sh b/gix/tests/fixtures/make_complex_shallow_repo.sh index 739b1bed75f..00cab5533ad 100755 --- a/gix/tests/fixtures/make_complex_shallow_repo.sh +++ b/gix/tests/fixtures/make_complex_shallow_repo.sh @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash set -eu -o pipefail remote="${1:?First argument is the complex repo to clone from}" diff --git a/gix/tests/fixtures/make_config_repo.sh b/gix/tests/fixtures/make_config_repo.sh index 76b52b80484..3cd6cb85fea 100755 --- a/gix/tests/fixtures/make_config_repo.sh +++ b/gix/tests/fixtures/make_config_repo.sh @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash set -eu -o pipefail git init -q diff --git a/gix/tests/fixtures/make_config_repos.sh b/gix/tests/fixtures/make_config_repos.sh index 3219eb32a1c..be8b3f75706 100755 --- a/gix/tests/fixtures/make_config_repos.sh +++ b/gix/tests/fixtures/make_config_repos.sh @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash set -eu -o pipefail git init http-config diff --git a/gix/tests/fixtures/make_core_worktree_repo.sh b/gix/tests/fixtures/make_core_worktree_repo.sh index 4e7beb74b59..775265d2ec6 100755 --- a/gix/tests/fixtures/make_core_worktree_repo.sh +++ b/gix/tests/fixtures/make_core_worktree_repo.sh @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash set -eu -o pipefail mkdir worktree diff --git a/gix/tests/fixtures/make_diff_repo.sh b/gix/tests/fixtures/make_diff_repo.sh index 1175cf4ccd9..0fe6d560fb0 100755 --- a/gix/tests/fixtures/make_diff_repo.sh +++ b/gix/tests/fixtures/make_diff_repo.sh @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash set -eu -o pipefail git init -q diff --git a/gix/tests/fixtures/make_empty_repo.sh b/gix/tests/fixtures/make_empty_repo.sh index 2ca9da4d632..4cb30aa5a3e 100755 --- a/gix/tests/fixtures/make_empty_repo.sh +++ b/gix/tests/fixtures/make_empty_repo.sh @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash set -eu -o pipefail git -c init.defaultBranch=special init -q diff --git a/gix/tests/fixtures/make_fetch_repos.sh b/gix/tests/fixtures/make_fetch_repos.sh index 99c9acf0749..11b2d84e8e0 100755 --- a/gix/tests/fixtures/make_fetch_repos.sh +++ b/gix/tests/fixtures/make_fetch_repos.sh @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash set -eu -o pipefail # IMPORTANT: keep this repo small as it's used for writes, hence will be executed for each writer! diff --git a/gix/tests/fixtures/make_head_repos.sh b/gix/tests/fixtures/make_head_repos.sh old mode 100644 new mode 100755 index dcdc3277aef..736059a28a1 --- a/gix/tests/fixtures/make_head_repos.sh +++ b/gix/tests/fixtures/make_head_repos.sh @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash set -eu -o pipefail (git init symbolic && cd symbolic @@ -27,4 +27,3 @@ git clone tag-symbolic tag-detached git rev-parse point-at-tag-start > .git/HEAD.tmp mv .git/HEAD.tmp .git/HEAD ) - diff --git a/gix/tests/fixtures/make_merge_repo.sh b/gix/tests/fixtures/make_merge_repo.sh index 837a513301c..d726985d2c2 100755 --- a/gix/tests/fixtures/make_merge_repo.sh +++ b/gix/tests/fixtures/make_merge_repo.sh @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash set -eu -o pipefail git init -q diff --git a/gix/tests/fixtures/make_packed_and_loose.sh b/gix/tests/fixtures/make_packed_and_loose.sh index 180cd79c27a..1601359596c 100755 --- a/gix/tests/fixtures/make_packed_and_loose.sh +++ b/gix/tests/fixtures/make_packed_and_loose.sh @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash set -eu -o pipefail git init -q diff --git a/gix/tests/fixtures/make_pre_epoch_repo.sh b/gix/tests/fixtures/make_pre_epoch_repo.sh index 4ce7bda6395..2dce6a14377 100755 --- a/gix/tests/fixtures/make_pre_epoch_repo.sh +++ b/gix/tests/fixtures/make_pre_epoch_repo.sh @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash set -eu -o pipefail git init -q diff --git a/gix/tests/fixtures/make_rebase_i_repo.sh b/gix/tests/fixtures/make_rebase_i_repo.sh index a9ef1db1db4..b1959e7d65a 100755 --- a/gix/tests/fixtures/make_rebase_i_repo.sh +++ b/gix/tests/fixtures/make_rebase_i_repo.sh @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash set -eu -o pipefail git init -q diff --git a/gix/tests/fixtures/make_references_repo.sh b/gix/tests/fixtures/make_references_repo.sh index 3a4077b7af8..daea997523b 100755 --- a/gix/tests/fixtures/make_references_repo.sh +++ b/gix/tests/fixtures/make_references_repo.sh @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash set -eu -o pipefail git init -q diff --git a/gix/tests/fixtures/make_remote_config_repos.sh b/gix/tests/fixtures/make_remote_config_repos.sh index d767cf4f365..0982c21f47a 100755 --- a/gix/tests/fixtures/make_remote_config_repos.sh +++ b/gix/tests/fixtures/make_remote_config_repos.sh @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash set -eu -o pipefail (mkdir fetch && cd fetch diff --git a/gix/tests/fixtures/make_remote_repos.sh b/gix/tests/fixtures/make_remote_repos.sh index 69d105becc5..ab2c09111f6 100755 --- a/gix/tests/fixtures/make_remote_repos.sh +++ b/gix/tests/fixtures/make_remote_repos.sh @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash set -eu -o pipefail function tick () { @@ -22,7 +22,7 @@ GIT_COMMITTER_EMAIL=committer@example.com GIT_COMMITTER_NAME='C O Mitter' GIT_COMMITTER_DATE='1112354055 +0200' -# runup to the correct count for ambigous commits +# runup to the correct count for ambiguous commits tick; tick; tick; tick; tick git init base diff --git a/gix/tests/fixtures/make_repo_with_fork_and_dates.sh b/gix/tests/fixtures/make_repo_with_fork_and_dates.sh index 8f841dca8f2..82af9fe5e3b 100755 --- a/gix/tests/fixtures/make_repo_with_fork_and_dates.sh +++ b/gix/tests/fixtures/make_repo_with_fork_and_dates.sh @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash set -eu -o pipefail git init -q diff --git a/gix/tests/fixtures/make_rev_spec_parse_repos.sh b/gix/tests/fixtures/make_rev_spec_parse_repos.sh index 414b805df89..971417aa75d 100755 --- a/gix/tests/fixtures/make_rev_spec_parse_repos.sh +++ b/gix/tests/fixtures/make_rev_spec_parse_repos.sh @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash set -eu -o pipefail diff --git a/gix/tests/fixtures/make_revert_repo.sh b/gix/tests/fixtures/make_revert_repo.sh index 9e4daf3e2cd..0e1477e94e1 100755 --- a/gix/tests/fixtures/make_revert_repo.sh +++ b/gix/tests/fixtures/make_revert_repo.sh @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash set -eu -o pipefail git init -q diff --git a/gix/tests/fixtures/make_revert_sequence_repo.sh b/gix/tests/fixtures/make_revert_sequence_repo.sh index 37f182deac7..2130e012352 100755 --- a/gix/tests/fixtures/make_revert_sequence_repo.sh +++ b/gix/tests/fixtures/make_revert_sequence_repo.sh @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash set -eu -o pipefail git init -q diff --git a/gix/tests/fixtures/make_shallow_repo.sh b/gix/tests/fixtures/make_shallow_repo.sh index 185b2f490b6..099ad0434ab 100755 --- a/gix/tests/fixtures/make_shallow_repo.sh +++ b/gix/tests/fixtures/make_shallow_repo.sh @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash set -eu -o pipefail mkdir base diff --git a/gix/tests/fixtures/make_signatures_repo.sh b/gix/tests/fixtures/make_signatures_repo.sh index e90021ddeb4..71799a6df67 100755 --- a/gix/tests/fixtures/make_signatures_repo.sh +++ b/gix/tests/fixtures/make_signatures_repo.sh @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash set -eu -o pipefail git init -q diff --git a/gix/tests/fixtures/make_status_repos.sh b/gix/tests/fixtures/make_status_repos.sh new file mode 100755 index 00000000000..63c1cfb1d48 --- /dev/null +++ b/gix/tests/fixtures/make_status_repos.sh @@ -0,0 +1,15 @@ +#!/usr/bin/env bash +set -eu -o pipefail + +git init -q untracked-only +(cd untracked-only + touch this + mkdir subdir + >subdir/that + + git add . + git commit -q -m init + + mkdir new + touch new/untracked subdir/untracked +) diff --git a/gix/tests/fixtures/make_submodules.sh b/gix/tests/fixtures/make_submodules.sh index af5b9c96786..be7fcd48887 100755 --- a/gix/tests/fixtures/make_submodules.sh +++ b/gix/tests/fixtures/make_submodules.sh @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash set -eu -o pipefail git init -q module1 @@ -10,6 +10,62 @@ git init -q module1 git commit -q -m c1 echo hello >> this git commit -q -am c2 + touch untracked +) + +git init submodule-head-changed +(cd submodule-head-changed + git submodule add ../module1 m1 + git commit -m "add submodule" + + cd m1 && git checkout @~1 +) + +git init submodule-head-changed-no-worktree +(cd submodule-head-changed-no-worktree + git submodule add ../module1 m1 + git commit -m "add submodule" + + (cd m1 && git checkout @~1) + rm -Rf m1 && mkdir m1 +) + +git init modified-and-untracked +(cd modified-and-untracked + git submodule add ../module1 m1 + git commit -m "add submodule" + + (cd m1 + echo change >> this + touch new + ) +) + +git init submodule-head-changed-and-modified +(cd submodule-head-changed-and-modified + git submodule add ../module1 m1 + git commit -m "add submodule" + + (cd m1 + git checkout @~1 + echo change >> this + ) +) + +git init modified-untracked-and-submodule-head-changed-and-modified +(cd modified-untracked-and-submodule-head-changed-and-modified + git submodule add ../module1 m1 + git commit -m "add submodule" + + (cd m1 + git checkout @~1 + echo change >> this + ) + + touch this + git add this && git commit -m "this" + echo change >> this + touch untracked ) git init with-submodules diff --git a/gix/tests/fixtures/make_worktree_repo.sh b/gix/tests/fixtures/make_worktree_repo.sh index d18faf78e45..b7681a351d8 100755 --- a/gix/tests/fixtures/make_worktree_repo.sh +++ b/gix/tests/fixtures/make_worktree_repo.sh @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash set -eu -o pipefail bare="${1:-}" diff --git a/gix/tests/fixtures/make_worktree_repo_with_configs.sh b/gix/tests/fixtures/make_worktree_repo_with_configs.sh index f1c046d7987..5bee58c628c 100755 --- a/gix/tests/fixtures/make_worktree_repo_with_configs.sh +++ b/gix/tests/fixtures/make_worktree_repo_with_configs.sh @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash set -eu -o pipefail diff --git a/gix/tests/gix.rs b/gix/tests/gix.rs index b4b71c9db7f..d47cb2ccf07 100644 --- a/gix/tests/gix.rs +++ b/gix/tests/gix.rs @@ -16,5 +16,7 @@ mod remote; mod repository; #[cfg(feature = "revision")] mod revision; +#[cfg(feature = "status")] +mod status; #[cfg(feature = "attributes")] mod submodule; diff --git a/gix/tests/id/mod.rs b/gix/tests/id/mod.rs index f284a76e65a..143881d92c9 100644 --- a/gix/tests/id/mod.rs +++ b/gix/tests/id/mod.rs @@ -87,7 +87,7 @@ mod ancestors { let commits_by_commit_date = head .ancestors() .use_commit_graph(!use_commit_graph) - .sorting(commit::Sorting::ByCommitTimeNewestFirst) + .sorting(commit::simple::Sorting::ByCommitTimeNewestFirst) .all()? .map(|c| c.map(gix::revision::walk::Info::detach)) .collect::, _>>()?; @@ -121,7 +121,7 @@ mod ancestors { let head = repo.head()?.into_peeled_id()?; let commits = head .ancestors() - .sorting(commit::Sorting::ByCommitTimeNewestFirst) // assure we have time set + .sorting(commit::simple::Sorting::ByCommitTimeNewestFirst) // assure we have time set .use_commit_graph(use_commit_graph) .all()? .collect::, _>>()?; @@ -141,9 +141,9 @@ mod ancestors { for use_commit_graph in [false, true] { for sorting in [ - commit::Sorting::BreadthFirst, - commit::Sorting::ByCommitTimeNewestFirst, - commit::Sorting::ByCommitTimeNewestFirstCutoffOlderThan { seconds: 0 }, + commit::simple::Sorting::BreadthFirst, + commit::simple::Sorting::ByCommitTimeNewestFirst, + commit::simple::Sorting::ByCommitTimeNewestFirstCutoffOlderThan { seconds: 0 }, ] { let commits_graph_order = head .ancestors() diff --git a/gix/tests/reference/mod.rs b/gix/tests/reference/mod.rs index d168819eb64..7dd762f097e 100644 --- a/gix/tests/reference/mod.rs +++ b/gix/tests/reference/mod.rs @@ -18,8 +18,6 @@ mod log { } } mod find { - use std::convert::TryInto; - use gix_ref as refs; use gix_ref::{FullName, FullNameRef, Target}; diff --git a/gix/tests/remote/save.rs b/gix/tests/remote/save.rs index 44e5aca8316..69206763894 100644 --- a/gix/tests/remote/save.rs +++ b/gix/tests/remote/save.rs @@ -45,8 +45,6 @@ mod save_to { } mod save_as_to { - use std::convert::TryInto; - use crate::{basic_repo, remote::save::uniformize}; #[test] diff --git a/gix/tests/repository/config/mod.rs b/gix/tests/repository/config/mod.rs index 056bc903546..227a7ed308e 100644 --- a/gix/tests/repository/config/mod.rs +++ b/gix/tests/repository/config/mod.rs @@ -38,7 +38,6 @@ mod ssh_options { #[cfg(any(feature = "blocking-network-client", feature = "async-network-client"))] mod transport_options; -#[cfg(feature = "blocking-network-client")] #[cfg(feature = "blocking-network-client")] pub fn repo(name: &str) -> gix::Repository { repo_opts(name, |opts| opts.strict_config(true)) diff --git a/gix/tests/repository/config/remote.rs b/gix/tests/repository/config/remote.rs index 0bca87da755..ba1e5bdb373 100644 --- a/gix/tests/repository/config/remote.rs +++ b/gix/tests/repository/config/remote.rs @@ -1,6 +1,5 @@ use gix::bstr::BStr; use std::borrow::Cow; -use std::iter::FromIterator; use crate::remote; diff --git a/gix/tests/repository/excludes.rs b/gix/tests/repository/excludes.rs new file mode 100644 index 00000000000..5904b4be425 --- /dev/null +++ b/gix/tests/repository/excludes.rs @@ -0,0 +1,28 @@ +use crate::util::named_subrepo_opts; +use gix_worktree::stack::state::ignore::Source; + +#[test] +fn empty_core_excludes() -> crate::Result { + let repo = named_subrepo_opts( + "make_basic_repo.sh", + "empty-core-excludes", + gix::open::Options::default().strict_config(true), + )?; + let index = repo.index_or_empty()?; + match repo.excludes(&index, None, Source::WorktreeThenIdMappingIfNotSkipped) { + Ok(_) => { + unreachable!("Should fail due to empty excludes path") + } + Err(err) => { + assert_eq!( + err.to_string(), + "The value for `core.excludesFile` could not be read from configuration" + ); + } + }; + + let repo = gix::open_opts(repo.git_dir(), repo.open_options().clone().strict_config(false))?; + repo.excludes(&index, None, Source::WorktreeThenIdMappingIfNotSkipped) + .expect("empty paths are now just skipped"); + Ok(()) +} diff --git a/gix/tests/repository/mod.rs b/gix/tests/repository/mod.rs index 80a711e17f0..750987bc590 100644 --- a/gix/tests/repository/mod.rs +++ b/gix/tests/repository/mod.rs @@ -1,6 +1,8 @@ use gix::Repository; mod config; +#[cfg(feature = "excludes")] +mod excludes; #[cfg(feature = "attributes")] mod filter; mod object; @@ -19,6 +21,7 @@ mod worktree; mod dirwalk { use gix_dir::entry::Kind::*; use gix_dir::walk::EmissionMode; + use std::sync::atomic::AtomicBool; #[test] fn basics() -> crate::Result { @@ -26,22 +29,49 @@ mod dirwalk { let untracked_only = repo.dirwalk_options()?.emit_untracked(EmissionMode::CollapseDirectory); let mut collect = gix::dir::walk::delegate::Collect::default(); let index = repo.index()?; - repo.dirwalk(&index, None::<&str>, untracked_only, &mut collect)?; + repo.dirwalk( + &index, + None::<&str>, + &AtomicBool::default(), + untracked_only, + &mut collect, + )?; + let expected = [ + ("all-untracked".to_string(), Repository), + ("bare-repo-with-index.git".to_string(), Directory), + ("bare.git".into(), Directory), + ("empty-core-excludes".into(), Repository), + ("non-bare-repo-without-index".into(), Repository), + ("some".into(), Directory), + ]; assert_eq!( collect .into_entries_by_path() .into_iter() .map(|e| (e.0.rela_path.to_string(), e.0.disk_kind.expect("kind is known"))) .collect::>(), - [ - ("all-untracked".to_string(), Repository), - ("bare-repo-with-index.git".to_string(), Directory), - ("bare.git".into(), Directory), - ("non-bare-repo-without-index".into(), Repository), - ("some".into(), Directory) - ], + expected, "note how bare repos are just directories by default" ); + let mut iter = repo.dirwalk_iter(index, None::<&str>, Default::default(), untracked_only)?; + let mut actual: Vec<_> = iter + .by_ref() + .map(Result::unwrap) + .map(|item| { + ( + item.entry.rela_path.to_string(), + item.entry.disk_kind.expect("kind is known"), + ) + }) + .collect(); + actual.sort_by(|a, b| a.0.cmp(&b.0)); + assert_eq!(actual, expected, "the iterator works the same"); + let out = iter.into_outcome().expect("iteration done and no error"); + assert_eq!( + out.dirwalk.returned_entries, + expected.len(), + "just a minor sanity check, assuming everything else works as well" + ); Ok(()) } } diff --git a/gix/tests/repository/object.rs b/gix/tests/repository/object.rs index 3761c0e8209..2aa2607cc69 100644 --- a/gix/tests/repository/object.rs +++ b/gix/tests/repository/object.rs @@ -6,7 +6,7 @@ mod write_object { #[test] fn empty_tree() -> crate::Result { let (_tmp, repo) = empty_bare_repo()?; - let oid = repo.write_object(&gix::objs::TreeRef::empty())?; + let oid = repo.write_object(gix::objs::TreeRef::empty())?; assert_eq!( oid, gix::hash::ObjectId::empty_tree(repo.object_hash()), @@ -280,7 +280,7 @@ mod commit { crate::restricted(), )? .to_thread_local(); - let empty_tree_id = repo.write_object(&gix::objs::Tree::empty())?.detach(); + let empty_tree_id = repo.write_object(gix::objs::Tree::empty())?.detach(); let err = repo .commit("HEAD", "initial", empty_tree_id, [empty_tree_id]) .unwrap_err(); @@ -304,7 +304,7 @@ mod commit { restricted_and_git(), )? .to_thread_local(); - let empty_tree_id = repo.write_object(&gix::objs::Tree::empty())?; + let empty_tree_id = repo.write_object(gix::objs::Tree::empty())?; let commit_id = repo.commit("HEAD", "initial", empty_tree_id, gix::commit::NO_PARENT_IDS)?; assert_eq!( commit_id, diff --git a/gix/tests/repository/open.rs b/gix/tests/repository/open.rs index f779ca39648..17a658dbf28 100644 --- a/gix/tests/repository/open.rs +++ b/gix/tests/repository/open.rs @@ -268,8 +268,14 @@ mod object_caches { fn default_git_and_custom_caches() -> crate::Result { let opts = gix::open::Options::isolated(); let repo = named_subrepo_opts("make_config_repos.sh", "object-caches", opts)?; - assert_eq!(repo.objects.has_object_cache(), cfg!(feature = "comfort")); - assert_eq!(repo.objects.has_pack_cache(), cfg!(feature = "comfort")); + assert_eq!( + repo.objects.has_object_cache(), + cfg!(all(feature = "parallel", feature = "comfort")) + ); + assert_eq!( + repo.objects.has_pack_cache(), + cfg!(all(feature = "parallel", feature = "comfort")) + ); Ok(()) } diff --git a/gix/tests/repository/shallow.rs b/gix/tests/repository/shallow.rs index aac1ee5cc37..8e5c2bd0a66 100644 --- a/gix/tests/repository/shallow.rs +++ b/gix/tests/repository/shallow.rs @@ -44,7 +44,7 @@ fn yes() -> crate::Result { } mod traverse { - use gix_traverse::commit::Sorting; + use gix_traverse::commit::simple::Sorting; use serial_test::parallel; use crate::util::{hex_to_id, named_subrepo_opts}; diff --git a/gix/tests/repository/worktree.rs b/gix/tests/repository/worktree.rs index 16dfb35073d..19bce459584 100644 --- a/gix/tests/repository/worktree.rs +++ b/gix/tests/repository/worktree.rs @@ -49,7 +49,7 @@ mod with_core_worktree_config { } else { assert_eq!( repo.work_dir().unwrap(), - gix_path::realpath(&repo.git_dir().parent().unwrap().parent().unwrap().join("worktree"))?, + gix_path::realpath(repo.git_dir().parent().unwrap().parent().unwrap().join("worktree"))?, "absolute workdirs are left untouched" ); } diff --git a/gix/tests/status/mod.rs b/gix/tests/status/mod.rs new file mode 100644 index 00000000000..de6e9d65edd --- /dev/null +++ b/gix/tests/status/mod.rs @@ -0,0 +1,174 @@ +pub fn submodule_repo(name: &str) -> crate::Result { + use crate::util::named_subrepo_opts; + Ok(named_subrepo_opts( + "make_submodules.sh", + name, + gix::open::Options::isolated(), + )?) +} + +pub fn repo(name: &str) -> crate::Result { + use crate::util::named_subrepo_opts; + Ok(named_subrepo_opts( + "make_status_repos.sh", + name, + gix::open::Options::isolated(), + )?) +} + +mod index_worktree { + mod iter { + use crate::status::{repo, submodule_repo}; + use gix::status::index_worktree::iter::Item; + use pretty_assertions::assert_eq; + + #[test] + fn item_size() { + assert_eq!( + std::mem::size_of::(), + 264, + "The size is pretty huge and goes down ideally" + ); + } + + #[test] + fn submodule_modification() -> crate::Result { + let repo = submodule_repo("modified-untracked-and-submodule-head-changed-and-modified")?; + let mut status = repo + .status(gix::progress::Discard)? + .index_worktree_options_mut(|opts| { + opts.sorting = + Some(gix::status::plumbing::index_as_worktree_with_renames::Sorting::ByPathCaseSensitive) + }) + .into_index_worktree_iter(Vec::new())?; + let items: Vec<_> = status.by_ref().filter_map(Result::ok).collect(); + assert_eq!(items.len(), 3, "1 untracked, 1 modified file, 1 submodule modification"); + Ok(()) + } + + #[test] + fn untracked_files_collapse_by_default() -> crate::Result { + let repo = repo("untracked-only")?; + let status = repo + .status(gix::progress::Discard)? + .index_worktree_options_mut(|opts| { + opts.sorting = + Some(gix::status::plumbing::index_as_worktree_with_renames::Sorting::ByPathCaseSensitive) + }) + .into_index_worktree_iter(Vec::new())?; + let items: Vec<_> = status.filter_map(Result::ok).collect(); + assert_eq!( + items, + [ + Item::DirectoryContents { + entry: gix_dir::Entry { + rela_path: "new".into(), + status: gix_dir::entry::Status::Untracked, + property: None, + disk_kind: Some(gix_dir::entry::Kind::Directory), + index_kind: None, + pathspec_match: Some(gix_dir::entry::PathspecMatch::Always), + }, + collapsed_directory_status: None + }, + Item::DirectoryContents { + entry: gix_dir::Entry { + rela_path: "subdir/untracked".into(), + status: gix_dir::entry::Status::Untracked, + property: None, + disk_kind: Some(gix_dir::entry::Kind::File), + index_kind: None, + pathspec_match: Some(gix_dir::entry::PathspecMatch::Always), + }, + collapsed_directory_status: None + } + ], + "'new/untracked' gets collapsed, but the second untracked is in a folder with a tracked file.\ + This collapsing behaviour is the default." + ); + Ok(()) + } + + #[test] + fn untracked_files_settings_none() -> crate::Result { + let mut repo = repo("untracked-only")?; + repo.config_snapshot_mut() + .set_value(&gix::config::tree::Status::SHOW_UNTRACKED_FILES, "no")?; + + let mut status = repo + .status(gix::progress::Discard)? + .index_worktree_options_mut(|opts| { + opts.sorting = + Some(gix::status::plumbing::index_as_worktree_with_renames::Sorting::ByPathCaseSensitive) + }) + .into_index_worktree_iter(Vec::new())?; + let items: Vec<_> = status.by_ref().filter_map(Result::ok).collect(); + assert_eq!(items, [], "no untracked files are found…"); + assert_eq!( + status.outcome_mut().expect("iteration done").index_worktree.dirwalk, + None, + "…as there was no directory walk" + ); + Ok(()) + } + + #[test] + fn early_drop_for_is_dirty_emulation() -> crate::Result { + let repo = submodule_repo("modified-untracked-and-submodule-head-changed-and-modified")?; + let is_dirty = repo + .status(gix::progress::Discard)? + .index_worktree_submodules(gix::status::Submodule::AsConfigured { check_dirty: true }) + .index_worktree_options_mut(|opts| { + opts.sorting = + Some(gix::status::plumbing::index_as_worktree_with_renames::Sorting::ByPathCaseSensitive) + }) + .into_index_worktree_iter(Vec::new())? + .next() + .is_some(); + assert!(is_dirty, "this should abort the work as quickly as possible"); + Ok(()) + } + } +} + +mod is_dirty { + use crate::status::submodule_repo; + + #[test] + fn various_changes_positive() -> crate::Result { + let repo = submodule_repo("modified-untracked-and-submodule-head-changed-and-modified")?; + assert!(repo.is_dirty()?, "The repository has various changes"); + Ok(()) + } + + #[test] + fn submodule_changes_are_picked_up() -> crate::Result { + let repo = submodule_repo("submodule-head-changed")?; + assert!(repo.is_dirty()?, "head-changes are also discovered"); + Ok(()) + } + + #[test] + fn untracked_files_are_excluded() -> crate::Result { + let repo = submodule_repo("module1")?; + assert_eq!( + repo.status(gix::progress::Discard)? + .into_index_worktree_iter(Vec::new())? + .count(), + 1, + "there is one untracked file" + ); + assert!( + !repo.is_dirty()?, + "untracked files aren't taken into consideration, just like `git describe` which ignores them" + ); + Ok(()) + } + + #[test] + fn no_changes() -> crate::Result { + let repo = submodule_repo("with-submodules")?; + assert!(!repo.is_dirty()?, "there are no changes"); + Ok(()) + } +} diff --git a/gix/tests/submodule/mod.rs b/gix/tests/submodule/mod.rs index 1cdd27a9458..0ff6480d88e 100644 --- a/gix/tests/submodule/mod.rs +++ b/gix/tests/submodule/mod.rs @@ -26,6 +26,7 @@ mod open { worktree_checkout: true, superproject_configuration: true, }, + Some(false), ), ( "dir/m1", @@ -35,6 +36,7 @@ mod open { worktree_checkout: true, superproject_configuration: true, }, + Some(false), ), ] as &[_], ), @@ -48,6 +50,7 @@ mod open { worktree_checkout: false, superproject_configuration: true, }, + None, )], ), ( @@ -60,12 +63,15 @@ mod open { worktree_checkout: false, superproject_configuration: true, }, + None, )], ), ] { let repo = repo(name)?; - for (sm, (name, expected)) in repo.submodules()?.expect("modules present").zip(expected) { - assert_eq!(sm.name(), name); + for (sm, (sm_name, expected, _expected_is_dirty)) in + repo.submodules()?.expect("modules present").zip(expected) + { + assert_eq!(sm.name(), sm_name); let state = sm.state()?; assert_eq!(&state, expected); @@ -87,6 +93,28 @@ mod open { "there is a way to check for indicators that a submodule worktree isn't checked out though" ) } + #[cfg(feature = "status")] + for check_dirty in [false, true] { + let status = match sm.status(gix::submodule::config::Ignore::None, check_dirty) { + Ok(status) => status, + Err(err) => { + assert_eq!( + name, "not-a-submodule", + "{name}: BUG: only one submodule is expected to fail, got '{err:?}'" + ); + continue; + } + }; + assert_eq!( + &status.state, expected, + "no matter what status configuration, the state is always obtained" + ); + assert_eq!( + status.is_dirty(), + *_expected_is_dirty, + "none of these submodules are dirty, but some aren't checked out" + ) + } } assert_eq!( repo.modules()?.expect("present").names().count(), @@ -97,6 +125,179 @@ mod open { Ok(()) } + #[cfg(feature = "status")] + mod status { + use crate::submodule::repo; + use crate::util::hex_to_id; + + #[test] + fn changed_head_compared_to_superproject_index() -> crate::Result { + let repo = repo("submodule-head-changed")?; + let sm = repo.submodules()?.into_iter().flatten().next().expect("one submodule"); + let mut status = sm.status(gix::submodule::config::Ignore::None, false)?; + assert_eq!( + status.is_dirty(), + Some(true), + "we could decide that the submodule is dirty" + ); + assert_eq!( + status.index_id, + Some(hex_to_id("e046f3e51d955840619fc7d01fbd9a469663de22")) + ); + assert_eq!( + status.checked_out_head_id, + Some(hex_to_id("362cb5539acbd3c8ca355471f97c6a68d3db0da7")), + "the checked out head was reset to something else after the superproject commit" + ); + assert_eq!( + status.changes, + Some(Vec::new()), + "the status check ran, but there were no changes" + ); + // make it easier to compare this as baseline + status.changes.take(); + + let status_with_ignore = sm.status(gix::submodule::config::Ignore::Dirty, false)?; + assert_eq!( + status_with_ignore, status, + "The lowest status that makes these changes observable" + ); + + let status_with_ignore_check_only = sm.status(gix::submodule::config::Ignore::Dirty, true)?; + assert_eq!( + status_with_ignore_check_only, status, + "dirty-check has no observable influence here yet as there no 'more expensive' changes" + ); + + let status_with_ignore = sm.status(gix::submodule::config::Ignore::All, false)?; + assert_eq!( + status_with_ignore.is_dirty(), + Some(false), + "no dirty-information is retrieved, it seems clean" + ); + assert_eq!( + status_with_ignore.index_id, None, + "to avoid false-positives, we don't retrieve the value" + ); + assert_eq!( + status_with_ignore.checked_out_head_id, None, + "this check is ignored as it requires opening a repository" + ); + Ok(()) + } + + #[test] + fn modified_and_untracked() -> crate::Result { + let repo = repo("modified-and-untracked")?; + let sm = repo.submodules()?.into_iter().flatten().next().expect("one submodule"); + + let status = sm.status(gix::submodule::config::Ignore::Dirty, false)?; + assert_eq!(status.is_dirty(), Some(false), "Dirty skips worktree changes entirely"); + + let status = sm.status_opts( + gix::submodule::config::Ignore::None, + false, + &mut |status: gix::status::Platform<'_, gix::progress::Discard>| { + status.index_worktree_options_mut(|opts| { + opts.sorting = Some(gix_status::index_as_worktree_with_renames::Sorting::ByPathCaseSensitive); + }) + }, + )?; + assert_eq!( + status.is_dirty(), + Some(true), + "we could decide that the submodule is dirty" + ); + assert_eq!(status.index_id, status.checked_out_head_id, "the head didn't change"); + assert_eq!( + status.changes.as_ref().into_iter().flatten().count(), + 2, + "1 modified, 1 untracked" + ); + + let status_with_dirty_check = sm.status_opts( + gix::submodule::config::Ignore::None, + true, + &mut |status: gix::status::Platform<'_, gix::progress::Discard>| { + status.index_worktree_options_mut(|opts| { + opts.sorting = Some(gix_status::index_as_worktree_with_renames::Sorting::ByPathCaseSensitive); + }) + }, + )?; + assert_eq!( + status_with_dirty_check, status, + "it cannot abort early as the only change it sees is the modification check" + ); + + let status = sm.status(gix::submodule::config::Ignore::Untracked, false)?; + assert_eq!( + status.is_dirty(), + Some(true), + "we could decide that the submodule is dirty, even though untracked files are missing" + ); + assert_eq!(status.index_id, status.checked_out_head_id, "the head didn't change"); + assert_eq!(status.changes.as_ref().into_iter().flatten().count(), 1, "1 modified"); + + Ok(()) + } + + #[test] + fn changed_head_empty_worktree() -> crate::Result { + let repo = repo("submodule-head-changed-no-worktree")?; + let sm = repo.submodules()?.into_iter().flatten().next().expect("one submodule"); + + let status = sm.status(gix::submodule::config::Ignore::None, false)?; + assert_eq!( + status.state, + gix::submodule::State { + repository_exists: true, + is_old_form: false, + worktree_checkout: false, + superproject_configuration: true, + } + ); + assert_eq!( + status.is_dirty(), + None, + "a missing worktree counts as no-dirty, even though the checked out HEAD changed. \ + Git does the same, even though as we express it as 'not determined'" + ); + assert_ne!( + status.index_id, status.checked_out_head_id, + "not considered dirty despite head mismatch" + ); + assert!( + status.changes.is_none(), + "Detailed changes are never done if there is no worktree" + ); + + Ok(()) + } + + #[test] + fn is_dirty_skips_expensive_checks() -> crate::Result { + let repo = repo("submodule-head-changed-and-modified")?; + let sm = repo.submodules()?.into_iter().flatten().next().expect("one submodule"); + + let status = sm.status(gix::submodule::config::Ignore::None, true)?; + assert_eq!( + status.changes, None, + "computation was stopped on the first detected change (the index/head)" + ); + assert_eq!( + status.index_id, + Some(hex_to_id("e046f3e51d955840619fc7d01fbd9a469663de22")), + "the index id was obtained" + ); + assert_eq!( + status.checked_out_head_id, + Some(hex_to_id("362cb5539acbd3c8ca355471f97c6a68d3db0da7")), + "the checked out head was also obtained to be able to se if it's dirty" + ); + Ok(()) + } + } + #[test] fn not_a_submodule() -> crate::Result { let repo = repo("not-a-submodule")?; diff --git a/justfile b/justfile index c43c6c94826..8979a7777fd 100755 --- a/justfile +++ b/justfile @@ -92,6 +92,8 @@ check: cargo check -p gix-revision --no-default-features --features describe cargo check -p gix-mailmap --features serde cargo check -p gix-url --all-features + cargo check -p gix-status + cargo check -p gix-status --all-features cargo check -p gix-features --all-features cargo check -p gix-features --features parallel cargo check -p gix-features --features fs-walkdir-parallel @@ -179,6 +181,7 @@ unit-tests: cargo test -p gix-protocol --features blocking-client cargo test -p gix-protocol --features async-client cargo test -p gix --no-default-features + cargo test -p gix --no-default-features --features basic,extras,comfort cargo test -p gix --features async-network-client cargo test -p gix --features blocking-network-client cargo test -p gitoxide-core --lib @@ -249,3 +252,7 @@ fmt: # Cancel this after the first few seconds, as yanked crates will appear in warnings. find-yanked: cargo install --debug --locked --no-default-features --features max-pure --path . + +# Delete gix-packetline-blocking/src and regenerate from gix-packetline/src +copy-packetline: + ./etc/copy-packetline.sh diff --git a/src/ein.rs b/src/ein.rs index ebcfa5a9965..07a0fe314e7 100644 --- a/src/ein.rs +++ b/src/ein.rs @@ -1,9 +1,7 @@ #![deny(rust_2018_idioms, unsafe_code)] -mod porcelain; - fn main() -> anyhow::Result<()> { - porcelain::main() + gitoxide::porcelain::main() } #[cfg(not(feature = "pretty-cli"))] diff --git a/src/gix.rs b/src/gix.rs index 8527e57926a..2fd02b14e32 100644 --- a/src/gix.rs +++ b/src/gix.rs @@ -1,12 +1,8 @@ #![deny(unsafe_code, rust_2018_idioms)] -mod plumbing; - -use anyhow::Result; - #[cfg(feature = "pretty-cli")] -fn main() -> Result<()> { - plumbing::main() +fn main() -> anyhow::Result<()> { + gitoxide::plumbing::main() } #[cfg(not(feature = "pretty-cli"))] diff --git a/src/lib.rs b/src/lib.rs index 64313c03533..a512433c3ac 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -22,7 +22,9 @@ #![cfg_attr(all(doc, feature = "document-features"), feature(doc_cfg, doc_auto_cfg))] #![deny(rust_2018_idioms)] #![allow(missing_docs)] -#![forbid(unsafe_code)] +#![deny(unsafe_code)] +pub mod plumbing; +pub mod porcelain; /// everything in common between the `gix` and `ein` binaries. pub mod shared; diff --git a/src/plumbing/main.rs b/src/plumbing/main.rs index aeb589c584f..b4c788952f1 100644 --- a/src/plumbing/main.rs +++ b/src/plumbing/main.rs @@ -7,9 +7,9 @@ use std::{ }, }; +use crate::shared::pretty::prepare_and_run; use anyhow::{anyhow, Context, Result}; use clap::{CommandFactory, Parser}; -use gitoxide::shared::pretty::prepare_and_run; use gitoxide_core as core; use gitoxide_core::{pack::verify, repository::PathsOrPatterns}; use gix::bstr::{io::BufReadExt, BString}; @@ -24,7 +24,7 @@ use crate::plumbing::{ #[cfg(feature = "gitoxide-core-async-client")] pub mod async_util { - use gitoxide::shared::ProgressRange; + use crate::shared::ProgressRange; #[cfg(not(feature = "prodash-render-line"))] compile_error!("BUG: Need at least a line renderer in async mode"); @@ -38,7 +38,7 @@ pub mod async_util { Option, gix_features::progress::DoOrDiscard, ) { - use gitoxide::shared::{self, STANDARD_RANGE}; + use crate::shared::{self, STANDARD_RANGE}; shared::init_env_logger(); if verbose { @@ -146,6 +146,24 @@ pub fn main() -> Result<()> { } match cmd { + Subcommands::IsClean | Subcommands::IsChanged => { + let mode = if matches!(cmd, Subcommands::IsClean) { + core::repository::dirty::Mode::IsClean + } else { + core::repository::dirty::Mode::IsDirty + }; + prepare_and_run( + "clean", + trace, + verbose, + progress, + progress_keep_open, + None, + move |_progress, out, _err| { + core::repository::dirty::check(repository(Mode::Lenient)?, mode, out, format) + }, + ) + } #[cfg(feature = "gitoxide-core-tools-clean")] Subcommands::Clean(crate::plumbing::options::clean::Command { debug, @@ -188,10 +206,13 @@ pub fn main() -> Result<()> { }, ), Subcommands::Status(crate::plumbing::options::status::Platform { + ignored, + format: status_format, statistics, submodules, no_write, pathspec, + index_worktree_renames, }) => prepare_and_run( "status", trace, @@ -208,31 +229,56 @@ pub fn main() -> Result<()> { err, progress, core::repository::status::Options { - format, + format: match status_format.unwrap_or_default() { + crate::plumbing::options::status::Format::Simplified => { + core::repository::status::Format::Simplified + } + crate::plumbing::options::status::Format::PorcelainV2 => { + core::repository::status::Format::PorcelainV2 + } + }, + ignored: ignored.map(|ignored| match ignored.unwrap_or_default() { + crate::plumbing::options::status::Ignored::Matching => { + core::repository::status::Ignored::Matching + } + crate::plumbing::options::status::Ignored::Collapsed => { + core::repository::status::Ignored::Collapsed + } + }), + output_format: format, statistics, thread_limit: thread_limit.or(cfg!(target_os = "macos").then_some(3)), // TODO: make this a configurable when in `gix`, this seems to be optimal on MacOS, linux scales though! MacOS also scales if reading a lot of files for refresh index allow_write: !no_write, - submodules: match submodules { + index_worktree_renames: index_worktree_renames.map(|percentage| percentage.unwrap_or(0.5)), + submodules: submodules.map(|submodules| match submodules { Submodules::All => core::repository::status::Submodules::All, Submodules::RefChange => core::repository::status::Submodules::RefChange, Submodules::Modifications => core::repository::status::Submodules::Modifications, - }, + Submodules::None => core::repository::status::Submodules::None, + }), }, ) }, ), Subcommands::Submodule(platform) => match platform .cmds - .unwrap_or(crate::plumbing::options::submodule::Subcommands::List) + .unwrap_or(crate::plumbing::options::submodule::Subcommands::List { dirty_suffix: None }) { - crate::plumbing::options::submodule::Subcommands::List => prepare_and_run( + crate::plumbing::options::submodule::Subcommands::List { dirty_suffix } => prepare_and_run( "submodule-list", trace, verbose, progress, progress_keep_open, None, - move |_progress, out, _err| core::repository::submodule::list(repository(Mode::Lenient)?, out, format), + move |_progress, out, _err| { + core::repository::submodule::list( + repository(Mode::Lenient)?, + out, + format, + dirty_suffix.map(|suffix| suffix.unwrap_or_else(|| "dirty".to_string())), + ) + }, ), }, #[cfg(feature = "gitoxide-core-tools-archive")] @@ -358,6 +404,7 @@ pub fn main() -> Result<()> { handshake_info, bare, no_tags, + ref_name, remote, shallow, directory, @@ -367,6 +414,7 @@ pub fn main() -> Result<()> { bare, handshake_info, no_tags, + ref_name, shallow: shallow.into(), }; prepare_and_run( @@ -686,7 +734,7 @@ pub fn main() -> Result<()> { &url, directory, refs_directory, - refs.into_iter().map(|s| s.into()).collect(), + refs.into_iter().map(Into::into).collect(), progress, core::pack::receive::Context { thread_limit, @@ -1036,6 +1084,7 @@ pub fn main() -> Result<()> { statistics, max_candidates, rev_spec, + dirty_suffix, } => prepare_and_run( "commit-describe", trace, @@ -1057,6 +1106,7 @@ pub fn main() -> Result<()> { statistics, max_candidates, always, + dirty_suffix: dirty_suffix.map(|suffix| suffix.unwrap_or_else(|| "dirty".to_string())), }, ) }, @@ -1105,7 +1155,7 @@ pub fn main() -> Result<()> { ), }, Subcommands::Odb(cmd) => match cmd { - odb::Subcommands::Stats => prepare_and_run( + odb::Subcommands::Stats { extra_header_lookup } => prepare_and_run( "odb-stats", trace, auto_verbose, @@ -1118,7 +1168,11 @@ pub fn main() -> Result<()> { progress, out, err, - core::repository::odb::statistics::Options { format, thread_limit }, + core::repository::odb::statistics::Options { + format, + thread_limit, + extra_header_lookup, + }, ) }, ), diff --git a/src/plumbing/options/free.rs b/src/plumbing/options/free.rs index 083dd943b34..743b2e39d2d 100644 --- a/src/plumbing/options/free.rs +++ b/src/plumbing/options/free.rs @@ -19,6 +19,7 @@ pub enum Subcommands { } /// +#[allow(clippy::empty_docs)] pub mod commitgraph { use std::path::PathBuf; @@ -41,7 +42,7 @@ pub mod index { #[derive(Debug, clap::Parser)] pub struct Platform { /// The object format to assume when reading files that don't inherently know about it, or when writing files. - #[clap(long, default_value_t = gix::hash::Kind::default(), value_parser = gitoxide::shared::AsHashKind)] + #[clap(long, default_value_t = gix::hash::Kind::default(), value_parser = crate::shared::AsHashKind)] pub object_hash: gix::hash::Kind, /// The path to the index file. @@ -99,6 +100,7 @@ pub mod index { } /// +#[allow(clippy::empty_docs)] pub mod pack { use std::{ffi::OsString, path::PathBuf}; @@ -287,6 +289,7 @@ pub mod pack { } /// + #[allow(clippy::empty_docs)] pub mod multi_index { use std::path::PathBuf; @@ -321,6 +324,7 @@ pub mod pack { } /// + #[allow(clippy::empty_docs)] pub mod index { use std::path::PathBuf; @@ -458,6 +462,7 @@ pub mod pack { } /// +#[allow(clippy::empty_docs)] pub mod mailmap { use std::path::PathBuf; diff --git a/src/plumbing/options/mod.rs b/src/plumbing/options/mod.rs index 690ea56ecaa..af1374241fb 100644 --- a/src/plumbing/options/mod.rs +++ b/src/plumbing/options/mod.rs @@ -17,7 +17,7 @@ pub struct Args { /// /// For example, if `key` is `core.abbrev`, set configuration like `[core] abbrev = key`, /// or `remote.origin.url = foo` to set `[remote "origin"] url = foo`. - #[clap(long, short = 'c', value_parser = gitoxide::shared::AsBString)] + #[clap(long, short = 'c', value_parser = crate::shared::AsBString)] pub config: Vec, #[clap(long, short = 't')] @@ -64,12 +64,12 @@ pub struct Args { long, short = 'f', default_value = "human", - value_parser = gitoxide::shared::AsOutputFormat + value_parser = crate::shared::AsOutputFormat )] pub format: core::OutputFormat, /// The object format to assume when reading files that don't inherently know about it, or when writing files. - #[clap(long, default_value_t = gix::hash::Kind::default(), value_parser = gitoxide::shared::AsHashKind)] + #[clap(long, default_value_t = gix::hash::Kind::default(), value_parser = crate::shared::AsHashKind)] pub object_hash: gix::hash::Kind, #[clap(subcommand)] @@ -130,6 +130,8 @@ pub enum Subcommands { /// Interact with submodules. #[clap(alias = "submodules")] Submodule(submodule::Platform), + IsClean, + IsChanged, /// Show which git configuration values are used or planned. ConfigTree, Status(status::Platform), @@ -199,7 +201,7 @@ pub mod archive { } pub mod status { - use gitoxide::shared::CheckPathSpec; + use crate::shared::{CheckPathSpec, ParseRenameFraction}; use gix::bstr::BString; #[derive(Default, Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, clap::ValueEnum)] @@ -211,20 +213,54 @@ pub mod status { RefChange, /// See if there are worktree modifications compared to the index, but do not check for untracked files. Modifications, + /// Ignore all submodule changes. + None, + } + + #[derive(Default, Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, clap::ValueEnum)] + pub enum Ignored { + /// display all ignored files and directories, but collapse them if possible to simplify. + #[default] + Collapsed, + /// Show exact matches. Note that this may show directories if these are a match as well. + /// + /// Simplification will not happen in this mode. + Matching, + // TODO: figure out how to implement traditional, which right now can't be done as it requires ignored folders + // to be fully expanded. This should probably be implemented in `gix_dir` which then simply works by not + // allowing to ignore directories, naturally traversing the entire content. + } + + #[derive(Default, Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, clap::ValueEnum)] + pub enum Format { + /// A basic format that is easy to read, and useful for a first glimpse as flat list. + #[default] + Simplified, + /// Output very similar to `git status --porcelain=2`. + PorcelainV2, } #[derive(Debug, clap::Parser)] #[command(about = "compute repository status similar to `git status`")] pub struct Platform { - /// Define how to display submodule status. - #[clap(long, default_value = "all")] - pub submodules: Submodules, + /// The way status data is displayed. + #[clap(long, short = 'f')] + pub format: Option, + /// If enabled, show ignored files and directories. + #[clap(long)] + pub ignored: Option>, + /// Define how to display the submodule status. Defaults to git configuration if unset. + #[clap(long)] + pub submodules: Option, /// Print additional statistics to help understanding performance. #[clap(long, short = 's')] pub statistics: bool, /// Don't write back a changed index, which forces this operation to always be idempotent. #[clap(long)] pub no_write: bool, + /// Enable rename tracking between the index and the working tree, preventing the collapse of folders as well. + #[clap(long, value_parser = ParseRenameFraction)] + pub index_worktree_renames: Option>, /// The git path specifications to list attributes for, or unset to read from stdin one per line. #[clap(value_parser = CheckPathSpec)] pub pathspec: Vec, @@ -284,7 +320,7 @@ pub mod config { /// /// Typical filters are `branch` or `remote.origin` or `remote.or*` - git-style globs are supported /// and comparisons are case-insensitive. - #[clap(value_parser = gitoxide::shared::AsBString)] + #[clap(value_parser = crate::shared::AsBString)] pub filter: Vec, } } @@ -323,7 +359,7 @@ pub mod fetch { pub remote: Option, /// Override the built-in and configured ref-specs with one or more of the given ones. - #[clap(value_parser = gitoxide::shared::AsBString)] + #[clap(value_parser = crate::shared::AsBString)] pub ref_spec: Vec, } @@ -338,11 +374,11 @@ pub mod fetch { pub deepen: Option, /// Cutoff all history past the given date. Can be combined with shallow-exclude. - #[clap(long, help_heading = Some("SHALLOW"), value_parser = gitoxide::shared::AsTime, value_name = "DATE", conflicts_with_all = ["depth", "deepen", "unshallow"])] + #[clap(long, help_heading = Some("SHALLOW"), value_parser = crate::shared::AsTime, value_name = "DATE", conflicts_with_all = ["depth", "deepen", "unshallow"])] pub shallow_since: Option, /// Cutoff all history past the tag-name or ref-name. Can be combined with shallow-since. - #[clap(long, help_heading = Some("SHALLOW"), value_parser = gitoxide::shared::AsPartialRefName, value_name = "REF_NAME", conflicts_with_all = ["depth", "deepen", "unshallow"])] + #[clap(long, help_heading = Some("SHALLOW"), value_parser = crate::shared::AsPartialRefName, value_name = "REF_NAME", conflicts_with_all = ["depth", "deepen", "unshallow"])] pub shallow_exclude: Vec, /// Remove the shallow boundary and fetch the entire history available on the remote. @@ -398,6 +434,10 @@ pub mod clone { /// The url of the remote to connect to, like `https://github.com/byron/gitoxide`. pub remote: OsString, + /// The name of the reference to check out. + #[clap(long = "ref", value_parser = crate::shared::AsPartialRefName, value_name = "REF_NAME")] + pub ref_name: Option, + /// The directory to initialize with the new repository and to which all data should be written. pub directory: Option, } @@ -409,11 +449,11 @@ pub mod clone { pub depth: Option, /// Cutoff all history past the given date. Can be combined with shallow-exclude. - #[clap(long, help_heading = Some("SHALLOW"), value_parser = gitoxide::shared::AsTime, value_name = "DATE")] + #[clap(long, help_heading = Some("SHALLOW"), value_parser = crate::shared::AsTime, value_name = "DATE")] pub shallow_since: Option, /// Cutoff all history past the tag-name or ref-name. Can be combined with shallow-since. - #[clap(long, help_heading = Some("SHALLOW"), value_parser = gitoxide::shared::AsPartialRefName, value_name = "REF_NAME")] + #[clap(long, help_heading = Some("SHALLOW"), value_parser = crate::shared::AsPartialRefName, value_name = "REF_NAME")] pub shallow_exclude: Vec, } @@ -465,7 +505,7 @@ pub mod remote { #[clap(long, short = 'u')] show_unmapped_remote_refs: bool, /// Override the built-in and configured ref-specs with one or more of the given ones. - #[clap(value_parser = gitoxide::shared::AsBString)] + #[clap(value_parser = crate::shared::AsBString)] ref_spec: Vec, }, } @@ -481,7 +521,7 @@ pub mod mailmap { #[cfg(feature = "gitoxide-core-tools-clean")] pub mod clean { - use gitoxide::shared::CheckPathSpec; + use crate::shared::CheckPathSpec; use gix::bstr::BString; #[derive(Default, Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, clap::ValueEnum)] @@ -550,7 +590,11 @@ pub mod odb { Info, /// Count and obtain information on all, possibly duplicate, objects in the database. #[clap(visible_alias = "statistics")] - Stats, + Stats { + /// Lookup headers again, but without preloading indices. + #[clap(long)] + extra_header_lookup: bool, + }, } } @@ -627,6 +671,10 @@ pub mod commit { /// If there was no way to describe the commit, fallback to using the abbreviated input revision. always: bool, + /// Set the suffix to append if the repository is dirty (not counting untracked files). + #[clap(short = 'd', long)] + dirty_suffix: Option>, + /// A specification of the revision to use, or the current `HEAD` if unset. rev_spec: Option, }, @@ -649,6 +697,7 @@ pub mod credential { } /// +#[allow(clippy::empty_docs)] pub mod commitgraph { #[derive(Debug, clap::Subcommand)] pub enum Subcommands { @@ -742,7 +791,7 @@ pub mod revision { } pub mod attributes { - use gitoxide::shared::CheckPathSpec; + use crate::shared::CheckPathSpec; use gix::bstr::BString; #[derive(Debug, clap::Subcommand)] @@ -772,7 +821,7 @@ pub mod attributes { pub mod exclude { use std::ffi::OsString; - use gitoxide::shared::CheckPathSpec; + use crate::shared::CheckPathSpec; use gix::bstr::BString; #[derive(Debug, clap::Subcommand)] @@ -802,16 +851,16 @@ pub mod exclude { pub mod index { use std::path::PathBuf; - use gitoxide::shared::CheckPathSpec; + use crate::shared::CheckPathSpec; use gix::bstr::BString; pub mod entries { #[derive(Default, Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, clap::ValueEnum)] pub enum Format { - /// + /// Show only minimal information, useful for first glances. #[default] Simple, - /// Use the `.tar` file format, uncompressed. + /// Show much more information that is still human-readable. Rich, } } @@ -871,9 +920,14 @@ pub mod submodule { #[derive(Debug, clap::Subcommand)] pub enum Subcommands { /// Print all direct submodules to standard output - List, + List { + /// Set the suffix to append if the repository is dirty (not counting untracked files). + #[clap(short = 'd', long)] + dirty_suffix: Option>, + }, } } /// +#[allow(clippy::empty_docs)] pub mod free; diff --git a/src/plumbing/progress.rs b/src/plumbing/progress.rs index 4444370b795..c05b7f7ee11 100644 --- a/src/plumbing/progress.rs +++ b/src/plumbing/progress.rs @@ -94,14 +94,6 @@ static GIT_CONFIG: &[Record] = &[ config: "core.loosecompression", usage: Planned("") }, - Record { - config: "core.protectHFS", - usage: Planned("relevant for checkout on MacOS") - }, - Record { - config: "core.protectNTFS", - usage: NotPlanned("lack of demand") - }, Record { config: "core.sparseCheckout", usage: Planned("we want to support huge repos and be the fastest in doing so") @@ -263,7 +255,7 @@ static GIT_CONFIG: &[Record] = &[ }, Record { config: "fetch.showForcedUpdates", - usage: NotApplicable("we don't support advices") + usage: NotApplicable("we don't support advice") }, Record { config: "fetch.output", diff --git a/src/porcelain/main.rs b/src/porcelain/main.rs index a622571485e..95645d843d5 100644 --- a/src/porcelain/main.rs +++ b/src/porcelain/main.rs @@ -3,9 +3,9 @@ use std::sync::{ Arc, }; +use crate::shared::pretty::prepare_and_run; use anyhow::{anyhow, Result}; use clap::{CommandFactory, Parser}; -use gitoxide::shared::pretty::prepare_and_run; use gitoxide_core as core; use crate::porcelain::options::{Args, Subcommands}; @@ -41,7 +41,7 @@ pub fn main() -> Result<()> { verbose, progress, progress_keep_open, - gitoxide::shared::STANDARD_RANGE, + crate::shared::STANDARD_RANGE, move |_progress, _out, _err| panic!("something went very wrong"), ), Subcommands::Init { directory } => core::repository::init(directory).map(|_| ()), @@ -61,12 +61,12 @@ pub fn main() -> Result<()> { verbose, progress, progress_keep_open, - gitoxide::shared::STANDARD_RANGE, - move |mut progress, out, mut err| { + crate::shared::STANDARD_RANGE, + move |mut progress, out, err| { let engine = query::prepare( &repo_dir, &mut progress, - &mut err, + &mut *err, query::Options { object_cache_size_mb, find_copies_harder, @@ -101,7 +101,7 @@ pub fn main() -> Result<()> { verbose, progress, progress_keep_open, - gitoxide::shared::STANDARD_RANGE, + crate::shared::STANDARD_RANGE, move |progress, out, _err| { hours::estimate( &working_dir, @@ -128,7 +128,7 @@ pub fn main() -> Result<()> { verbose, progress, progress_keep_open, - gitoxide::shared::STANDARD_RANGE, + crate::shared::STANDARD_RANGE, move |progress, out, _err| { organize::discover( root.unwrap_or_else(|| [std::path::Component::CurDir].iter().collect()), @@ -152,7 +152,7 @@ pub fn main() -> Result<()> { verbose, progress, progress_keep_open, - gitoxide::shared::STANDARD_RANGE, + crate::shared::STANDARD_RANGE, move |progress, _out, _err| { organize::run( if execute { diff --git a/src/porcelain/options.rs b/src/porcelain/options.rs index 708ffdd1417..6fee59f0332 100644 --- a/src/porcelain/options.rs +++ b/src/porcelain/options.rs @@ -126,7 +126,7 @@ pub mod tools { #[cfg(feature = "gitoxide-core-tools-query")] pub mod query { - use gitoxide::shared::AsPathSpec; + use crate::shared::AsPathSpec; #[derive(Debug, clap::Subcommand)] pub enum Command { @@ -153,7 +153,7 @@ pub mod tools { #[clap(default_value = ".")] pub working_dir: PathBuf, /// The name of the revision as spec, like 'HEAD' or 'main' at which to start iterating the commit graph. - #[clap(default_value("HEAD"), value_parser = gitoxide::shared::AsBString)] + #[clap(default_value("HEAD"), value_parser = crate::shared::AsBString)] pub rev_spec: BString, /// Ignore github bots which match the `[bot]` search string. #[clap(short = 'b', long)] diff --git a/src/shared.rs b/src/shared.rs index b3d159c853b..8a51aa75993 100644 --- a/src/shared.rs +++ b/src/shared.rs @@ -361,6 +361,28 @@ mod clap { } } + #[derive(Clone)] + pub struct ParseRenameFraction; + + impl TypedValueParser for ParseRenameFraction { + type Value = f32; + + fn parse_ref(&self, cmd: &Command, arg: Option<&Arg>, value: &OsStr) -> Result { + StringValueParser::new() + .try_map(|arg: String| -> Result<_, Box> { + if arg.ends_with('%') { + let val = u32::from_str(&arg[..arg.len() - 1])?; + Ok(val as f32 / 100.0) + } else { + let val = u32::from_str(&arg)?; + let num = format!("0.{val}"); + Ok(f32::from_str(&num)?) + } + }) + .parse_ref(cmd, arg, value) + } + } + #[derive(Clone)] pub struct AsTime; @@ -387,4 +409,36 @@ mod clap { } } } -pub use self::clap::{AsBString, AsHashKind, AsOutputFormat, AsPartialRefName, AsPathSpec, AsTime, CheckPathSpec}; +pub use self::clap::{ + AsBString, AsHashKind, AsOutputFormat, AsPartialRefName, AsPathSpec, AsTime, CheckPathSpec, ParseRenameFraction, +}; + +#[cfg(test)] +mod value_parser_tests { + use super::ParseRenameFraction; + use clap::Parser; + + #[test] + fn rename_fraction() { + #[derive(Debug, clap::Parser)] + pub struct Cmd { + #[clap(long, short='a', value_parser = ParseRenameFraction)] + pub arg: Option>, + } + + let c = Cmd::parse_from(["cmd", "-a"]); + assert_eq!(c.arg, Some(None), "this means we need to fill in the default"); + + let c = Cmd::parse_from(["cmd", "-a=50%"]); + assert_eq!(c.arg, Some(Some(0.5)), "percentages become a fraction"); + + let c = Cmd::parse_from(["cmd", "-a=100%"]); + assert_eq!(c.arg, Some(Some(1.0))); + + let c = Cmd::parse_from(["cmd", "-a=5"]); + assert_eq!(c.arg, Some(Some(0.5)), "another way to specify fractions"); + + let c = Cmd::parse_from(["cmd", "-a=75"]); + assert_eq!(c.arg, Some(Some(0.75))); + } +} diff --git a/src/uni.rs b/src/uni.rs index b4a76ad6846..af8a5f97d30 100644 --- a/src/uni.rs +++ b/src/uni.rs @@ -8,9 +8,6 @@ use anyhow::{bail, Result}; -mod plumbing; -mod porcelain; - #[cfg(feature = "pretty-cli")] fn main() -> Result<()> { match std::env::current_exe()? @@ -18,8 +15,8 @@ fn main() -> Result<()> { .and_then(|stem| stem.to_str()) .unwrap_or("gix") { - "gix" => plumbing::main(), - "ein" => porcelain::main(), + "gix" => crate::plumbing::main(), + "ein" => crate::porcelain::main(), unknown => bail!("Executable named '{unknown}' cannot be launched. Exe must be named either `gix` or `ein`."), } } diff --git a/tests/journey/gix.sh b/tests/journey/gix.sh index ce359b134d8..6af7cb507aa 100644 --- a/tests/journey/gix.sh +++ b/tests/journey/gix.sh @@ -345,12 +345,33 @@ title "gix commit-graph" } ) ) + (with "an ambiguous ssh username which could be mistaken for an argument" + snapshot="$snapshot/fail-ambiguous-username" + (with "explicit ssh (true url with scheme)" + it "fails without trying to pass it to command-line programs" && { + WITH_SNAPSHOT="$snapshot/explicit-ssh" \ + expect_run $WITH_FAILURE "$exe_plumbing" free pack receive 'ssh://-Fconfigfile@foo/bar' + } + ) + (with "implicit ssh (special syntax with no scheme)" + it "fails without trying to pass it to command-line programs" && { + WITH_SNAPSHOT="$snapshot/implicit-ssh" \ + expect_run $WITH_FAILURE "$exe_plumbing" free pack receive -- '-Fconfigfile@foo:bar/baz' + } + ) + ) (with "an ambiguous ssh host which could be mistaken for an argument" it "fails without trying to pass it to command-line programs" && { - WITH_SNAPSHOT="$snapshot/fail-ambigous-host" \ + WITH_SNAPSHOT="$snapshot/fail-ambiguous-host" \ expect_run $WITH_FAILURE "$exe_plumbing" free pack receive 'ssh://-oProxyCommand=open$IFS-aCalculator/foo' } ) + (with "an ambiguous ssh path which could be mistaken for an argument" + it "fails without trying to pass it to command-line programs" && { + WITH_SNAPSHOT="$snapshot/fail-ambiguous-path" \ + expect_run $WITH_FAILURE "$exe_plumbing" free pack receive 'git@foo:-oProxyCommand=open$IFS-aCalculator/bar' + } + ) fi ) elif [[ "$kind" = "small" ]]; then @@ -364,12 +385,33 @@ title "gix commit-graph" if test "$kind" = "max" || test "$kind" = "max-pure"; then (with "the 'clone' sub-command" snapshot="$snapshot/clone" + (with "an ambiguous ssh username which could be mistaken for an argument" + snapshot="$snapshot/fail-ambiguous-username" + (with "explicit ssh (true url with scheme)" + it "fails without trying to pass it to command-line programs" && { + WITH_SNAPSHOT="$snapshot/explicit-ssh" \ + expect_run $WITH_FAILURE "$exe_plumbing" clone 'ssh://-Fconfigfile@foo/bar' + } + ) + (with "implicit ssh (special syntax with no scheme)" + it "fails without trying to pass it to command-line programs" && { + WITH_SNAPSHOT="$snapshot/implicit-ssh" \ + expect_run $WITH_FAILURE "$exe_plumbing" clone -- '-Fconfigfile@foo:bar/baz' + } + ) + ) (with "an ambiguous ssh host which could be mistaken for an argument" it "fails without trying to pass it to command-line programs" && { - WITH_SNAPSHOT="$snapshot/fail-ambigous-host" \ + WITH_SNAPSHOT="$snapshot/fail-ambiguous-host" \ expect_run $WITH_FAILURE "$exe_plumbing" clone 'ssh://-oProxyCommand=open$IFS-aCalculator/foo' } ) + (with "an ambiguous ssh path which could be mistaken for an argument" + it "fails without trying to pass it to command-line programs" && { + WITH_SNAPSHOT="$snapshot/fail-ambiguous-path" \ + expect_run $WITH_FAILURE "$exe_plumbing" clone 'git@foo:-oProxyCommand=open$IFS-aCalculator/bar' + } + ) ) fi (with "the 'index' sub-command" diff --git a/tests/snapshots/plumbing/no-repo/pack/clone/fail-ambigous-host b/tests/snapshots/plumbing/no-repo/pack/clone/fail-ambiguous-host similarity index 100% rename from tests/snapshots/plumbing/no-repo/pack/clone/fail-ambigous-host rename to tests/snapshots/plumbing/no-repo/pack/clone/fail-ambiguous-host diff --git a/tests/snapshots/plumbing/no-repo/pack/clone/fail-ambiguous-path b/tests/snapshots/plumbing/no-repo/pack/clone/fail-ambiguous-path new file mode 100644 index 00000000000..5ce09d4e1a9 --- /dev/null +++ b/tests/snapshots/plumbing/no-repo/pack/clone/fail-ambiguous-path @@ -0,0 +1 @@ + Error: The repository path '-oProxyCommand=open$IFS-aCalculator/bar' could be mistaken for a command-line argument \ No newline at end of file diff --git a/tests/snapshots/plumbing/no-repo/pack/clone/fail-ambiguous-username/explicit-ssh b/tests/snapshots/plumbing/no-repo/pack/clone/fail-ambiguous-username/explicit-ssh new file mode 100644 index 00000000000..78d252596ad --- /dev/null +++ b/tests/snapshots/plumbing/no-repo/pack/clone/fail-ambiguous-username/explicit-ssh @@ -0,0 +1 @@ + Error: Username '-Fconfigfile' could be mistaken for a command-line argument \ No newline at end of file diff --git a/tests/snapshots/plumbing/no-repo/pack/clone/fail-ambiguous-username/implicit-ssh b/tests/snapshots/plumbing/no-repo/pack/clone/fail-ambiguous-username/implicit-ssh new file mode 100644 index 00000000000..78d252596ad --- /dev/null +++ b/tests/snapshots/plumbing/no-repo/pack/clone/fail-ambiguous-username/implicit-ssh @@ -0,0 +1 @@ + Error: Username '-Fconfigfile' could be mistaken for a command-line argument \ No newline at end of file diff --git a/tests/snapshots/plumbing/no-repo/pack/index/create/output-dir-restore-as-json-success b/tests/snapshots/plumbing/no-repo/pack/index/create/output-dir-restore-as-json-success index b4141a5b2b5..959f1b6bf96 100644 --- a/tests/snapshots/plumbing/no-repo/pack/index/create/output-dir-restore-as-json-success +++ b/tests/snapshots/plumbing/no-repo/pack/index/create/output-dir-restore-as-json-success @@ -55,5 +55,5 @@ "object_hash": "Sha1", "index_path": "" "data_path": "" - "keep_path": "" + "keep_path": null } \ No newline at end of file diff --git a/tests/snapshots/plumbing/no-repo/pack/receive/fail-ambigous-host b/tests/snapshots/plumbing/no-repo/pack/receive/fail-ambiguous-host similarity index 100% rename from tests/snapshots/plumbing/no-repo/pack/receive/fail-ambigous-host rename to tests/snapshots/plumbing/no-repo/pack/receive/fail-ambiguous-host diff --git a/tests/snapshots/plumbing/no-repo/pack/receive/fail-ambiguous-path b/tests/snapshots/plumbing/no-repo/pack/receive/fail-ambiguous-path new file mode 100644 index 00000000000..2d3c844e17f --- /dev/null +++ b/tests/snapshots/plumbing/no-repo/pack/receive/fail-ambiguous-path @@ -0,0 +1 @@ +Error: The repository path '-oProxyCommand=open$IFS-aCalculator/bar' could be mistaken for a command-line argument \ No newline at end of file diff --git a/tests/snapshots/plumbing/no-repo/pack/receive/fail-ambiguous-username/explicit-ssh b/tests/snapshots/plumbing/no-repo/pack/receive/fail-ambiguous-username/explicit-ssh new file mode 100644 index 00000000000..8a9fca8771d --- /dev/null +++ b/tests/snapshots/plumbing/no-repo/pack/receive/fail-ambiguous-username/explicit-ssh @@ -0,0 +1 @@ +Error: Username '-Fconfigfile' could be mistaken for a command-line argument \ No newline at end of file diff --git a/tests/snapshots/plumbing/no-repo/pack/receive/fail-ambiguous-username/implicit-ssh b/tests/snapshots/plumbing/no-repo/pack/receive/fail-ambiguous-username/implicit-ssh new file mode 100644 index 00000000000..8a9fca8771d --- /dev/null +++ b/tests/snapshots/plumbing/no-repo/pack/receive/fail-ambiguous-username/implicit-ssh @@ -0,0 +1 @@ +Error: Username '-Fconfigfile' could be mistaken for a command-line argument \ No newline at end of file diff --git a/tests/tools/Cargo.toml b/tests/tools/Cargo.toml index 1593c4969c2..b9f396b2615 100644 --- a/tests/tools/Cargo.toml +++ b/tests/tools/Cargo.toml @@ -18,8 +18,8 @@ gix-lock = "10" gix-discover = "0.26" gix-ignore = "0.8" gix-worktree = "0.26" -gix-fs = { version = "^0.10.0", path = "../../gix-fs" } -gix-tempfile = { version = "^13.0.0", default-features = false, features = ["signals"], path = "../../gix-tempfile" } +gix-fs = { version = "^0.11.1", path = "../../gix-fs" } +gix-tempfile = { version = "^14.0.0", default-features = false, features = ["signals"], path = "../../gix-tempfile" } winnow = { version = "0.6.0", features = ["simd"] } fastrand = "2.0.0" diff --git a/tests/tools/src/lib.rs b/tests/tools/src/lib.rs index 145e24ecd23..a55afde5816 100644 --- a/tests/tools/src/lib.rs +++ b/tests/tools/src/lib.rs @@ -29,7 +29,7 @@ pub use tempfile; /// /// #[test] /// fn this() -> Result { -/// let x: usize = "42".parse()?; +/// let x: usize = "42".parse()?; /// Ok(()) /// /// } @@ -264,8 +264,8 @@ fn fixture_bytes_inner(path: impl AsRef, root: DirectoryRoot) -> Vec { /// If a script result doesn't exist, these will be checked first and extracted if present, which they are by default. /// This behaviour can be prohibited by setting the `GIX_TEST_IGNORE_ARCHIVES` to any value. /// -/// To speed CI up, one can add these archives to the repository. It's absolutely recommended to use `gix-lfs` for that to -/// not bloat the repository size. +/// To speed CI up, one can add these archives to the repository. Since LFS is not currently being used, it is +/// important to check their size first, though in most cases generated archives will not be very large. /// /// #### Disable Archive Creation /// diff --git a/tests/utilities.sh b/tests/utilities.sh index f355042b9fc..c538af332f3 100644 --- a/tests/utilities.sh +++ b/tests/utilities.sh @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash WHITE="$(tput setaf 9 2>/dev/null || echo -n '')" YELLOW="$(tput setaf 3 2>/dev/null || echo -n '')"