Compare commits
No commits in common. "master" and "v0.7.0" have entirely different histories.
|
|
@ -1,8 +0,0 @@
|
|||
[target.aarch64-unknown-linux-gnu]
|
||||
linker = "aarch64-linux-gnu-gcc"
|
||||
|
||||
[target.arm-unknown-linux-gnueabihf]
|
||||
linker = "arm-linux-gnueabihf-gcc"
|
||||
|
||||
[target.armv7-unknown-linux-gnueabihf]
|
||||
linker = "arm-linux-gnueabihf-gcc"
|
||||
|
|
@ -1,2 +0,0 @@
|
|||
msrv = "1.88.0"
|
||||
cognitive-complexity-threshold = 18
|
||||
|
|
@ -1,3 +0,0 @@
|
|||
root = true
|
||||
[*.rs]
|
||||
indent_style = tab
|
||||
2
.github/FUNDING.yml
vendored
|
|
@ -1 +1 @@
|
|||
github: extrawurst
|
||||
github: extrawurst
|
||||
|
|
|
|||
10
.github/ISSUE_TEMPLATE/bug_report.md
vendored
|
|
@ -2,7 +2,7 @@
|
|||
name: Bug report
|
||||
about: Create a report to help us improve
|
||||
title: ''
|
||||
labels: 'bug'
|
||||
labels: ''
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
|
@ -23,10 +23,10 @@ A clear and concise description of what you expected to happen.
|
|||
**Screenshots**
|
||||
If applicable, add screenshots to help explain your problem.
|
||||
|
||||
**Context (please complete the following information):**
|
||||
- OS/Distro + Version: [e.g. `macOS 10.15.5`]
|
||||
- GitUI Version [e.g. `0.5`]
|
||||
- Rust version: [e.g `1.44`]
|
||||
**Desktop (please complete the following information):**
|
||||
- OS: [e.g. iOS]
|
||||
- Browser [e.g. chrome, safari]
|
||||
- Version [e.g. 22]
|
||||
|
||||
**Additional context**
|
||||
Add any other context about the problem here.
|
||||
|
|
|
|||
2
.github/ISSUE_TEMPLATE/feature_request.md
vendored
|
|
@ -2,7 +2,7 @@
|
|||
name: Feature request
|
||||
about: Suggest an idea for this project
|
||||
title: ''
|
||||
labels: 'feature-request'
|
||||
labels: ''
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
|
|
|||
16
.github/PULL_REQUEST_TEMPLATE.md
vendored
|
|
@ -1,16 +0,0 @@
|
|||
<!---
|
||||
Thank you for contributing to GitUI! Please fill out the template below, and remove or add any
|
||||
information as you feel necessary.
|
||||
--->
|
||||
|
||||
This Pull Request fixes/closes #{issue_num}.
|
||||
|
||||
It changes the following:
|
||||
-
|
||||
-
|
||||
|
||||
I followed the checklist:
|
||||
- [ ] I added unittests
|
||||
- [ ] I ran `make check` without errors
|
||||
- [ ] I tested the overall application
|
||||
- [ ] I added an appropriate item to the changelog
|
||||
16
.github/dependabot.yml
vendored
|
|
@ -1,16 +0,0 @@
|
|||
version: 2
|
||||
updates:
|
||||
- package-ecosystem: cargo
|
||||
directory: "/"
|
||||
schedule:
|
||||
interval: daily
|
||||
open-pull-requests-limit: 10
|
||||
groups:
|
||||
cargo-minor:
|
||||
patterns: ["*"]
|
||||
update-types:
|
||||
- 'minor'
|
||||
cargo-patch:
|
||||
patterns: ["*"]
|
||||
update-types:
|
||||
- 'patch'
|
||||
11
.github/stale.yml
vendored
|
|
@ -1,18 +1,17 @@
|
|||
# Number of days of inactivity before an issue becomes stale
|
||||
daysUntilStale: 180
|
||||
daysUntilStale: 90
|
||||
# Number of days of inactivity before a stale issue is closed
|
||||
daysUntilClose: 14
|
||||
daysUntilClose: 7
|
||||
# Issues with these labels will never be considered stale
|
||||
exemptLabels:
|
||||
- pinned
|
||||
- security
|
||||
- nostale
|
||||
# Label to use when marking an issue as stale
|
||||
staleLabel: dormant
|
||||
staleLabel: wontfix
|
||||
# Comment to post when marking an issue as stale. Set to `false` to disable
|
||||
markComment: >
|
||||
This issue has been automatically marked as stale because it has not had
|
||||
any activity half a year. It will be closed in 14 days if no further activity occurs. Thank you
|
||||
recent activity. It will be closed if no further activity occurs. Thank you
|
||||
for your contributions.
|
||||
# Comment to post when closing a stale issue. Set to `false` to disable
|
||||
closeComment: false
|
||||
closeComment: false
|
||||
23
.github/workflows/brew.yml
vendored
|
|
@ -1,23 +0,0 @@
|
|||
name: brew update
|
||||
|
||||
on:
|
||||
# only manually
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
tag-name:
|
||||
required: true
|
||||
description: 'release tag'
|
||||
|
||||
jobs:
|
||||
update_brew:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Bump homebrew-core formula
|
||||
uses: mislav/bump-homebrew-formula-action@v3
|
||||
env:
|
||||
COMMITTER_TOKEN: ${{ secrets.BREW_TOKEN }}
|
||||
with:
|
||||
formula-name: gitui
|
||||
# https://github.com/mislav/bump-homebrew-formula-action/issues/58
|
||||
formula-path: Formula/g/gitui.rb
|
||||
tag-name: ${{ github.event.inputs.tag-name }}
|
||||
183
.github/workflows/cd.yml
vendored
|
|
@ -2,133 +2,82 @@ name: CD
|
|||
|
||||
on:
|
||||
push:
|
||||
tags:
|
||||
- "*"
|
||||
workflow_dispatch:
|
||||
|
||||
permissions:
|
||||
contents: write
|
||||
tags:
|
||||
- '*'
|
||||
|
||||
jobs:
|
||||
release:
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
os: [ubuntu-latest, macos-latest, windows-latest, ubuntu-22.04]
|
||||
os: [ubuntu-latest, macos-latest, windows-latest]
|
||||
|
||||
runs-on: ${{ matrix.os }}
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v2
|
||||
- name: Get version
|
||||
id: get_version
|
||||
run: echo ::set-output name=version::${GITHUB_REF/refs\/tags\//}
|
||||
|
||||
- name: Get version
|
||||
id: get_version
|
||||
run: echo "version=${GITHUB_REF/refs\/tags\//}" >> $GITHUB_OUTPUT
|
||||
- name: Install Rust
|
||||
uses: actions-rs/toolchain@v1
|
||||
with:
|
||||
toolchain: stable
|
||||
profile: minimal
|
||||
components: clippy
|
||||
|
||||
- name: Restore cargo cache
|
||||
uses: Swatinem/rust-cache@v2
|
||||
env:
|
||||
cache-name: ci
|
||||
with:
|
||||
shared-key: ${{ matrix.os }}-${{ env.cache-name }}-stable
|
||||
- name: Build
|
||||
run: cargo build
|
||||
- name: Run tests
|
||||
run: make test
|
||||
- name: Run clippy
|
||||
run: |
|
||||
cargo clean
|
||||
make clippy
|
||||
|
||||
- name: Install Rust
|
||||
uses: dtolnay/rust-toolchain@stable
|
||||
with:
|
||||
components: clippy
|
||||
- name: Setup MUSL
|
||||
if: matrix.os == 'ubuntu-latest'
|
||||
run: |
|
||||
rustup target add x86_64-unknown-linux-musl
|
||||
sudo apt-get -qq install musl-tools
|
||||
|
||||
- name: Build Release Mac
|
||||
if: matrix.os == 'macos-latest'
|
||||
run: make release-mac
|
||||
- name: Build Release Linux
|
||||
if: matrix.os == 'ubuntu-latest'
|
||||
run: make release-linux-musl
|
||||
- name: Build Release Win
|
||||
if: matrix.os == 'windows-latest'
|
||||
run: make release-win
|
||||
|
||||
- uses: taiki-e/install-action@nextest
|
||||
- name: Set SHA
|
||||
if: matrix.os == 'macos-latest'
|
||||
id: shasum
|
||||
run: |
|
||||
echo ::set-output name=sha::"$(shasum -a 256 ./release/gitui-mac.tar.gz | awk '{printf $1}')"
|
||||
|
||||
- name: Build
|
||||
if: matrix.os != 'ubuntu-22.04'
|
||||
env:
|
||||
GITUI_RELEASE: 1
|
||||
run: cargo build
|
||||
- name: Run tests
|
||||
if: matrix.os != 'ubuntu-22.04'
|
||||
run: make test
|
||||
- name: Run clippy
|
||||
if: matrix.os != 'ubuntu-22.04'
|
||||
run: |
|
||||
cargo clean
|
||||
make clippy
|
||||
- name: Extract release notes
|
||||
if: matrix.os == 'ubuntu-latest'
|
||||
id: release_notes
|
||||
uses: ffurrer2/extract-release-notes@v1
|
||||
- name: Release
|
||||
uses: softprops/action-gh-release@v1
|
||||
with:
|
||||
body: ${{ steps.release_notes.outputs.release_notes }}
|
||||
prerelease: false
|
||||
files: |
|
||||
./release/*.tar.gz
|
||||
./release/*.zip
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: Setup MUSL
|
||||
if: matrix.os == 'ubuntu-latest'
|
||||
run: |
|
||||
rustup target add x86_64-unknown-linux-musl
|
||||
sudo apt-get -qq install musl-tools
|
||||
|
||||
- name: Setup ARM toolchain
|
||||
if: matrix.os == 'ubuntu-22.04'
|
||||
run: |
|
||||
rustup target add aarch64-unknown-linux-gnu
|
||||
rustup target add armv7-unknown-linux-gnueabihf
|
||||
rustup target add arm-unknown-linux-gnueabihf
|
||||
|
||||
curl -o $GITHUB_WORKSPACE/aarch64.tar.xz https://armkeil.blob.core.windows.net/developer/Files/downloads/gnu-a/8.2-2018.08/gcc-arm-8.2-2018.08-x86_64-aarch64-linux-gnu.tar.xz
|
||||
curl -o $GITHUB_WORKSPACE/arm.tar.xz https://armkeil.blob.core.windows.net/developer/Files/downloads/gnu-a/8.2-2018.08/gcc-arm-8.2-2018.08-x86_64-arm-linux-gnueabihf.tar.xz
|
||||
|
||||
tar xf $GITHUB_WORKSPACE/aarch64.tar.xz
|
||||
tar xf $GITHUB_WORKSPACE/arm.tar.xz
|
||||
|
||||
echo "$GITHUB_WORKSPACE/gcc-arm-8.2-2018.08-x86_64-aarch64-linux-gnu/bin" >> $GITHUB_PATH
|
||||
echo "$GITHUB_WORKSPACE/gcc-arm-8.2-2018.08-x86_64-arm-linux-gnueabihf/bin" >> $GITHUB_PATH
|
||||
|
||||
- name: Build Release Mac
|
||||
if: matrix.os == 'macos-latest'
|
||||
env:
|
||||
GITUI_RELEASE: 1
|
||||
run: make release-mac
|
||||
- name: Build Release Mac x86
|
||||
if: matrix.os == 'macos-latest'
|
||||
env:
|
||||
GITUI_RELEASE: 1
|
||||
run: |
|
||||
rustup target add x86_64-apple-darwin
|
||||
make release-mac-x86
|
||||
- name: Build Release Linux
|
||||
if: matrix.os == 'ubuntu-latest'
|
||||
env:
|
||||
GITUI_RELEASE: 1
|
||||
run: make release-linux-musl
|
||||
- name: Build Release Win
|
||||
if: matrix.os == 'windows-latest'
|
||||
env:
|
||||
GITUI_RELEASE: 1
|
||||
run: make release-win
|
||||
- name: Build Release Linux ARM
|
||||
if: matrix.os == 'ubuntu-22.04'
|
||||
env:
|
||||
GITUI_RELEASE: 1
|
||||
run: make release-linux-arm
|
||||
|
||||
- name: Set SHA
|
||||
if: matrix.os == 'macos-latest'
|
||||
id: shasum
|
||||
run: |
|
||||
echo sha="$(shasum -a 256 ./release/gitui-mac.tar.gz | awk '{printf $1}')" >> $GITHUB_OUTPUT
|
||||
|
||||
- name: Extract release notes
|
||||
if: matrix.os == 'ubuntu-latest'
|
||||
id: release_notes
|
||||
uses: ffurrer2/extract-release-notes@v2
|
||||
|
||||
- name: Release
|
||||
uses: softprops/action-gh-release@v2
|
||||
with:
|
||||
body: ${{ steps.release_notes.outputs.release_notes }}
|
||||
prerelease: ${{ contains(github.ref, '-') }}
|
||||
files: |
|
||||
./release/*.tar.gz
|
||||
./release/*.zip
|
||||
./release/*.msi
|
||||
|
||||
- name: Bump homebrew-core formula
|
||||
uses: mislav/bump-homebrew-formula-action@v3
|
||||
if: "matrix.os == 'macos-latest' && !contains(github.ref, '-')" # skip prereleases
|
||||
env:
|
||||
COMMITTER_TOKEN: ${{ secrets.BREW_TOKEN }}
|
||||
with:
|
||||
formula-name: gitui
|
||||
# https://github.com/mislav/bump-homebrew-formula-action/issues/58
|
||||
formula-path: Formula/g/gitui.rb
|
||||
- name: Bump Brew
|
||||
if: matrix.os == 'macos-latest'
|
||||
env:
|
||||
HOMEBREW_GITHUB_API_TOKEN: ${{ secrets.BREW_TOKEN }}
|
||||
run: |
|
||||
brew tap extrawurst/tap
|
||||
brew bump-formula-pr -f --version=${{ steps.get_version.outputs.version }} --no-browse --no-audit \
|
||||
--sha256=${{ steps.shasum.outputs.sha }} \
|
||||
--url="https://github.com/extrawurst/gitui/releases/download/${{ steps.get_version.outputs.version }}/gitui-mac.tar.gz" \
|
||||
extrawurst/tap/gitui
|
||||
355
.github/workflows/ci.yml
vendored
|
|
@ -2,324 +2,97 @@ name: CI
|
|||
|
||||
on:
|
||||
schedule:
|
||||
- cron: "0 2 * * *"
|
||||
- cron: '0 2 * * *' # run at 2 AM UTC
|
||||
push:
|
||||
branches: ["*"]
|
||||
branches: [ '*' ]
|
||||
pull_request:
|
||||
branches: [master]
|
||||
|
||||
env:
|
||||
CARGO_TERM_COLOR: always
|
||||
branches: [ master ]
|
||||
|
||||
jobs:
|
||||
build:
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
os: [ubuntu-latest, macos-latest, windows-latest]
|
||||
rust: [nightly, stable, "1.88"]
|
||||
os: [ubuntu-latest, windows-latest, macos-latest]
|
||||
|
||||
runs-on: ${{ matrix.os }}
|
||||
continue-on-error: ${{ matrix.rust == 'nightly' }}
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v2
|
||||
|
||||
- name: Restore cargo cache
|
||||
uses: Swatinem/rust-cache@v2
|
||||
env:
|
||||
cache-name: ci
|
||||
with:
|
||||
shared-key: ${{ matrix.os }}-${{ env.cache-name }}-${{ matrix.rust }}
|
||||
- name: Install Rust
|
||||
uses: actions-rs/toolchain@v1
|
||||
with:
|
||||
toolchain: stable
|
||||
profile: minimal
|
||||
components: clippy
|
||||
|
||||
- name: MacOS Workaround
|
||||
if: matrix.os == 'macos-latest'
|
||||
run: cargo clean -p serde_derive -p thiserror
|
||||
- name: Build Debug
|
||||
run: |
|
||||
rustc --version
|
||||
cargo build
|
||||
|
||||
- name: Install Rust
|
||||
uses: dtolnay/rust-toolchain@master
|
||||
with:
|
||||
toolchain: ${{ matrix.rust }}
|
||||
components: clippy
|
||||
- name: Run tests
|
||||
run: make test
|
||||
|
||||
- name: Override rust toolchain
|
||||
run: rustup override set ${{ matrix.rust }}
|
||||
|
||||
- name: Rustup Show
|
||||
run: rustup show
|
||||
|
||||
- uses: taiki-e/install-action@nextest
|
||||
|
||||
- name: Build Debug
|
||||
run: |
|
||||
cargo build
|
||||
|
||||
- name: Run tests
|
||||
run: make test
|
||||
|
||||
- name: Run clippy
|
||||
run: |
|
||||
make clippy
|
||||
|
||||
- name: Build Release
|
||||
run: make build-release
|
||||
|
||||
- name: Test Install
|
||||
run: cargo install --path "." --force --locked
|
||||
|
||||
- name: Binary Size (unix)
|
||||
if: matrix.os != 'windows-latest'
|
||||
run: |
|
||||
ls -l ./target/release/gitui
|
||||
|
||||
- name: Binary Size (win)
|
||||
if: matrix.os == 'windows-latest'
|
||||
run: |
|
||||
ls -l ./target/release/gitui.exe
|
||||
|
||||
- name: Binary dependencies (mac)
|
||||
if: matrix.os == 'macos-latest'
|
||||
run: |
|
||||
otool -L ./target/release/gitui
|
||||
|
||||
- name: Build MSI (windows)
|
||||
if: matrix.os == 'windows-latest'
|
||||
run: |
|
||||
cargo install cargo-wix --version 0.3.3 --locked
|
||||
cargo wix --version
|
||||
cargo wix -p gitui --no-build --nocapture --output ./target/wix/gitui-win.msi
|
||||
ls -l ./target/wix/gitui-win.msi
|
||||
- name: Run clippy
|
||||
run: |
|
||||
cargo clean
|
||||
make clippy
|
||||
- name: Build Release
|
||||
run: make build-release
|
||||
|
||||
build-linux-musl:
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
rust: [nightly, stable, "1.88"]
|
||||
continue-on-error: ${{ matrix.rust == 'nightly' }}
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v2
|
||||
- name: Install Rust
|
||||
uses: actions-rs/toolchain@v1
|
||||
with:
|
||||
toolchain: stable
|
||||
profile: minimal
|
||||
target: x86_64-unknown-linux-musl
|
||||
|
||||
- name: Restore cargo cache
|
||||
uses: Swatinem/rust-cache@v2
|
||||
env:
|
||||
cache-name: ci
|
||||
with:
|
||||
key: ubuntu-latest-${{ env.cache-name }}-${{ matrix.rust }}
|
||||
- name: Setup MUSL
|
||||
run: |
|
||||
sudo apt-get -qq install musl-tools
|
||||
- name: Build Debug
|
||||
run: cargo build --target=x86_64-unknown-linux-musl
|
||||
- name: Build Release
|
||||
run: |
|
||||
cargo build --release --target=x86_64-unknown-linux-musl
|
||||
|
||||
- name: Install Rust
|
||||
uses: dtolnay/rust-toolchain@master
|
||||
with:
|
||||
toolchain: ${{ matrix.rust }}
|
||||
targets: x86_64-unknown-linux-musl
|
||||
|
||||
# The build would fail without manually installing the target.
|
||||
# https://github.com/dtolnay/rust-toolchain/issues/83
|
||||
- name: Manually install target
|
||||
run: rustup target add x86_64-unknown-linux-musl
|
||||
|
||||
- name: Override rust toolchain
|
||||
run: rustup override set ${{ matrix.rust }}
|
||||
|
||||
- name: Rustup Show
|
||||
run: rustup show
|
||||
|
||||
- uses: taiki-e/install-action@nextest
|
||||
|
||||
- name: Setup MUSL
|
||||
run: |
|
||||
sudo apt-get -qq install musl-tools
|
||||
- name: Build Debug
|
||||
run: |
|
||||
make build-linux-musl-debug
|
||||
./target/x86_64-unknown-linux-musl/debug/gitui --version
|
||||
- name: Build Release
|
||||
run: |
|
||||
make build-linux-musl-release
|
||||
./target/x86_64-unknown-linux-musl/release/gitui --version
|
||||
ls -l ./target/x86_64-unknown-linux-musl/release/gitui
|
||||
- name: Test
|
||||
run: |
|
||||
make test-linux-musl
|
||||
- name: Test Install
|
||||
run: cargo install --path "." --force --locked
|
||||
|
||||
build-linux-arm:
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
rust: [nightly, stable, "1.88"]
|
||||
continue-on-error: ${{ matrix.rust == 'nightly' }}
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Restore cargo cache
|
||||
uses: Swatinem/rust-cache@v2
|
||||
env:
|
||||
cache-name: ci
|
||||
with:
|
||||
key: ubuntu-latest-${{ env.cache-name }}-${{ matrix.rust }}
|
||||
|
||||
- name: Install Rust
|
||||
uses: dtolnay/rust-toolchain@master
|
||||
with:
|
||||
toolchain: ${{ matrix.rust }}
|
||||
|
||||
- name: Override rust toolchain
|
||||
run: rustup override set ${{ matrix.rust }}
|
||||
|
||||
- name: Setup ARM toolchain
|
||||
run: |
|
||||
rustup target add aarch64-unknown-linux-gnu
|
||||
rustup target add armv7-unknown-linux-gnueabihf
|
||||
rustup target add arm-unknown-linux-gnueabihf
|
||||
|
||||
curl -o $GITHUB_WORKSPACE/aarch64.tar.xz https://armkeil.blob.core.windows.net/developer/Files/downloads/gnu-a/8.2-2018.08/gcc-arm-8.2-2018.08-x86_64-aarch64-linux-gnu.tar.xz
|
||||
curl -o $GITHUB_WORKSPACE/arm.tar.xz https://armkeil.blob.core.windows.net/developer/Files/downloads/gnu-a/8.2-2018.08/gcc-arm-8.2-2018.08-x86_64-arm-linux-gnueabihf.tar.xz
|
||||
|
||||
tar xf $GITHUB_WORKSPACE/aarch64.tar.xz
|
||||
tar xf $GITHUB_WORKSPACE/arm.tar.xz
|
||||
|
||||
echo "$GITHUB_WORKSPACE/gcc-arm-8.2-2018.08-x86_64-aarch64-linux-gnu/bin" >> $GITHUB_PATH
|
||||
echo "$GITHUB_WORKSPACE/gcc-arm-8.2-2018.08-x86_64-arm-linux-gnueabihf/bin" >> $GITHUB_PATH
|
||||
|
||||
- name: Rustup Show
|
||||
run: rustup show
|
||||
|
||||
- name: Build Debug
|
||||
run: |
|
||||
make build-linux-arm-debug
|
||||
- name: Build Release
|
||||
run: |
|
||||
make build-linux-arm-release
|
||||
ls -l ./target/aarch64-unknown-linux-gnu/release/gitui || ls -l ./target/armv7-unknown-linux-gnueabihf/release/gitui || ls -l ./target/arm-unknown-linux-gnueabihf/release/gitui
|
||||
|
||||
build-apple-x86:
|
||||
runs-on: macos-latest
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
rust: [nightly, stable, "1.88"]
|
||||
continue-on-error: ${{ matrix.rust == 'nightly' }}
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Restore cargo cache
|
||||
uses: Swatinem/rust-cache@v2
|
||||
env:
|
||||
cache-name: ci
|
||||
with:
|
||||
key: apple-x86-${{ env.cache-name }}-${{ matrix.rust }}
|
||||
|
||||
- name: Install Rust
|
||||
uses: dtolnay/rust-toolchain@master
|
||||
with:
|
||||
toolchain: ${{ matrix.rust }}
|
||||
|
||||
- name: Override rust toolchain
|
||||
run: rustup override set ${{ matrix.rust }}
|
||||
|
||||
- name: Setup target
|
||||
run: rustup target add x86_64-apple-darwin
|
||||
|
||||
- name: Rustup Show
|
||||
run: rustup show
|
||||
|
||||
- name: Build Debug
|
||||
run: |
|
||||
make build-apple-x86-debug
|
||||
- name: Build Release
|
||||
run: |
|
||||
make build-apple-x86-release
|
||||
ls -l ./target/x86_64-apple-darwin/release/gitui
|
||||
|
||||
linting:
|
||||
name: Lints
|
||||
rustfmt:
|
||||
name: Rustfmt
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@master
|
||||
- name: Install Rust
|
||||
uses: actions-rs/toolchain@v1
|
||||
with:
|
||||
toolchain: stable
|
||||
components: rustfmt
|
||||
- run: cargo fmt -- --check
|
||||
|
||||
- name: Restore cargo cache
|
||||
uses: Swatinem/rust-cache@v2
|
||||
env:
|
||||
cache-name: ci
|
||||
with:
|
||||
key: ubuntu-latest-${{ env.cache-name }}-stable
|
||||
|
||||
- name: Install Rust
|
||||
uses: dtolnay/rust-toolchain@stable
|
||||
with:
|
||||
components: rustfmt
|
||||
|
||||
- run: cargo fmt -- --check
|
||||
|
||||
- name: tombi install
|
||||
uses: tombi-toml/setup-tombi@v1
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
with:
|
||||
version: '0.9.0'
|
||||
|
||||
- name: tombi check
|
||||
run: |
|
||||
tombi format --check
|
||||
|
||||
- name: cargo-deny install
|
||||
run: |
|
||||
cargo install --locked cargo-deny
|
||||
|
||||
- name: cargo-deny checks
|
||||
run: |
|
||||
cargo deny check
|
||||
|
||||
udeps:
|
||||
name: udeps
|
||||
sec:
|
||||
name: Security audit
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Restore cargo cache
|
||||
uses: Swatinem/rust-cache@v2
|
||||
env:
|
||||
cache-name: ci
|
||||
with:
|
||||
key: ubuntu-latest-${{ env.cache-name }}-nightly
|
||||
|
||||
- name: Install Rust
|
||||
uses: dtolnay/rust-toolchain@nightly
|
||||
|
||||
- name: build cargo-udeps
|
||||
run: cargo install --git https://github.com/est31/cargo-udeps --locked
|
||||
|
||||
- name: run cargo-udeps
|
||||
run: cargo +nightly udeps --all-targets
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions-rs/audit-check@v1
|
||||
with:
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
log-test:
|
||||
name: Changelog Test
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Extract release notes
|
||||
id: extract_release_notes
|
||||
uses: ffurrer2/extract-release-notes@v2
|
||||
with:
|
||||
release_notes_file: ./release-notes.txt
|
||||
- uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: release-notes.txt
|
||||
path: ./release-notes.txt
|
||||
|
||||
test-homebrew:
|
||||
name: Test Homebrew Formula (macOS)
|
||||
runs-on: macos-latest
|
||||
steps:
|
||||
- name: Set up Homebrew
|
||||
uses: Homebrew/actions/setup-homebrew@master
|
||||
|
||||
- name: Install stable Rust
|
||||
uses: actions-rs/toolchain@v1
|
||||
with:
|
||||
toolchain: stable
|
||||
|
||||
- name: Let Homebrew build gitui from source
|
||||
run: brew install --build-from-source gitui
|
||||
- uses: actions/checkout@master
|
||||
- name: Extract release notes
|
||||
id: extract_release_notes
|
||||
uses: ffurrer2/extract-release-notes@v1
|
||||
with:
|
||||
release_notes_file: ./release-notes.txt
|
||||
- uses: actions/upload-artifact@v1
|
||||
with:
|
||||
name: release-notes.txt
|
||||
path: ./release-notes.txt
|
||||
125
.github/workflows/nightly.yml
vendored
|
|
@ -1,125 +0,0 @@
|
|||
name: Build Nightly Releases
|
||||
|
||||
on:
|
||||
schedule:
|
||||
- cron: "0 3 * * *"
|
||||
workflow_dispatch:
|
||||
|
||||
env:
|
||||
CARGO_TERM_COLOR: always
|
||||
AWS_BUCKET_NAME: s3://gitui/nightly/
|
||||
|
||||
jobs:
|
||||
release:
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
os: [ubuntu-latest, macos-latest, windows-latest, ubuntu-22.04]
|
||||
runs-on: ${{ matrix.os }}
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Restore cargo cache
|
||||
uses: Swatinem/rust-cache@v2
|
||||
env:
|
||||
cache-name: ci
|
||||
with:
|
||||
shared-key: ${{ matrix.os }}-${{ env.cache-name }}-stable
|
||||
|
||||
- name: Install Rust
|
||||
uses: dtolnay/rust-toolchain@stable
|
||||
with:
|
||||
components: clippy
|
||||
|
||||
- uses: taiki-e/install-action@nextest
|
||||
|
||||
# ideally we trigger the nightly build/deploy only if the normal nightly CI finished successfully
|
||||
- name: Run tests
|
||||
if: matrix.os != 'ubuntu-22.04'
|
||||
run: make test
|
||||
- name: Run clippy
|
||||
if: matrix.os != 'ubuntu-22.04'
|
||||
run: |
|
||||
cargo clean
|
||||
make clippy
|
||||
|
||||
- name: Setup MUSL
|
||||
if: matrix.os == 'ubuntu-latest'
|
||||
run: |
|
||||
rustup target add x86_64-unknown-linux-musl
|
||||
sudo apt-get -qq install musl-tools
|
||||
|
||||
- name: Setup ARM toolchain
|
||||
if: matrix.os == 'ubuntu-22.04'
|
||||
run: |
|
||||
rustup target add aarch64-unknown-linux-gnu
|
||||
rustup target add armv7-unknown-linux-gnueabihf
|
||||
rustup target add arm-unknown-linux-gnueabihf
|
||||
|
||||
curl -o $GITHUB_WORKSPACE/aarch64.tar.xz https://armkeil.blob.core.windows.net/developer/Files/downloads/gnu-a/8.2-2018.08/gcc-arm-8.2-2018.08-x86_64-aarch64-linux-gnu.tar.xz
|
||||
curl -o $GITHUB_WORKSPACE/arm.tar.xz https://armkeil.blob.core.windows.net/developer/Files/downloads/gnu-a/8.2-2018.08/gcc-arm-8.2-2018.08-x86_64-arm-linux-gnueabihf.tar.xz
|
||||
|
||||
tar xf $GITHUB_WORKSPACE/aarch64.tar.xz
|
||||
tar xf $GITHUB_WORKSPACE/arm.tar.xz
|
||||
|
||||
echo "$GITHUB_WORKSPACE/gcc-arm-8.2-2018.08-x86_64-aarch64-linux-gnu/bin" >> $GITHUB_PATH
|
||||
echo "$GITHUB_WORKSPACE/gcc-arm-8.2-2018.08-x86_64-arm-linux-gnueabihf/bin" >> $GITHUB_PATH
|
||||
|
||||
- name: Build Release Mac
|
||||
if: matrix.os == 'macos-latest'
|
||||
run: make release-mac
|
||||
- name: Build Release Mac x86
|
||||
if: matrix.os == 'macos-latest'
|
||||
run: |
|
||||
rustup target add x86_64-apple-darwin
|
||||
make release-mac-x86
|
||||
- name: Build Release Linux
|
||||
if: matrix.os == 'ubuntu-latest'
|
||||
run: make release-linux-musl
|
||||
- name: Build Release Win
|
||||
if: matrix.os == 'windows-latest'
|
||||
run: make release-win
|
||||
- name: Build Release Linux ARM
|
||||
if: matrix.os == 'ubuntu-22.04'
|
||||
run: make release-linux-arm
|
||||
|
||||
- name: Ubuntu 22.04 Upload Artifact
|
||||
if: matrix.os == 'ubuntu-22.04'
|
||||
env:
|
||||
AWS_ACCESS_KEY_ID: ${{ secrets.AWS_KEY_ID }}
|
||||
AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_KEY_SECRET }}
|
||||
AWS_DEFAULT_REGION: ${{ secrets.AWS_REGION }}
|
||||
run: |
|
||||
aws s3 cp ./release/gitui-linux-armv7.tar.gz $AWS_BUCKET_NAME
|
||||
aws s3 cp ./release/gitui-linux-arm.tar.gz $AWS_BUCKET_NAME
|
||||
aws s3 cp ./release/gitui-linux-aarch64.tar.gz $AWS_BUCKET_NAME
|
||||
|
||||
- name: Ubuntu Latest Upload Artifact
|
||||
if: matrix.os == 'ubuntu-latest'
|
||||
env:
|
||||
AWS_ACCESS_KEY_ID: ${{ secrets.AWS_KEY_ID }}
|
||||
AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_KEY_SECRET }}
|
||||
AWS_DEFAULT_REGION: ${{ secrets.AWS_REGION }}
|
||||
run: |
|
||||
aws s3 cp ./release/gitui-linux-x86_64.tar.gz $AWS_BUCKET_NAME
|
||||
|
||||
- name: MacOS Upload Artifact
|
||||
if: matrix.os == 'macos-latest'
|
||||
env:
|
||||
AWS_ACCESS_KEY_ID: ${{ secrets.AWS_KEY_ID }}
|
||||
AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_KEY_SECRET }}
|
||||
AWS_DEFAULT_REGION: ${{ secrets.AWS_REGION }}
|
||||
run: |
|
||||
aws s3 cp ./release/gitui-mac.tar.gz $AWS_BUCKET_NAME
|
||||
aws s3 cp ./release/gitui-mac-x86.tar.gz $AWS_BUCKET_NAME
|
||||
|
||||
- name: Windows Upload Artifact
|
||||
if: matrix.os == 'windows-latest'
|
||||
env:
|
||||
AWS_ACCESS_KEY_ID: ${{ secrets.AWS_KEY_ID }}
|
||||
AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_KEY_SECRET }}
|
||||
AWS_DEFAULT_REGION: ${{ secrets.AWS_REGION }}
|
||||
run: |
|
||||
aws s3 cp ./release/gitui-win.msi $env:AWS_BUCKET_NAME
|
||||
aws s3 cp ./release/gitui-win.tar.gz $env:AWS_BUCKET_NAME
|
||||
1
.gitignore
vendored
|
|
@ -2,4 +2,3 @@
|
|||
/release
|
||||
.DS_Store
|
||||
/.idea/
|
||||
flamegraph.svg
|
||||
|
|
|
|||
1
.vscode/settings.json
vendored
|
|
@ -1,4 +1,5 @@
|
|||
{
|
||||
"editor.formatOnSave": true,
|
||||
"workbench.settings.enableNaturalLanguageSearch": false,
|
||||
"telemetry.enableTelemetry": false,
|
||||
}
|
||||
976
CHANGELOG.md
|
|
@ -1,26 +0,0 @@
|
|||
# Contributing
|
||||
|
||||
We’re glad you found this document that is intended to make contributing to
|
||||
GitUI as easy as possible!
|
||||
|
||||
## Building GitUI
|
||||
|
||||
In order to build GitUI on your machine, follow the instructions in the
|
||||
[“Build” section](./README.md#build).
|
||||
|
||||
## Getting help
|
||||
|
||||
There’s a [Discord server][discord-server] you can join if you get stuck or
|
||||
don’t know where to start. People are happy to answer any questions you might
|
||||
have!
|
||||
|
||||
## Getting started
|
||||
|
||||
If you are looking for something to work on, but don’t yet know what might be a
|
||||
good first issue, you can take a look at [issues labelled with
|
||||
`good-first-issue`][good-first-issues]. They have been selected to not require
|
||||
too much context so that people not familiar with the codebase yet can still
|
||||
make a contribution.
|
||||
|
||||
[discord-server]: https://discord.gg/rZv4uxSQx3
|
||||
[good-first-issues]: https://github.com/gitui-org/gitui/issues?q=is%3Aissue+is%3Aopen+label%3A%22good+first+issue%22
|
||||
4969
Cargo.lock
generated
129
Cargo.toml
|
|
@ -1,110 +1,55 @@
|
|||
[package]
|
||||
name = "gitui"
|
||||
version = "0.28.1"
|
||||
authors = ["extrawurst <mail@rusticorn.com>"]
|
||||
version = "0.7.0"
|
||||
authors = ["Stephan Dilly <dilly.stephan@gmail.com>"]
|
||||
description = "blazing fast terminal-ui for git"
|
||||
edition = "2021"
|
||||
rust-version = "1.88"
|
||||
exclude = [".github/*", ".vscode/*", "assets/*"]
|
||||
homepage = "https://github.com/gitui-org/gitui"
|
||||
repository = "https://github.com/gitui-org/gitui"
|
||||
edition = "2018"
|
||||
exclude = [".github/*",".vscode/*"]
|
||||
homepage = "https://github.com/extrawurst/gitui"
|
||||
repository = "https://github.com/extrawurst/gitui"
|
||||
readme = "README.md"
|
||||
license = "MIT"
|
||||
categories = ["command-line-utilities"]
|
||||
keywords = ["cli", "git", "gui", "terminal", "ui"]
|
||||
build = "build.rs"
|
||||
|
||||
[workspace]
|
||||
members = [
|
||||
"asyncgit",
|
||||
"filetreelist",
|
||||
"git2-hooks",
|
||||
"git2-testing",
|
||||
"scopetime",
|
||||
keywords = [
|
||||
"git",
|
||||
"gui",
|
||||
"cli",
|
||||
"terminal",
|
||||
"ui",
|
||||
]
|
||||
|
||||
[dependencies]
|
||||
anyhow = "1.0"
|
||||
asyncgit = { path = "./asyncgit", version = "0.28.1", default-features = false }
|
||||
backtrace = "0.3"
|
||||
base64 = "0.22"
|
||||
bitflags = "2.10"
|
||||
bugreport = "0.5.1"
|
||||
bwrap = { version = "1.3", features = ["use_std"] }
|
||||
bytesize = { version = "2.3", default-features = false }
|
||||
chrono = { version = "0.4", default-features = false, features = ["clock"] }
|
||||
clap = { version = "4.5", features = ["cargo", "env"] }
|
||||
crossbeam-channel = "0.5"
|
||||
crossterm = { version = "0.29", features = ["serde"] }
|
||||
dirs = "6.0"
|
||||
easy-cast = "0.5"
|
||||
filetreelist = { path = "./filetreelist", version = ">=0.6" }
|
||||
fuzzy-matcher = "0.3"
|
||||
gh-emoji = { version = "1.0", optional = true }
|
||||
indexmap = "2"
|
||||
itertools = "0.14"
|
||||
log = "0.4"
|
||||
notify = "8"
|
||||
notify-debouncer-mini = "0.7"
|
||||
once_cell = "1"
|
||||
parking_lot_core = "0.9"
|
||||
ratatui = { version = "0.30", default-features = false, features = [
|
||||
"crossterm",
|
||||
"serde",
|
||||
] }
|
||||
ratatui-textarea = "0.8"
|
||||
rayon-core = "1.13"
|
||||
ron = "0.12"
|
||||
scopeguard = "1.2"
|
||||
scopetime = { path = "./scopetime", version = "0.1" }
|
||||
asyncgit = { path = "./asyncgit", version = "0.7" }
|
||||
crossterm = "0.17"
|
||||
clap = { version = "2.33", default-features = false }
|
||||
tui = { version = "0.9", default-features = false, features = ['crossterm'] }
|
||||
itertools = "0.9"
|
||||
rayon-core = "1.7"
|
||||
log = "0.4"
|
||||
simplelog = { version = "0.8", default-features = false }
|
||||
dirs = "2.0"
|
||||
crossbeam-channel = "0.4"
|
||||
scopeguard = "1.1"
|
||||
bitflags = "1.2"
|
||||
chrono = "0.4"
|
||||
backtrace = "0.3"
|
||||
ron = "0.6"
|
||||
serde = "1.0"
|
||||
shellexpand = "3.1"
|
||||
simplelog = { version = "0.12", default-features = false }
|
||||
struct-patch = "0.10"
|
||||
syntect = { version = "5.3", default-features = false, features = [
|
||||
"default-syntaxes",
|
||||
"default-themes",
|
||||
"html",
|
||||
"parsing",
|
||||
"plist-load",
|
||||
] }
|
||||
two-face = { version = "0.4.4", default-features = false }
|
||||
unicode-segmentation = "1.12"
|
||||
unicode-truncate = "2.0"
|
||||
unicode-width = "0.2"
|
||||
which = "8.0"
|
||||
|
||||
[dev-dependencies]
|
||||
env_logger = "0.11"
|
||||
git2-testing = { path = "./git2-testing" }
|
||||
insta = { version = "1.41.0", features = ["filters"] }
|
||||
pretty_assertions = "1.4"
|
||||
tempfile = "3"
|
||||
|
||||
[build-dependencies]
|
||||
chrono = { version = "0.4", default-features = false, features = ["clock"] }
|
||||
|
||||
[badges]
|
||||
maintenance = { status = "actively-developed" }
|
||||
anyhow = "1.0.31"
|
||||
unicode-width = "0.1"
|
||||
|
||||
[features]
|
||||
default = ["ghemoji", "regex-fancy", "trace-libgit", "vendor-openssl"]
|
||||
ghemoji = ["gh-emoji"]
|
||||
# regex-* features are mutually exclusive.
|
||||
regex-fancy = ["syntect/regex-fancy", "two-face/syntect-fancy"]
|
||||
regex-onig = ["syntect/regex-onig", "two-face/syntect-onig"]
|
||||
timing = ["scopetime/enabled"]
|
||||
trace-libgit = ["asyncgit/trace-libgit"]
|
||||
vendor-openssl = ["asyncgit/vendor-openssl"]
|
||||
default=[]
|
||||
timing=["scopetime/enabled"]
|
||||
|
||||
# make debug build as fast as release
|
||||
# usage of utf8 encoding inside tui
|
||||
# makes their debug profile slow
|
||||
[profile.dev.package."ratatui"]
|
||||
opt-level = 3
|
||||
[workspace]
|
||||
members=[
|
||||
"asyncgit",
|
||||
"scopetime",
|
||||
]
|
||||
|
||||
[profile.release]
|
||||
opt-level = "z" # Optimize for size.
|
||||
strip = "debuginfo"
|
||||
lto = true
|
||||
opt-level = 'z' # Optimize for size.
|
||||
codegen-units = 1
|
||||
|
|
|
|||
26
FAQ.md
|
|
@ -1,26 +0,0 @@
|
|||
|
||||
|
||||
## <a name="table-of-contents"></a> Table of Contents
|
||||
|
||||
1. ["Bad Credentials" Error](#credentials)
|
||||
2. [Custom key bindings](#keybindings)
|
||||
2. [Watcher](#watcher)
|
||||
|
||||
## 1. <a name="credentials"></a> "Bad Credentials" Error <small><sup>[Top ▲](#table-of-contents)</sup></small>
|
||||
|
||||
Some users have trouble pushing/pulling from remotes and adding their ssh-key to their ssh-agent solved the issue. The error they get is:
|
||||

|
||||
|
||||
See Github's excellent documentation for [Adding your SSH Key to the ssh-agent](https://docs.github.com/en/authentication/connecting-to-github-with-ssh/generating-a-new-ssh-key-and-adding-it-to-the-ssh-agent#adding-your-ssh-key-to-the-ssh-agent)
|
||||
|
||||
Note that in some cases adding the line `ssh-add -K ~/.ssh/id_ed25519`(or whatever your key is called) to your bash init script is necessary too to survive restarts.
|
||||
|
||||
## 2. <a name="keybindings"></a> Custom key bindings <small><sup>[Top ▲](#table-of-contents)</sup></small>
|
||||
|
||||
If you want to use `vi`-style keys or customize your key bindings in any other fashion see the specific docs on that: [key config](./KEY_CONFIG.md)
|
||||
|
||||
## 3. <a name="watcher"></a> Watching for changes <small><sup>[Top ▲](#table-of-contents)</sup></small>
|
||||
|
||||
By default, `gitui` polls for changes in the working directory every 5 seconds. If you supply `--watcher` as an argument, it uses a `notify`-based approach instead. This is usually faster and was for some time the default update strategy. It turned out, however, that `notify`-based updates can cause issues on some platforms, so tick-based updates seemed like a safer default.
|
||||
|
||||
See #1444 for details.
|
||||
|
|
@ -1,51 +0,0 @@
|
|||
# Key Config
|
||||
|
||||
The default keys are based on arrow keys to navigate.
|
||||
|
||||
However popular demand lead to fully customizability of the key bindings.
|
||||
|
||||
Create a `key_bindings.ron` file like this:
|
||||
```
|
||||
(
|
||||
move_left: Some(( code: Char('h'), modifiers: "")),
|
||||
move_right: Some(( code: Char('l'), modifiers: "")),
|
||||
move_up: Some(( code: Char('k'), modifiers: "")),
|
||||
move_down: Some(( code: Char('j'), modifiers: "")),
|
||||
|
||||
stash_open: Some(( code: Char('l'), modifiers: "")),
|
||||
open_help: Some(( code: F(1), modifiers: "")),
|
||||
|
||||
status_reset_item: Some(( code: Char('U'), modifiers: "SHIFT")),
|
||||
)
|
||||
```
|
||||
|
||||
The config file format based on the [Ron file format](https://github.com/ron-rs/ron).
|
||||
The location of the file depends on your OS:
|
||||
* `$HOME/.config/gitui/key_bindings.ron` (mac)
|
||||
* `$XDG_CONFIG_HOME/gitui/key_bindings.ron` (linux using XDG)
|
||||
* `$HOME/.config/gitui/key_bindings.ron` (linux)
|
||||
* `%APPDATA%/gitui/key_bindings.ron` (Windows)
|
||||
|
||||
See all possible keys to overwrite in gitui: [here](https://github.com/gitui-org/gitui/blob/master/src/keys/key_list.rs#L83)
|
||||
|
||||
Possible values for:
|
||||
* `code` are defined by the type `KeyCode` in crossterm: [here](https://docs.rs/crossterm/latest/crossterm/event/enum.KeyCode.html)
|
||||
* `modifiers` are defined by the type `KeyModifiers` in crossterm: [here](https://docs.rs/crossterm/latest/crossterm/event/struct.KeyModifiers.html)
|
||||
|
||||
Here is a [vim style key config](vim_style_key_config.ron) with `h`, `j`, `k`, `l` to navigate. Use it to copy the content into `key_bindings.ron` to get vim style key bindings.
|
||||
|
||||
# Key Symbols
|
||||
|
||||
Similar to the above GitUI allows you to change the way the UI visualizes key combos containing special keys like `enter`(default: `⏎`) and `shift`(default: `⇧`).
|
||||
|
||||
If we can find a file `key_symbols.ron` in the above folders we apply the overwrites in it.
|
||||
|
||||
Example content of this file looks like:
|
||||
|
||||
```
|
||||
(
|
||||
enter: Some("enter"),
|
||||
shift: Some("shift-")
|
||||
)
|
||||
```
|
||||
This example will only overwrite two symbols. Find all possible symbols to overwrite in `symbols.rs` in the type `KeySymbolsFile` ([src/keys/symbols.rs](https://github.com/gitui-org/gitui/blob/master/src/keys/symbols.rs))
|
||||
|
|
@ -1,6 +1,6 @@
|
|||
MIT License
|
||||
|
||||
Copyright (c) 2025 gitui-org
|
||||
Copyright (c) 2020 Stephan Dilly
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
|
|
|
|||
107
Makefile
|
|
@ -1,120 +1,47 @@
|
|||
|
||||
.PHONY: debug build-release release-linux-musl test clippy clippy-pedantic install install-debug sort
|
||||
|
||||
ARGS=-l
|
||||
# ARGS=-l -d ~/code/extern/kubernetes
|
||||
# ARGS=-l -d ~/code/extern/linux
|
||||
# ARGS=-l -d ~/code/git-bare-test.git -w ~/code/git-bare-test
|
||||
|
||||
profile:
|
||||
CARGO_PROFILE_RELEASE_DEBUG=true cargo flamegraph --features timing -- ${ARGS}
|
||||
|
||||
run-timing:
|
||||
cargo run --features=timing --release -- ${ARGS}
|
||||
.PHONY: debug build-release release-linux-musl test clippy clippy-pedantic install install-debug
|
||||
|
||||
debug:
|
||||
RUST_BACKTRACE=true cargo run --features=timing -- ${ARGS}
|
||||
cargo run --features=timing -- -l
|
||||
|
||||
build-release:
|
||||
cargo build --release --locked
|
||||
cargo build --release
|
||||
|
||||
release-mac: build-release
|
||||
strip target/release/gitui
|
||||
otool -L target/release/gitui
|
||||
ls -lisah target/release/gitui
|
||||
mkdir -p release
|
||||
tar -C ./target/release/ -czvf ./release/gitui-mac.tar.gz ./gitui
|
||||
ls -lisah ./release/gitui-mac.tar.gz
|
||||
|
||||
release-mac-x86: build-apple-x86-release
|
||||
strip target/x86_64-apple-darwin/release/gitui
|
||||
otool -L target/x86_64-apple-darwin/release/gitui
|
||||
ls -lisah target/x86_64-apple-darwin/release/gitui
|
||||
mkdir -p release
|
||||
tar -C ./target/x86_64-apple-darwin/release/ -czvf ./release/gitui-mac-x86.tar.gz ./gitui
|
||||
ls -lisah ./release/gitui-mac-x86.tar.gz
|
||||
|
||||
release-win: build-release
|
||||
mkdir -p release
|
||||
tar -C ./target/release/ -czvf ./release/gitui-win.tar.gz ./gitui.exe
|
||||
cargo install cargo-wix --version 0.3.3 --locked
|
||||
cargo wix -p gitui --no-build --nocapture --output ./release/gitui-win.msi
|
||||
ls -l ./release/gitui-win.msi
|
||||
|
||||
release-linux-musl: build-linux-musl-release
|
||||
release-linux-musl:
|
||||
cargo build --release --target=x86_64-unknown-linux-musl
|
||||
strip target/x86_64-unknown-linux-musl/release/gitui
|
||||
mkdir -p release
|
||||
tar -C ./target/x86_64-unknown-linux-musl/release/ -czvf ./release/gitui-linux-x86_64.tar.gz ./gitui
|
||||
|
||||
build-apple-x86-debug:
|
||||
cargo build --target=x86_64-apple-darwin
|
||||
|
||||
build-apple-x86-release:
|
||||
cargo build --release --target=x86_64-apple-darwin --locked
|
||||
|
||||
build-linux-musl-debug:
|
||||
cargo build --target=x86_64-unknown-linux-musl
|
||||
|
||||
build-linux-musl-release:
|
||||
cargo build --release --target=x86_64-unknown-linux-musl --locked
|
||||
|
||||
test-linux-musl:
|
||||
cargo nextest run --workspace --target=x86_64-unknown-linux-musl
|
||||
|
||||
release-linux-arm: build-linux-arm-release
|
||||
mkdir -p release
|
||||
|
||||
aarch64-linux-gnu-strip target/aarch64-unknown-linux-gnu/release/gitui
|
||||
arm-linux-gnueabihf-strip target/armv7-unknown-linux-gnueabihf/release/gitui
|
||||
arm-linux-gnueabihf-strip target/arm-unknown-linux-gnueabihf/release/gitui
|
||||
|
||||
tar -C ./target/aarch64-unknown-linux-gnu/release/ -czvf ./release/gitui-linux-aarch64.tar.gz ./gitui
|
||||
tar -C ./target/armv7-unknown-linux-gnueabihf/release/ -czvf ./release/gitui-linux-armv7.tar.gz ./gitui
|
||||
tar -C ./target/arm-unknown-linux-gnueabihf/release/ -czvf ./release/gitui-linux-arm.tar.gz ./gitui
|
||||
|
||||
build-linux-arm-debug:
|
||||
cargo build --target=aarch64-unknown-linux-gnu
|
||||
cargo build --target=armv7-unknown-linux-gnueabihf
|
||||
cargo build --target=arm-unknown-linux-gnueabihf
|
||||
|
||||
build-linux-arm-release:
|
||||
cargo build --release --target=aarch64-unknown-linux-gnu --locked
|
||||
cargo build --release --target=armv7-unknown-linux-gnueabihf --locked
|
||||
cargo build --release --target=arm-unknown-linux-gnueabihf --locked
|
||||
tar -C ./target/x86_64-unknown-linux-musl/release/ -czvf ./release/gitui-linux-musl.tar.gz ./gitui
|
||||
|
||||
test:
|
||||
cargo nextest run --workspace
|
||||
cargo test --workspace
|
||||
|
||||
fmt:
|
||||
cargo fmt -- --check
|
||||
|
||||
clippy:
|
||||
cargo clippy --workspace --all-features
|
||||
touch src/main.rs
|
||||
cargo clean -p gitui -p asyncgit -p scopetime
|
||||
cargo clippy --all-features
|
||||
|
||||
clippy-nightly:
|
||||
cargo +nightly clippy --workspace --all-features
|
||||
clippy-pedantic:
|
||||
cargo clean -p gitui -p asyncgit -p scopetime
|
||||
cargo clippy --all-features -- -W clippy::pedantic
|
||||
|
||||
check: fmt clippy test sort deny
|
||||
|
||||
check-nightly:
|
||||
cargo +nightly c
|
||||
cargo +nightly clippy --workspace --all-features
|
||||
cargo +nightly t
|
||||
|
||||
deny:
|
||||
cargo deny check
|
||||
|
||||
sort:
|
||||
tombi format --check
|
||||
check: fmt clippy
|
||||
|
||||
install:
|
||||
cargo install --path "." --offline --locked
|
||||
cargo install --path "."
|
||||
|
||||
install-timing:
|
||||
cargo install --features=timing --path "." --offline --locked
|
||||
|
||||
licenses:
|
||||
cargo bundle-licenses --format toml --output THIRDPARTY.toml
|
||||
|
||||
clean:
|
||||
cargo clean
|
||||
install-debug:
|
||||
cargo install --features=timing --path "." --offline
|
||||
14
NIGHTLIES.md
|
|
@ -1,14 +0,0 @@
|
|||
# Nightlies
|
||||
|
||||
**Use with caution as these binaries are build nightly and might be broken**
|
||||
|
||||
When you find problems please report them and always mention the version that you see in the `help popup` or when running `gitui -V`
|
||||
|
||||
* [gitui-linux-aarch64.tar.gz](https://gitui.s3.eu-west-1.amazonaws.com/nightly/gitui-linux-aarch64.tar.gz)
|
||||
* [gitui-linux-arm.tar.gz](https://gitui.s3.eu-west-1.amazonaws.com/nightly/gitui-linux-arm.tar.gz)
|
||||
* [gitui-linux-armv7.tar.gz](https://gitui.s3.eu-west-1.amazonaws.com/nightly/gitui-linux-armv7.tar.gz)
|
||||
* [gitui-linux-x86_64.tar.gz](https://gitui.s3.eu-west-1.amazonaws.com/nightly/gitui-linux-x86_64.tar.gz)
|
||||
* [gitui-mac.tar.gz](https://gitui.s3.eu-west-1.amazonaws.com/nightly/gitui-mac.tar.gz)
|
||||
* [gitui-mac-x86.tar.gz](https://gitui.s3.eu-west-1.amazonaws.com/nightly/gitui-mac-x86.tar.gz)
|
||||
* [gitui-win.tar.gz](https://gitui.s3.eu-west-1.amazonaws.com/nightly/gitui-win.tar.gz)
|
||||
* [gitui-win.msi](https://gitui.s3.eu-west-1.amazonaws.com/nightly/gitui-win.msi)
|
||||
318
README.md
|
|
@ -1,301 +1,125 @@
|
|||
<h1 align="center">
|
||||
<img width="300px" src="assets/logo.png" />
|
||||
|
||||
[![CI][s0]][l0] [![crates][s1]][l1] ![MIT][s2] [![UNSAFE][s3]][l3] [![TWEET][s6]][l6] [![dep_status][s7]][l7] [![discord][s8]][l8]
|
||||
|
||||
![CI][s0] [![crates][s1]][l1] ![MIT][s2] [![UNSAFE][s3]][l3] [![ITCH][s4]][l4]
|
||||
</h1>
|
||||
|
||||
[s0]: https://github.com/gitui-org/gitui/workflows/CI/badge.svg
|
||||
[l0]: https://github.com/gitui-org/gitui/actions
|
||||
[s0]: https://github.com/extrawurst/gitui/workflows/CI/badge.svg
|
||||
[s1]: https://img.shields.io/crates/v/gitui.svg
|
||||
[l1]: https://crates.io/crates/gitui
|
||||
[s2]: https://img.shields.io/badge/license-MIT-blue.svg
|
||||
[s3]: https://img.shields.io/badge/unsafe-forbidden-success.svg
|
||||
[l3]: https://github.com/rust-secure-code/safety-dance/
|
||||
[s6]: https://img.shields.io/twitter/follow/extrawurst?label=follow&style=social
|
||||
[l6]: https://twitter.com/intent/follow?screen_name=extrawurst
|
||||
[s7]: https://deps.rs/repo/github/gitui-org/gitui/status.svg
|
||||
[l7]: https://deps.rs/repo/github/gitui-org/gitui
|
||||
[s8]: https://img.shields.io/discord/1176858176897953872
|
||||
[l8]: https://discord.gg/rQNeEnMhus
|
||||
[s4]: https://img.shields.io/badge/itch.io-ok-green
|
||||
[l4]: https://extrawurst.itch.io/gitui
|
||||
|
||||
<h5 align="center">GitUI provides you with the comfort of a git GUI but right in your terminal</h1>
|
||||
blazing fast terminal-ui for git written in rust
|
||||
|
||||

|
||||

|
||||
|
||||
## <a name="table-of-contents"></a> Table of Contents
|
||||
# features
|
||||
|
||||
1. [Features](#features)
|
||||
2. [Motivation](#motivation)
|
||||
3. [Benchmarks](#bench)
|
||||
4. [Roadmap](#roadmap)
|
||||
5. [Limitations](#limitations)
|
||||
6. [Installation](#installation)
|
||||
7. [Build](#build)
|
||||
8. [FAQs](#faqs)
|
||||
9. [Diagnostics](#diagnostics)
|
||||
10. [Color Theme](#theme)
|
||||
11. [Key Bindings](#bindings)
|
||||
12. [Sponsoring](#sponsoring)
|
||||
13. [Inspiration](#inspiration)
|
||||
14. [Contributing](#contributing)
|
||||
15. [Contributors](#contributors)
|
||||
* fast and intuitive key only control
|
||||
* context based help (**no** need to remember tons of hot-keys)
|
||||
* inspect/commit/amend changes (incl. hooks: *commit-msg*/*post-commit*)
|
||||
* (un)stage files/hunks, revert/reset files/hunk
|
||||
* stashing (save, apply, drop, inspect content)
|
||||
* browse commit log, diff committed changes
|
||||
* scalable terminal ui layout
|
||||
* async [input polling](assets/perf_compare.jpg) and
|
||||
* async git API for fluid control
|
||||
|
||||
## 1. <a name="features"></a> Features <small><sup>[Top ▲](#table-of-contents)</sup></small>
|
||||
# benchmarks
|
||||
|
||||
- Fast and intuitive **keyboard only** control
|
||||
- Context based help (**no need to memorize** tons of hot-keys)
|
||||
- Inspect, commit, and amend changes (incl. hooks: *pre-commit*,*commit-msg*,*post-commit*,*prepare-commit-msg*)
|
||||
- Stage, unstage, revert and reset files, hunks and lines
|
||||
- Stashing (save, pop, apply, drop, and inspect)
|
||||
- Push / Fetch to / from remote
|
||||
- Branch List (create, rename, delete, checkout, remotes)
|
||||
- Browse / **Search** commit log, diff committed changes
|
||||
- Responsive terminal UI
|
||||
- Async git API for fluid control
|
||||
- Submodule support
|
||||
- gpg commit signing with shortcomings (see [#97](https://github.com/gitui-org/gitui/issues/97)))
|
||||
For a [RustBerlin meetup presentation](https://youtu.be/rpilJV-eIVw?t=5334) I compared *lazygit*,*tig* and *gitui* parsing the entire linux git repository (~900k commits):
|
||||
|
||||
## 2. <a name="motivation"></a> Motivation <small><sup>[Top ▲](#table-of-contents)</sup></small>
|
||||
||lazygit|tig|**gitui**|
|
||||
|--|--|--|--|
|
||||
|time| 57s | 4m 20s |**24s**
|
||||
| mem |2.6 gb|1.3 gb| **170 mb**
|
||||
| binary | 16 mb | **600 kb** | 1.4 mb
|
||||
| freezes | yes | soso | **no**
|
||||
| crashes | sometimes | no | **no**
|
||||
|
||||
I do most of my git work in a terminal but I frequently found myself using git GUIs for some use-cases like: index, commit, diff, stash, blame and log.
|
||||
presentation slides: https://github.com/extrawurst/gitui-presentation
|
||||
|
||||
Unfortunately popular git GUIs all fail on giant repositories or become unresponsive and unusable.
|
||||
# known limitations
|
||||
|
||||
GitUI provides you with the user experience and comfort of a git GUI but right in your terminal while being portable, fast, free and opensource.
|
||||
* no support for [bare repositories](https://git-scm.com/book/en/v2/Git-on-the-Server-Getting-Git-on-a-Server) (see [#100](https://github.com/extrawurst/gitui/issues/100))
|
||||
* [core.hooksPath](https://git-scm.com/docs/githooks) config not supported
|
||||
* revert/reset hunk in working dir (see [#11](https://github.com/extrawurst/gitui/issues/11))
|
||||
|
||||
## 3. <a name="bench"></a> Benchmarks <small><sup>[Top ▲](#table-of-contents)</sup></small>
|
||||
# motivation
|
||||
|
||||
For a [RustBerlin meetup presentation](https://youtu.be/rpilJV-eIVw?t=5334) ([slides](https://github.com/extrawurst/gitui-presentation)) I compared `lazygit`,`tig` and `gitui` by parsing the entire Linux git repository (which contains over 900k commits):
|
||||
I do most of my git usage in a terminal but I frequently found myself using git UIs for some use cases like: index/commit, diff, stash and log.
|
||||
|
||||
| | Time | Memory (GB) | Binary (MB) | Freezes | Crashes |
|
||||
| --------- | ---------- | ----------- | ----------- | --------- | --------- |
|
||||
| `gitui` | **24 s** ✅ | **0.17** ✅ | 10 | **No** ✅ | **No** ✅ |
|
||||
| `lazygit` | 57 s | 2.6 | 25 | Yes | Sometimes |
|
||||
| `tig` | 4 m 20 s | 1.3 | **0.6** ✅ | Sometimes | **No** ✅ |
|
||||
Over the last 2 years my go-to GUI tool for this was [fork](https://git-fork.com) because it was not bloated, snappy and free. Unfortunately the *free* part will [change soon](https://github.com/ForkIssues/TrackerWin/issues/571) and so I decided to build a fast & simple terminal tool myself to copy the fork features i am using the most.
|
||||
|
||||
## 4. <a name="roadmap"></a> Road(map) to 1.0 <small><sup>[Top ▲](#table-of-contents)</sup></small>
|
||||
# installation
|
||||
|
||||
These are the high level goals before calling out `1.0`:
|
||||
For the time being this product is considered alpha and **not** production ready.
|
||||
|
||||
* visualize branching structure in log tab ([#81](https://github.com/gitui-org/gitui/issues/81))
|
||||
* interactive rebase ([#32](https://github.com/gitui-org/gitui/issues/32))
|
||||
- no git-lfs support (see [#2812](https://github.com/gitui-org/gitui/issues/2812))
|
||||
## Arch Linux
|
||||
|
||||
## 5. <a name="limitations"></a> Known Limitations <small><sup>[Top ▲](#table-of-contents)</sup></small>
|
||||
|
||||
- no sparse repo support (see [#1226](https://github.com/gitui-org/gitui/issues/1226))
|
||||
- *credential.helper* for https needs to be **explicitly** configured (see [#800](https://github.com/gitui-org/gitui/issues/800))
|
||||
|
||||
Currently, this tool does not fully substitute the _git shell_, however both tools work well in tandem.
|
||||
|
||||
The priorities for `gitui` are on features that are making me mad when done on the _git shell_, like stashing, staging lines or hunks. Eventually, I will be able to work on making `gitui` a one stop solution - but for that I need help - this is just a spare time project for now.
|
||||
|
||||
All support is welcomed! Sponsors as well! ❤️
|
||||
|
||||
## 6. <a name="installation"></a> Installation <small><sup>[Top ▲](#table-of-contents)</sup></small>
|
||||
|
||||
GitUI is in beta and may contain bugs and missing features. However, for personal use it is reasonably stable and is being used while developing itself.
|
||||
|
||||
<a href="https://repology.org/project/gitui/versions">
|
||||
<img src="https://repology.org/badge/vertical-allrepos/gitui.svg" alt="Packaging status" align="right">
|
||||
</a>
|
||||
|
||||
### Various Package Managers
|
||||
|
||||
<details>
|
||||
<summary>Install Instructions</summary>
|
||||
|
||||
##### [Arch Linux](https://archlinux.org/packages/extra/x86_64/gitui/)
|
||||
There is an [AUR package for `gitui`](https://aur.archlinux.org/packages/gitui/):
|
||||
|
||||
```sh
|
||||
pacman -S gitui
|
||||
git clone https://aur.archlinux.org/gitui.git
|
||||
cd gitui
|
||||
makepkg -si
|
||||
```
|
||||
|
||||
##### Fedora
|
||||
## Fedora
|
||||
|
||||
```sh
|
||||
sudo dnf install gitui
|
||||
```
|
||||
|
||||
##### Gentoo
|
||||
Available in [dm9pZCAq overlay](https://github.com/gentoo-mirror/dm9pZCAq)
|
||||
|
||||
```sh
|
||||
sudo eselect repository enable dm9pZCAq
|
||||
sudo emerge --sync dm9pZCAq
|
||||
sudo emerge dev-vcs/gitui::dm9pZCAq
|
||||
```
|
||||
|
||||
##### [openSUSE](https://software.opensuse.org/package/gitui)
|
||||
|
||||
```sh
|
||||
sudo zypper install gitui
|
||||
```
|
||||
|
||||
##### Homebrew (macOS)
|
||||
|
||||
```sh
|
||||
brew install gitui
|
||||
```
|
||||
|
||||
##### [MacPorts (macOS)](https://ports.macports.org/port/gitui/details/)
|
||||
|
||||
```sh
|
||||
port install gitui
|
||||
```
|
||||
|
||||
##### [Winget](https://github.com/microsoft/winget-pkgs/tree/master/manifests/s/StephanDilly/gitui) (Windows)
|
||||
## homebrew (macos)
|
||||
|
||||
```
|
||||
winget install gitui
|
||||
brew install extrawurst/tap/gitui
|
||||
```
|
||||
|
||||
##### [Scoop](https://github.com/ScoopInstaller/Main/blob/master/bucket/gitui.json) (Windows)
|
||||
## release binaries
|
||||
|
||||
see [releases](https://github.com/extrawurst/gitui/releases)
|
||||
|
||||
## install from source
|
||||
|
||||
### requirements
|
||||
|
||||
install **latest** `rust`/`cargo`: https://www.rust-lang.org/tools/install
|
||||
|
||||
### cargo install
|
||||
|
||||
the simplest way to start playing around with `gitui` is to have `cargo` build/install it:
|
||||
|
||||
```
|
||||
scoop install gitui
|
||||
cargo install gitui
|
||||
```
|
||||
|
||||
##### [Chocolatey](https://chocolatey.org/packages/gitui) (Windows)
|
||||
# diagnostics
|
||||
|
||||
to enable logging:
|
||||
```
|
||||
choco install gitui
|
||||
gitui -l
|
||||
```
|
||||
|
||||
##### [Mise](https://github.com/jdx/mise)
|
||||
this will log to:
|
||||
* `$HOME/Library/Caches/gitui/gitui.log` (mac)
|
||||
* `$XDG_CACHE_HOME/gitui/gitui.log` (linux using `XDG`)
|
||||
* `$HOME/.cache/gitui/gitui.log` (linux)
|
||||
|
||||
```shell
|
||||
mise use -g gitui@latest
|
||||
```
|
||||
|
||||
##### [Nix](https://search.nixos.org/packages?channel=unstable&show=gitui&from=0&size=50&sort=relevance&query=gitui) (Nix/NixOS)
|
||||
|
||||
Nixpkg
|
||||
```
|
||||
nix-env -iA nixpkgs.gitui
|
||||
```
|
||||
NixOS
|
||||
```
|
||||
nix-env -iA nixos.gitui
|
||||
```
|
||||
|
||||
##### [Termux](https://github.com/termux/termux-packages/tree/master/packages/gitui) (Android)
|
||||
|
||||
```
|
||||
pkg install gitui
|
||||
```
|
||||
|
||||
##### [Anaconda](https://anaconda.org/conda-forge/gitui)
|
||||
```
|
||||
conda install -c conda-forge gitui
|
||||
```
|
||||
|
||||
</details>
|
||||
|
||||
### Release Binaries
|
||||
|
||||
[Available for download in releases](https://github.com/gitui-org/gitui/releases)
|
||||
|
||||
Binaries available for:
|
||||
|
||||
#### Linux
|
||||
|
||||
- gitui-linux-x86_64.tar.gz (linux musl statically linked)
|
||||
- gitui-linux-aarch64.tar.gz (linux on 64 bit arm)
|
||||
- gitui-linux-arm.tar.gz
|
||||
- gitui-linux-armv7.tar.gz
|
||||
|
||||
All contain a single binary file
|
||||
|
||||
#### macOS
|
||||
|
||||
- gitui-mac.tar.gz (arm64)
|
||||
- gitui-mac-x86.tar.gz (intel x86)
|
||||
|
||||
#### Windows
|
||||
|
||||
- gitui-win.tar.gz (single 64bit binary)
|
||||
- gitui-win.msi (64bit Installer package)
|
||||
|
||||
### Nightly Builds
|
||||
|
||||
see [NIGHTLIES.md](./NIGHTLIES.md)
|
||||
|
||||
## 7. <a name="build"></a> Build <small><sup>[Top ▲](#table-of-contents)</sup></small>
|
||||
|
||||
### Requirements
|
||||
|
||||
- Minimum supported `rust`/`cargo` version: `1.88`
|
||||
- See [Install Rust](https://www.rust-lang.org/tools/install)
|
||||
|
||||
- To build openssl dependency (see https://docs.rs/openssl/latest/openssl/)
|
||||
- perl >= 5.12 (strawberry perl works for windows https://strawberryperl.com/)
|
||||
- a c compiler (msvc, gcc or clang, cargo will find it)
|
||||
|
||||
- To run the complete test suite python is required (and it must be invocable as `python`)
|
||||
|
||||
### Cargo Install
|
||||
|
||||
The simplest way to start playing around with `gitui` is to have `cargo` build and install it with `cargo install gitui --locked`. If you are not familiar with rust and cargo: [Getting Started with Rust](https://doc.rust-lang.org/book/ch01-00-getting-started.html)
|
||||
|
||||
### Cargo Features
|
||||
#### trace-libgit
|
||||
enable `libgit2` tracing
|
||||
|
||||
works if `libgit2` built with `-DENABLE_TRACE=ON`
|
||||
|
||||
this feature enabled by default, to disable: `cargo install --no-default-features`
|
||||
|
||||
## 8. <a name="faqs"></a> FAQs <small><sup>[Top ▲](#table-of-contents)</sup></small>
|
||||
|
||||
see [FAQs page](./FAQ.md)
|
||||
|
||||
## 9. <a name="diagnostics"></a> Diagnostics <small><sup>[Top ▲](#table-of-contents)</sup></small>
|
||||
|
||||
To run with logging enabled run `gitui -l`.
|
||||
|
||||
This will log to:
|
||||
|
||||
- macOS: `$HOME/Library/Caches/gitui/gitui.log`
|
||||
- Linux using `XDG`: `$XDG_CACHE_HOME/gitui/gitui.log`
|
||||
- Linux: `$HOME/.cache/gitui/gitui.log`
|
||||
- Windows: `%LOCALAPPDATA%/gitui/gitui.log`
|
||||
|
||||
## 10. <a name="theme"></a> Color Theme <small><sup>[Top ▲](#table-of-contents)</sup></small>
|
||||
# color theme
|
||||
|
||||

|
||||
|
||||
`gitui` should automatically work on both light and dark terminal themes.
|
||||
In general `gitui` should automatically work on `dark` and `light` terminal themes.
|
||||
However you can customize everything to your liking: [see THEMES.md](THEMES.md)
|
||||
|
||||
However, you can customize everything to your liking: See [Themes](THEMES.md).
|
||||
# inspiration
|
||||
|
||||
## 11. <a name="bindings"></a> Key Bindings <small><sup>[Top ▲](#table-of-contents)</sup></small>
|
||||
|
||||
The key bindings can be customized: See [Key Config](KEY_CONFIG.md) on how to set them to `vim`-like bindings.
|
||||
|
||||
## 12. <a name="sponsoring"></a> Sponsoring <small><sup>[Top ▲](#table-of-contents)</sup></small>
|
||||
|
||||
[](https://github.com/sponsors/extrawurst)
|
||||
|
||||
## 13. <a name="inspiration"></a> Inspiration <small><sup>[Top ▲](#table-of-contents)</sup></small>
|
||||
|
||||
- [lazygit](https://github.com/jesseduffield/lazygit)
|
||||
- [tig](https://github.com/jonas/tig)
|
||||
- [GitUp](https://github.com/git-up/GitUp)
|
||||
- It would be nice to come up with a way to have the map view available in a terminal tool
|
||||
- [git-brunch](https://github.com/andys8/git-brunch)
|
||||
|
||||
## 14. <a name="contributing"></a> Contributing <small><sup>[Top ▲](#table-of-contents)</sup></small>
|
||||
|
||||
See [CONTRIBUTING.md](CONTRIBUTING.md).
|
||||
|
||||
## 15. <a name="contributors"></a> Contributors <small><sup>[Top ▲](#table-of-contents)</sup></small>
|
||||
|
||||
Thanks goes to all the contributors that help make GitUI amazing! ❤️
|
||||
|
||||
Wanna become a co-maintainer? We are looking for [you](https://github.com/gitui-org/gitui/issues/2084)!
|
||||
|
||||
<a href="https://github.com/gitui-org/gitui/graphs/contributors">
|
||||
<img src="https://contrib.rocks/image?repo=gitui-org/gitui" />
|
||||
</a>
|
||||
* https://github.com/jesseduffield/lazygit
|
||||
* https://github.com/jonas/tig
|
||||
* https://github.com/git-up/GitUp (would be nice to comeup with a way to have the map view available in a terminal tool)
|
||||
|
|
|
|||
88
THEMES.md
|
|
@ -1,90 +1,14 @@
|
|||
# Themes
|
||||
# Themes
|
||||
|
||||
default on light terminal:
|
||||

|
||||
|
||||
## Configuration
|
||||
to change the colors of the program you have to modify `theme.ron` file
|
||||
[Ron format](https://github.com/ron-rs/ron) located at config path. The path differs depending on the operating system:
|
||||
|
||||
To change the colors of the default theme you need to add a `theme.ron` file that contains the colors you want to override. Note that you don’t have to specify the full theme anymore (as of 0.23). Instead, it is sufficient to override just the values that you want to differ from their default values.
|
||||
|
||||
The file uses the [Ron format](https://github.com/ron-rs/ron) and is located at one of the following paths, depending on your operating system:
|
||||
|
||||
* `$HOME/.config/gitui/theme.ron` (mac)
|
||||
* `$HOME/Library/Preferences/gitui/theme.ron` (mac)
|
||||
* `$XDG_CONFIG_HOME/gitui/theme.ron` (linux using XDG)
|
||||
* `$HOME/.config/gitui/theme.ron` (linux)
|
||||
* `%APPDATA%/gitui/theme.ron` (Windows)
|
||||
|
||||
Alternatively, you can create a theme in the same directory mentioned above and use it with the `-t` flag followed by the name of the file in the directory. E.g. If you are on linux calling `gitui -t arc.ron`, this will load the theme in `$XDG_CONFIG_HOME/gitui/arc.ron` or `$HOME/.config/gitui/arc.ron`.
|
||||
|
||||
Example theme override:
|
||||
|
||||
```ron
|
||||
(
|
||||
selection_bg: Some("Blue"),
|
||||
selection_fg: Some("#ffffff"),
|
||||
)
|
||||
```
|
||||
|
||||
Note that you need to wrap values in `Some` due to the way the overrides work (as of 0.23).
|
||||
|
||||
Notes:
|
||||
|
||||
* rgb colors might not be supported in every terminal.
|
||||
* using a color like `yellow` might appear in whatever your terminal/theme defines for `yellow`
|
||||
* valid colors can be found in ratatui's [Color](https://docs.rs/ratatui/latest/ratatui/style/enum.Color.html) struct.
|
||||
* all customizable theme elements can be found in [`style.rs` in the `impl Default for Theme` block](https://github.com/gitui-org/gitui/blob/master/src/ui/style.rs#L305)
|
||||
|
||||
## Preset Themes
|
||||
|
||||
You can find preset themes by Catppuccin [here](https://github.com/catppuccin/gitui.git).
|
||||
|
||||
## Syntax Highlighting
|
||||
|
||||
The syntax highlighting theme can be defined using the element `syntax`. Both [default themes of the syntect library](https://github.com/trishume/syntect/blob/7fe13c0fd53cdfa0f9fea1aa14c5ba37f81d8b71/src/dumps.rs#L215) and custom themes are supported.
|
||||
|
||||
Example syntax theme:
|
||||
```ron
|
||||
(
|
||||
syntax: Some("InspiredGitHub"),
|
||||
)
|
||||
```
|
||||
|
||||
Custom themes are located in the [configuration directory](#configuration), are using TextMate's theme format and must have a `.tmTheme` file extension. To load a custom theme, `syntax` must be set to the file name without the file extension. For example, to load [`Blackboard.tmTheme`](https://raw.githubusercontent.com/filmgirl/TextMate-Themes/refs/heads/master/Blackboard.tmTheme), place the file next to `theme.ron` and set:
|
||||
```ron
|
||||
(
|
||||
syntax: Some("Blackboard"),
|
||||
)
|
||||
```
|
||||
|
||||
[filmgirl/TextMate-Themes](https://github.com/filmgirl/TextMate-Themes) offers many [beautiful](https://inkdeep.github.io/TextMate-Themes) TextMate themes to choose from.
|
||||
|
||||
## Customizing line breaks
|
||||
|
||||
If you want to change how the line break is displayed in the diff, you can also specify `line_break` in your `theme.ron`:
|
||||
|
||||
```ron
|
||||
(
|
||||
line_break: Some("¶"),
|
||||
)
|
||||
```
|
||||
|
||||
Note that if you want to turn it off, you should use a blank string:
|
||||
|
||||
```ron
|
||||
(
|
||||
line_break: Some(""),
|
||||
)
|
||||
```
|
||||
## Customizing selection
|
||||
|
||||
By default the `selection_fg` color is used to color the text of the selected line.
|
||||
Diff line, filename, commit hashes, time and author are re-colored with `selection_fg` color.
|
||||
This can be changed by specifying the `use_selection_fg` boolean in your `theme.ron`:
|
||||
|
||||
```
|
||||
(
|
||||
use_selection_fg: Some(false),
|
||||
)
|
||||
```
|
||||
|
||||
By default, `use_selection_fg` is set to `true`.
|
||||
Valid colors can be found in [ColorDef](./src/ui/style.rs#ColorDef) struct. note that rgb colors might not be supported
|
||||
in every terminal.
|
||||
|
|
|
|||
|
Before Width: | Height: | Size: 67 KiB |
|
Before Width: | Height: | Size: 69 KiB |
|
Before Width: | Height: | Size: 31 KiB |
|
Before Width: | Height: | Size: 302 KiB |
BIN
assets/blame.gif
|
Before Width: | Height: | Size: 1.5 MiB |
|
Before Width: | Height: | Size: 318 KiB |
|
Before Width: | Height: | Size: 148 KiB |
|
Before Width: | Height: | Size: 527 KiB |
|
Before Width: | Height: | Size: 98 KiB |
|
Before Width: | Height: | Size: 127 KiB |
|
Before Width: | Height: | Size: 122 KiB After Width: | Height: | Size: 278 KiB |
|
Before Width: | Height: | Size: 481 KiB |
|
Before Width: | Height: | Size: 82 KiB |
|
Before Width: | Height: | Size: 38 KiB |
|
Before Width: | Height: | Size: 653 KiB |
|
Before Width: | Height: | Size: 243 KiB |
|
Before Width: | Height: | Size: 1.8 MiB After Width: | Height: | Size: 1.8 MiB |
|
Before Width: | Height: | Size: 136 KiB |
|
Before Width: | Height: | Size: 96 KiB |
|
Before Width: | Height: | Size: 98 KiB |
|
Before Width: | Height: | Size: 299 KiB |
|
Before Width: | Height: | Size: 900 KiB |
|
Before Width: | Height: | Size: 444 KiB |
|
Before Width: | Height: | Size: 415 KiB |
|
Before Width: | Height: | Size: 22 KiB |
|
Before Width: | Height: | Size: 418 KiB After Width: | Height: | Size: 756 KiB |
|
Before Width: | Height: | Size: 2.7 MiB |
BIN
assets/logo.png
|
Before Width: | Height: | Size: 36 KiB After Width: | Height: | Size: 25 KiB |
|
|
@ -1 +0,0 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" width="1000" height="600" fill="none"><g clip-path="url(#a)"><mask id="c" width="438" height="484" x="38" y="57" maskUnits="userSpaceOnUse" style="mask-type:alpha"><path fill="#D9D9D9" d="m340 541 76-39 60-52-76-43-35-8-13 42H231l-13-38-46-7-24-106 20-29v-44l85-47 101-6 48 26 67-40-60-67-150-25-189 78-31 201 116 182 185 22Z"/></mask><g stroke="#000" filter="url(#b)" mask="url(#c)"><path stroke-width="38.4" d="M290 483a183 183 0 1 0 0-366 183 183 0 0 0 0 366Z"/><path fill="#000" stroke-linejoin="round" stroke-width="12.8" d="m480 351 23-9-18-16-5 25Zm-14 36 25-4-15-20-10 24Zm-20 33 25 1-11-23-14 22Zm-26 28 24 6-6-24-18 18Zm-32 22 23 11-1-25-22 14Zm-35 16 20 15 4-25-24 10Zm-37 9 16 18 9-23-25 5Zm-39 1 13 21 13-21h-26Zm-38-6 9 23 16-18-25-5Zm-36-14 4 25 20-15-24-10Zm-33-20-1 25 23-11-22-14Zm-28-26-6 24 24-6-18-18Zm-22-32-11 23 25-1-14-22Zm-16-35-15 20 25 4-10-24Zm-9-37-18 16 23 9-5-25Zm-1-39-21 13 21 13v-26Zm6-38-23 9 18 16 5-25Zm14-36-25 4 15 20 10-24Zm20-33-25-1 11 23 14-22Zm26-28-24-6 6 24 18-18Zm32-22-23-11 1 25 22-14Zm35-16-20-15-4 25 24-10Zm37-9-16-18-9 23 25-5Zm39-1-13-21-13 21h26Zm38 6-9-23-16 18 25 5Zm36 14-4-25-20 15 24 10Zm33 20 1-25-23 11 22 14Zm28 26 6-24-24 6 18 18Zm22 32 11-23-25 1 14 22Zm16 36 15-20-25-4 10 24Zm9 36 18-16-23-9 5 25Z"/><path fill="#000" stroke-linejoin="round" stroke-width="25.6" d="m260 121 30 30 30-30h-60Zm191 95-19 38 38 19-19-57Zm-32 211-41-6-7 41 48-35Zm-210 35-7-41-41 6 48 35Zm-99-189 38-19-19-38-19 57Z"/></g></g><g filter="url(#d)"><path fill="#000" d="M305 307h81v7h-18v71l-35-6a98 98 0 0 1-92-5c-24-16-36-41-36-75 0-26 8-47 23-63 16-16 37-24 63-24 15 0 28 3 41 8l27-5 1 60h-9c-7-36-24-54-51-54h-7c-29 4-44 30-44 80 0 16 1 30 4 42 7 24 22 36 45 36 12 0 22-4 31-10v-55h-24v-7Zm157 71h3c2 0 4 3 4 7h-70v-7l6-1c7-1 10-6 10-14v-92h-16v-8h52v115h11Zm-8-152c0 4-1 7-3 10-4 7-10 11-18 11-4 0-7-1-10-3-8-4-11-10-11-18l2-10c4-7 10-11 19-11 3 0 7 1 10 3 7 4 11 10 11 18Zm24 46v-7h20v-27l36-6v33h37v7h-37v86l1 7c1 10 5 15 12 15l4-1c7-1 13-10 16-25l8 1c-1 8-4 14-6 19-6 9-16 14-30 14h-7c-23-3-34-18-34-45v-71h-20Zm101-49v-8h85v8h-22v97c0 11 0 20 2 28 5 21 16 31 36 31 30 0 45-19 45-59v-97h-22v-8h55v8h-22v96c0 13-2 25-5 34-8 23-27 35-57 35-48 0-72-22-72-65V223h-23Zm271 162h-85v-8h23V223h-23v-8h85v8h-22v154h15c4 1 6 3 7 8Z"/></g><defs><filter id="b" width="455.7" height="455.7" x="62" y="72" color-interpolation-filters="sRGB" filterUnits="userSpaceOnUse"><feFlood flood-opacity="0" result="BackgroundImageFix"/><feColorMatrix in="SourceAlpha" result="hardAlpha" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0"/><feOffset/><feGaussianBlur stdDeviation="2"/><feComposite in2="hardAlpha" operator="out"/><feColorMatrix values="0 0 0 0 1 0 0 0 0 1 0 0 0 0 1 0 0 0 1 0"/><feBlend in2="BackgroundImageFix" result="effect1_dropShadow_1_2"/><feBlend in="SourceGraphic" in2="effect1_dropShadow_1_2" result="shape"/></filter><filter id="d" width="653.4" height="191.2" x="201" y="201" color-interpolation-filters="sRGB" filterUnits="userSpaceOnUse"><feFlood flood-opacity="0" result="BackgroundImageFix"/><feColorMatrix in="SourceAlpha" result="hardAlpha" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0"/><feOffset/><feGaussianBlur stdDeviation="2"/><feComposite in2="hardAlpha" operator="out"/><feColorMatrix values="0 0 0 0 1 0 0 0 0 1 0 0 0 0 1 0 0 0 1 0"/><feBlend in2="BackgroundImageFix" result="effect1_dropShadow_1_2"/><feBlend in="SourceGraphic" in2="effect1_dropShadow_1_2" result="shape"/></filter><clipPath id="a"><path fill="#fff" d="M64 74h452v452H64z"/></clipPath></defs></svg>
|
||||
|
Before Width: | Height: | Size: 3.5 KiB |
|
Before Width: | Height: | Size: 136 KiB |
|
Before Width: | Height: | Size: 639 KiB |
|
Before Width: | Height: | Size: 219 KiB |
|
Before Width: | Height: | Size: 1 MiB |
|
Before Width: | Height: | Size: 6.6 KiB |
|
|
@ -1 +0,0 @@
|
|||
<mxfile host="app.diagrams.net" modified="2021-08-17T21:58:53.216Z" agent="5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/14.1.1 Safari/605.1.15" etag="DR-vNI6rA-1d9_EpYLQC" version="14.9.7" type="device"><diagram id="cIq82w5ce00BbVejkL92" name="Page-1">5Zldc6IwFIZ/DZfrEJCvS0Xb7exHZ9bd6WxvdlIIkGlMmBir3V+/iQQVg+12Bqm7eqHwkkB43nMCJ1puPF9fc1gWX1iKiOXY6dpyJ5bjABCG8kcpz5Xi+0El5BynutFOmOHfSIu2Vpc4RYtGQ8EYEbhsigmjFCWioUHO2arZLGOkedUS5sgQZgkkpnqHU1FUaugEO/0jwnlRXxn4UXVkDuvG+k4WBUzZak9yp5Ybc8ZEtTVfx4goeDWXqt/VkaPbgXFExd90KLNx+X10/wl4v+6/5fx28jMVH/RZniBZ6hvWgxXPNQHOljRF6iS25Y5XBRZoVsJEHV1Jz6VWiDmRe0Bu6tMhLtD66DjB9u5l2CA2R4I/yyZ1h0B30REDvGDgVcpq54AbaazFHn0n1CLUrufbs+/AyA3N5g2cnPPj5Hh+g5Njm5S2wbdPCYTeiSi5BqVZIUPesX9QwWHyKOkcUpP3L4c1hgTnVCoEZWpXgcEyCUdanuM0VT3GTcgZJiRmhPHNudxs8+mIrn9A17UNutE2MBtBeKoYHBp0JyiDSyKOQd2Lt4Xg7BHVrCij6ACflmofEokJ8ZecaAvwpjsduDC03RrxCz74bVPBqVzwDBfuFInFhoR8DhWQ5vKB1b0lr6VGL4Y4fjBwDhwJTUcA6Dc1fMOUm5wyjv7nzBgOgteN6DU1AsOFmNENccf+jKnKCh/OFQ76sCi3HC4iS1zbNCfs05zQMGd4UdnRZkCv2REZBmDFqljSRymTKj8uJx+clvepPu2oC8w9P+wT8D/jhGhxoNeEAGbxORNQLM8zDTL5KJvpIQ07cKTtTQq8e40BzEJ3gmVNdamO+O/viFlU35YCM3r+aaL2CXxAZMx4ivjBNbtwzIsOZzUQvb9jZoloWPWmxaIDdzoB11zdcD2TWuva0fBk0MwSLt6U0moAkKby/SiXm0xORfZyt5pky2BW7032DZVf9ePDtrzxNRYJoxmWvWJ1aLMQpba+oWTJF/gJWd7kWAp1uxzVZm8nJgaDenl962PLGiDwt9PavpXuyaxsqQMJWxwtxs9u7Q8EzewYAq9tBbrXOcUs32YrGeFFFezW1LGiqTUC1vTKGodWeKWUUWyF3k7pK9xPMD+1OxBEfTpg1m9f4VOFfzD4p+DWL94vh3c3cOXu7i+uzbG9Pwrd6R8=</diagram></mxfile>
|
||||
|
Before Width: | Height: | Size: 203 KiB |
BIN
assets/perf_compare.jpg
Normal file
|
After Width: | Height: | Size: 64 KiB |
|
Before Width: | Height: | Size: 781 KiB |
BIN
assets/pull.gif
|
Before Width: | Height: | Size: 18 KiB |
BIN
assets/push.gif
|
Before Width: | Height: | Size: 50 KiB |
|
Before Width: | Height: | Size: 155 KiB |
|
Before Width: | Height: | Size: 275 KiB |
|
Before Width: | Height: | Size: 1.2 MiB |
|
Before Width: | Height: | Size: 682 KiB |
|
Before Width: | Height: | Size: 1.8 MiB |
|
Before Width: | Height: | Size: 2.6 MiB |
|
Before Width: | Height: | Size: 511 KiB After Width: | Height: | Size: 1 MiB |
|
Before Width: | Height: | Size: 1,000 KiB After Width: | Height: | Size: 1.5 MiB |
|
Before Width: | Height: | Size: 421 KiB After Width: | Height: | Size: 944 KiB |
|
Before Width: | Height: | Size: 492 KiB After Width: | Height: | Size: 1,002 KiB |
|
Before Width: | Height: | Size: 392 KiB |
|
Before Width: | Height: | Size: 207 KiB |
|
Before Width: | Height: | Size: 61 KiB |
|
Before Width: | Height: | Size: 277 KiB |
|
Before Width: | Height: | Size: 640 KiB |
|
Before Width: | Height: | Size: 750 KiB |
|
Before Width: | Height: | Size: 285 KiB |
|
Before Width: | Height: | Size: 285 KiB |
|
Before Width: | Height: | Size: 257 KiB |
|
Before Width: | Height: | Size: 1.4 MiB |
|
Before Width: | Height: | Size: 641 KiB |
|
Before Width: | Height: | Size: 1,019 KiB |
|
Before Width: | Height: | Size: 135 KiB |
|
|
@ -1,52 +1,23 @@
|
|||
[package]
|
||||
name = "asyncgit"
|
||||
version = "0.28.1"
|
||||
authors = ["extrawurst <mail@rusticorn.com>"]
|
||||
edition = "2021"
|
||||
version = "0.7.0"
|
||||
authors = ["Stephan Dilly <dilly.stephan@gmail.com>"]
|
||||
edition = "2018"
|
||||
description = "allow using git2 in a asynchronous context"
|
||||
homepage = "https://github.com/gitui-org/gitui"
|
||||
repository = "https://github.com/gitui-org/gitui"
|
||||
homepage = "https://github.com/extrawurst/gitui"
|
||||
repository = "https://github.com/extrawurst/gitui"
|
||||
readme = "README.md"
|
||||
license = "MIT"
|
||||
categories = ["asynchronous", "concurrency"]
|
||||
categories = ["concurrency","asynchronous"]
|
||||
keywords = ["git"]
|
||||
|
||||
[dependencies]
|
||||
bitflags = "2"
|
||||
crossbeam-channel = "0.5"
|
||||
dirs = "6.0"
|
||||
easy-cast = "0.5"
|
||||
fuzzy-matcher = "0.3"
|
||||
git2 = "0.20"
|
||||
git2-hooks = { path = "../git2-hooks", version = "0.7" }
|
||||
gix = { version = "0.78.0", default-features = false, features = [
|
||||
"mailmap",
|
||||
"max-performance",
|
||||
"revision",
|
||||
"status",
|
||||
] }
|
||||
git2 = { version = "0.13.6", default-features = false }
|
||||
rayon-core = "1.7"
|
||||
crossbeam-channel = "0.4"
|
||||
log = "0.4"
|
||||
# git2 = { path = "../../extern/git2-rs", features = ["vendored-openssl"]}
|
||||
# git2 = { git="https://github.com/extrawurst/git2-rs.git", rev="fc13dcc", features = ["vendored-openssl"]}
|
||||
# pinning to vendored openssl, using the git2 feature this gets lost with new resolver
|
||||
openssl-sys = { version = "0.9", features = ["vendored"], optional = true }
|
||||
rayon = "1.11"
|
||||
rayon-core = "1.13"
|
||||
scopetime = { path = "../scopetime", version = "0.1" }
|
||||
serde = { version = "1.0", features = ["derive"] }
|
||||
ssh-key = { version = "0.6.7", features = ["crypto", "encryption"] }
|
||||
thiserror = "2.0"
|
||||
unicode-truncate = "2.0"
|
||||
url = "2.5"
|
||||
thiserror = "1.0"
|
||||
|
||||
[dev-dependencies]
|
||||
env_logger = "0.11"
|
||||
invalidstring = { path = "../invalidstring", version = "0.1" }
|
||||
pretty_assertions = "1.4"
|
||||
serial_test = "3.3"
|
||||
tempfile = "3"
|
||||
|
||||
[features]
|
||||
default = ["trace-libgit"]
|
||||
trace-libgit = []
|
||||
vendor-openssl = ["openssl-sys"]
|
||||
tempfile = "3.1"
|
||||
|
|
@ -1,12 +1,6 @@
|
|||
# asyncgit
|
||||
|
||||
*allow using git2 in an asynchronous context*
|
||||
|
||||
This crate is designed as part of the [gitui](http://gitui.org) project.
|
||||
|
||||
`asyncgit` provides the primary interface to interact with *git* repositories. It is split into the main module and a `sync` part. The latter provides convenience wrapper for typical usage patterns against git repositories.
|
||||
|
||||
The primary goal however is to allow putting certain (potentially) long running [git2](https://github.com/rust-lang/git2-rs) calls onto a thread pool.[crossbeam-channel](https://github.com/crossbeam-rs/crossbeam) is then used to wait for a notification confirming the result.
|
||||
|
||||
In `gitui` this allows the main-thread and therefore the *ui* to stay responsive.
|
||||
*allow using git2 in a asynchronous context*
|
||||
|
||||
This crate is part of the [gitui](http://gitui.org) project.
|
||||
It is used put long running [git2](https://github.com/rust-lang/git2-rs) calls onto a thread pool and use [crossbeam-channel](https://github.com/crossbeam-rs/crossbeam) to wait for a message to confirm the call finished.
|
||||
|
|
@ -1,302 +0,0 @@
|
|||
//! provides `AsyncJob` trait and `AsyncSingleJob` struct
|
||||
|
||||
#![deny(clippy::expect_used)]
|
||||
|
||||
use crate::error::Result;
|
||||
use crossbeam_channel::Sender;
|
||||
use std::sync::{Arc, Mutex, RwLock};
|
||||
|
||||
/// Passed to `AsyncJob::run` allowing sending intermediate progress notifications
|
||||
pub struct RunParams<
|
||||
T: Copy + Send,
|
||||
P: Clone + Send + Sync + PartialEq,
|
||||
> {
|
||||
sender: Sender<T>,
|
||||
progress: Arc<RwLock<P>>,
|
||||
}
|
||||
|
||||
impl<T: Copy + Send, P: Clone + Send + Sync + PartialEq>
|
||||
RunParams<T, P>
|
||||
{
|
||||
/// send an intermediate update notification.
|
||||
/// do not confuse this with the return value of `run`.
|
||||
/// `send` should only be used about progress notifications
|
||||
/// and not for the final notification indicating the end of the async job.
|
||||
/// see `run` for more info
|
||||
pub fn send(&self, notification: T) -> Result<()> {
|
||||
self.sender.send(notification)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// set the current progress
|
||||
pub fn set_progress(&self, p: P) -> Result<bool> {
|
||||
Ok(if *self.progress.read()? == p {
|
||||
false
|
||||
} else {
|
||||
*(self.progress.write()?) = p;
|
||||
true
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/// trait that defines an async task we can run on a threadpool
|
||||
pub trait AsyncJob: Send + Sync + Clone {
|
||||
/// defines what notification type is used to communicate outside
|
||||
type Notification: Copy + Send;
|
||||
/// type of progress
|
||||
type Progress: Clone + Default + Send + Sync + PartialEq;
|
||||
|
||||
/// can run a synchronous time intensive task.
|
||||
/// the returned notification is used to tell interested parties
|
||||
/// that the job finished and the job can be access via `take_last`.
|
||||
/// prior to this final notification it is not safe to assume `take_last`
|
||||
/// will already return the correct job
|
||||
fn run(
|
||||
&mut self,
|
||||
params: RunParams<Self::Notification, Self::Progress>,
|
||||
) -> Result<Self::Notification>;
|
||||
|
||||
/// allows observers to get intermediate progress status if the job customizes it
|
||||
/// by default this will be returning `Self::Progress::default()`
|
||||
fn get_progress(&self) -> Self::Progress {
|
||||
Self::Progress::default()
|
||||
}
|
||||
}
|
||||
|
||||
/// Abstraction for a FIFO task queue that will only queue up **one** `next` job.
|
||||
/// It keeps overwriting the next job until it is actually taken to be processed
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct AsyncSingleJob<J: AsyncJob> {
|
||||
next: Arc<Mutex<Option<J>>>,
|
||||
last: Arc<Mutex<Option<J>>>,
|
||||
progress: Arc<RwLock<J::Progress>>,
|
||||
sender: Sender<J::Notification>,
|
||||
pending: Arc<Mutex<()>>,
|
||||
}
|
||||
|
||||
impl<J: 'static + AsyncJob> AsyncSingleJob<J> {
|
||||
///
|
||||
pub fn new(sender: Sender<J::Notification>) -> Self {
|
||||
Self {
|
||||
next: Arc::new(Mutex::new(None)),
|
||||
last: Arc::new(Mutex::new(None)),
|
||||
pending: Arc::new(Mutex::new(())),
|
||||
progress: Arc::new(RwLock::new(J::Progress::default())),
|
||||
sender,
|
||||
}
|
||||
}
|
||||
|
||||
///
|
||||
pub fn is_pending(&self) -> bool {
|
||||
self.pending.try_lock().is_err()
|
||||
}
|
||||
|
||||
/// makes sure `next` is cleared and returns `true` if it actually canceled something
|
||||
pub fn cancel(&self) -> bool {
|
||||
if let Ok(mut next) = self.next.lock() {
|
||||
if next.is_some() {
|
||||
*next = None;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
false
|
||||
}
|
||||
|
||||
/// take out last finished job
|
||||
pub fn take_last(&self) -> Option<J> {
|
||||
self.last.lock().map_or(None, |mut last| last.take())
|
||||
}
|
||||
|
||||
/// spawns `task` if nothing is running currently,
|
||||
/// otherwise schedules as `next` overwriting if `next` was set before.
|
||||
/// return `true` if the new task gets started right away.
|
||||
pub fn spawn(&self, task: J) -> bool {
|
||||
self.schedule_next(task);
|
||||
self.check_for_job()
|
||||
}
|
||||
|
||||
///
|
||||
pub fn progress(&self) -> Option<J::Progress> {
|
||||
self.progress.read().ok().map(|d| (*d).clone())
|
||||
}
|
||||
|
||||
fn check_for_job(&self) -> bool {
|
||||
if self.is_pending() {
|
||||
return false;
|
||||
}
|
||||
|
||||
if let Some(task) = self.take_next() {
|
||||
let self_clone = (*self).clone();
|
||||
rayon_core::spawn(move || {
|
||||
if let Err(e) = self_clone.run_job(task) {
|
||||
log::error!("async job error: {e}");
|
||||
}
|
||||
});
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
false
|
||||
}
|
||||
|
||||
fn run_job(&self, mut task: J) -> Result<()> {
|
||||
//limit the pending scope
|
||||
{
|
||||
let _pending = self.pending.lock()?;
|
||||
|
||||
let notification = task.run(RunParams {
|
||||
progress: self.progress.clone(),
|
||||
sender: self.sender.clone(),
|
||||
})?;
|
||||
|
||||
if let Ok(mut last) = self.last.lock() {
|
||||
*last = Some(task);
|
||||
}
|
||||
|
||||
self.sender.send(notification)?;
|
||||
}
|
||||
|
||||
self.check_for_job();
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn schedule_next(&self, task: J) {
|
||||
if let Ok(mut next) = self.next.lock() {
|
||||
*next = Some(task);
|
||||
}
|
||||
}
|
||||
|
||||
fn take_next(&self) -> Option<J> {
|
||||
self.next.lock().map_or(None, |mut next| next.take())
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::*;
|
||||
use crossbeam_channel::unbounded;
|
||||
use pretty_assertions::assert_eq;
|
||||
use std::{
|
||||
sync::atomic::{AtomicBool, AtomicU32, Ordering},
|
||||
thread,
|
||||
time::Duration,
|
||||
};
|
||||
|
||||
#[derive(Clone)]
|
||||
struct TestJob {
|
||||
v: Arc<AtomicU32>,
|
||||
finish: Arc<AtomicBool>,
|
||||
value_to_add: u32,
|
||||
}
|
||||
|
||||
type TestNotification = ();
|
||||
|
||||
impl AsyncJob for TestJob {
|
||||
type Notification = TestNotification;
|
||||
type Progress = ();
|
||||
|
||||
fn run(
|
||||
&mut self,
|
||||
_params: RunParams<Self::Notification, Self::Progress>,
|
||||
) -> Result<Self::Notification> {
|
||||
println!("[job] wait");
|
||||
|
||||
while !self.finish.load(Ordering::SeqCst) {
|
||||
std::thread::yield_now();
|
||||
}
|
||||
|
||||
println!("[job] sleep");
|
||||
|
||||
thread::sleep(Duration::from_millis(100));
|
||||
|
||||
println!("[job] done sleeping");
|
||||
|
||||
let res =
|
||||
self.v.fetch_add(self.value_to_add, Ordering::SeqCst);
|
||||
|
||||
println!("[job] value: {res}");
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_overwrite() {
|
||||
let (sender, receiver) = unbounded();
|
||||
|
||||
let job: AsyncSingleJob<TestJob> =
|
||||
AsyncSingleJob::new(sender);
|
||||
|
||||
let task = TestJob {
|
||||
v: Arc::new(AtomicU32::new(1)),
|
||||
finish: Arc::new(AtomicBool::new(false)),
|
||||
value_to_add: 1,
|
||||
};
|
||||
|
||||
assert!(job.spawn(task.clone()));
|
||||
task.finish.store(true, Ordering::SeqCst);
|
||||
thread::sleep(Duration::from_millis(10));
|
||||
|
||||
for _ in 0..5 {
|
||||
println!("spawn");
|
||||
assert!(!job.spawn(task.clone()));
|
||||
}
|
||||
|
||||
println!("recv");
|
||||
receiver.recv().unwrap();
|
||||
receiver.recv().unwrap();
|
||||
assert!(receiver.is_empty());
|
||||
|
||||
assert_eq!(
|
||||
task.v.load(std::sync::atomic::Ordering::SeqCst),
|
||||
3
|
||||
);
|
||||
}
|
||||
|
||||
fn wait_for_job(job: &AsyncSingleJob<TestJob>) {
|
||||
while job.is_pending() {
|
||||
thread::sleep(Duration::from_millis(10));
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_cancel() {
|
||||
let (sender, receiver) = unbounded();
|
||||
|
||||
let job: AsyncSingleJob<TestJob> =
|
||||
AsyncSingleJob::new(sender);
|
||||
|
||||
let task = TestJob {
|
||||
v: Arc::new(AtomicU32::new(1)),
|
||||
finish: Arc::new(AtomicBool::new(false)),
|
||||
value_to_add: 1,
|
||||
};
|
||||
|
||||
assert!(job.spawn(task.clone()));
|
||||
task.finish.store(true, Ordering::SeqCst);
|
||||
thread::sleep(Duration::from_millis(10));
|
||||
|
||||
for _ in 0..5 {
|
||||
println!("spawn");
|
||||
assert!(!job.spawn(task.clone()));
|
||||
}
|
||||
|
||||
println!("cancel");
|
||||
assert!(job.cancel());
|
||||
|
||||
task.finish.store(true, Ordering::SeqCst);
|
||||
|
||||
wait_for_job(&job);
|
||||
|
||||
println!("recv");
|
||||
receiver.recv().unwrap();
|
||||
println!("received");
|
||||
|
||||
assert_eq!(
|
||||
task.v.load(std::sync::atomic::Ordering::SeqCst),
|
||||
2
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,188 +0,0 @@
|
|||
use crate::{
|
||||
error::Result,
|
||||
hash,
|
||||
sync::{self, CommitId, FileBlame, RepoPath},
|
||||
AsyncGitNotification,
|
||||
};
|
||||
use crossbeam_channel::Sender;
|
||||
use std::{
|
||||
hash::Hash,
|
||||
sync::{
|
||||
atomic::{AtomicUsize, Ordering},
|
||||
Arc, Mutex,
|
||||
},
|
||||
};
|
||||
|
||||
///
|
||||
#[derive(Hash, Clone, PartialEq, Eq)]
|
||||
pub struct BlameParams {
|
||||
/// path to the file to blame
|
||||
pub file_path: String,
|
||||
/// blame at a specific revision
|
||||
pub commit_id: Option<CommitId>,
|
||||
}
|
||||
|
||||
struct Request<R, A>(R, Option<A>);
|
||||
|
||||
#[derive(Default, Clone)]
|
||||
struct LastResult<P, R> {
|
||||
params: P,
|
||||
result: R,
|
||||
}
|
||||
|
||||
///
|
||||
pub struct AsyncBlame {
|
||||
current: Arc<Mutex<Request<u64, FileBlame>>>,
|
||||
last: Arc<Mutex<Option<LastResult<BlameParams, FileBlame>>>>,
|
||||
sender: Sender<AsyncGitNotification>,
|
||||
pending: Arc<AtomicUsize>,
|
||||
repo: RepoPath,
|
||||
}
|
||||
|
||||
impl AsyncBlame {
|
||||
///
|
||||
pub fn new(
|
||||
repo: RepoPath,
|
||||
sender: &Sender<AsyncGitNotification>,
|
||||
) -> Self {
|
||||
Self {
|
||||
repo,
|
||||
current: Arc::new(Mutex::new(Request(0, None))),
|
||||
last: Arc::new(Mutex::new(None)),
|
||||
sender: sender.clone(),
|
||||
pending: Arc::new(AtomicUsize::new(0)),
|
||||
}
|
||||
}
|
||||
|
||||
///
|
||||
pub fn last(&self) -> Result<Option<(BlameParams, FileBlame)>> {
|
||||
let last = self.last.lock()?;
|
||||
|
||||
Ok(last.clone().map(|last_result| {
|
||||
(last_result.params, last_result.result)
|
||||
}))
|
||||
}
|
||||
|
||||
///
|
||||
pub fn refresh(&self) -> Result<()> {
|
||||
if let Ok(Some(param)) = self.get_last_param() {
|
||||
self.clear_current()?;
|
||||
self.request(param)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
///
|
||||
pub fn is_pending(&self) -> bool {
|
||||
self.pending.load(Ordering::Relaxed) > 0
|
||||
}
|
||||
|
||||
///
|
||||
pub fn request(
|
||||
&self,
|
||||
params: BlameParams,
|
||||
) -> Result<Option<FileBlame>> {
|
||||
log::trace!("request");
|
||||
|
||||
let hash = hash(¶ms);
|
||||
|
||||
{
|
||||
let mut current = self.current.lock()?;
|
||||
|
||||
if current.0 == hash {
|
||||
return Ok(current.1.clone());
|
||||
}
|
||||
|
||||
current.0 = hash;
|
||||
current.1 = None;
|
||||
}
|
||||
|
||||
let arc_current = Arc::clone(&self.current);
|
||||
let arc_last = Arc::clone(&self.last);
|
||||
let sender = self.sender.clone();
|
||||
let arc_pending = Arc::clone(&self.pending);
|
||||
let repo = self.repo.clone();
|
||||
|
||||
self.pending.fetch_add(1, Ordering::Relaxed);
|
||||
|
||||
rayon_core::spawn(move || {
|
||||
let notify = Self::get_blame_helper(
|
||||
&repo,
|
||||
params,
|
||||
&arc_last,
|
||||
&arc_current,
|
||||
hash,
|
||||
);
|
||||
|
||||
let notify = match notify {
|
||||
Err(err) => {
|
||||
log::error!("get_blame_helper error: {err}");
|
||||
true
|
||||
}
|
||||
Ok(notify) => notify,
|
||||
};
|
||||
|
||||
arc_pending.fetch_sub(1, Ordering::Relaxed);
|
||||
|
||||
sender
|
||||
.send(if notify {
|
||||
AsyncGitNotification::Blame
|
||||
} else {
|
||||
AsyncGitNotification::FinishUnchanged
|
||||
})
|
||||
.expect("error sending blame");
|
||||
});
|
||||
|
||||
Ok(None)
|
||||
}
|
||||
|
||||
fn get_blame_helper(
|
||||
repo_path: &RepoPath,
|
||||
params: BlameParams,
|
||||
arc_last: &Arc<
|
||||
Mutex<Option<LastResult<BlameParams, FileBlame>>>,
|
||||
>,
|
||||
arc_current: &Arc<Mutex<Request<u64, FileBlame>>>,
|
||||
hash: u64,
|
||||
) -> Result<bool> {
|
||||
let file_blame = sync::blame::blame_file(
|
||||
repo_path,
|
||||
¶ms.file_path,
|
||||
params.commit_id,
|
||||
)?;
|
||||
|
||||
let mut notify = false;
|
||||
{
|
||||
let mut current = arc_current.lock()?;
|
||||
if current.0 == hash {
|
||||
current.1 = Some(file_blame.clone());
|
||||
notify = true;
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
let mut last = arc_last.lock()?;
|
||||
*last = Some(LastResult {
|
||||
result: file_blame,
|
||||
params,
|
||||
});
|
||||
}
|
||||
|
||||
Ok(notify)
|
||||
}
|
||||
|
||||
fn get_last_param(&self) -> Result<Option<BlameParams>> {
|
||||
Ok(self
|
||||
.last
|
||||
.lock()?
|
||||
.clone()
|
||||
.map(|last_result| last_result.params))
|
||||
}
|
||||
|
||||
fn clear_current(&self) -> Result<()> {
|
||||
let mut current = self.current.lock()?;
|
||||
current.0 = 0;
|
||||
current.1 = None;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
|
@ -1,77 +0,0 @@
|
|||
use crate::{
|
||||
asyncjob::{AsyncJob, RunParams},
|
||||
error::Result,
|
||||
sync::{branch::get_branches_info, BranchInfo, RepoPath},
|
||||
AsyncGitNotification,
|
||||
};
|
||||
use std::sync::{Arc, Mutex};
|
||||
|
||||
enum JobState {
|
||||
Request {
|
||||
local_branches: bool,
|
||||
repo: RepoPath,
|
||||
},
|
||||
Response(Result<Vec<BranchInfo>>),
|
||||
}
|
||||
|
||||
///
|
||||
#[derive(Clone, Default)]
|
||||
pub struct AsyncBranchesJob {
|
||||
state: Arc<Mutex<Option<JobState>>>,
|
||||
}
|
||||
|
||||
///
|
||||
impl AsyncBranchesJob {
|
||||
///
|
||||
pub fn new(repo: RepoPath, local_branches: bool) -> Self {
|
||||
Self {
|
||||
state: Arc::new(Mutex::new(Some(JobState::Request {
|
||||
repo,
|
||||
local_branches,
|
||||
}))),
|
||||
}
|
||||
}
|
||||
|
||||
///
|
||||
pub fn result(&self) -> Option<Result<Vec<BranchInfo>>> {
|
||||
if let Ok(mut state) = self.state.lock() {
|
||||
if let Some(state) = state.take() {
|
||||
return match state {
|
||||
JobState::Request { .. } => None,
|
||||
JobState::Response(result) => Some(result),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
impl AsyncJob for AsyncBranchesJob {
|
||||
type Notification = AsyncGitNotification;
|
||||
type Progress = ();
|
||||
|
||||
fn run(
|
||||
&mut self,
|
||||
_params: RunParams<Self::Notification, Self::Progress>,
|
||||
) -> Result<Self::Notification> {
|
||||
if let Ok(mut state) = self.state.lock() {
|
||||
*state = state.take().map(|state| match state {
|
||||
JobState::Request {
|
||||
local_branches,
|
||||
repo,
|
||||
} => {
|
||||
let branches =
|
||||
get_branches_info(&repo, local_branches);
|
||||
|
||||
JobState::Response(branches)
|
||||
}
|
||||
JobState::Response(result) => {
|
||||
JobState::Response(result)
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
Ok(AsyncGitNotification::Branches)
|
||||
}
|
||||
}
|
||||
|
|
@ -1,47 +0,0 @@
|
|||
use crate::{
|
||||
error::Result,
|
||||
sync::{self, branch::get_branch_name, RepoPathRef},
|
||||
};
|
||||
use sync::Head;
|
||||
|
||||
///
|
||||
pub struct BranchName {
|
||||
last_result: Option<(Head, String)>,
|
||||
repo: RepoPathRef,
|
||||
}
|
||||
|
||||
impl BranchName {
|
||||
///
|
||||
pub const fn new(repo: RepoPathRef) -> Self {
|
||||
Self {
|
||||
repo,
|
||||
last_result: None,
|
||||
}
|
||||
}
|
||||
|
||||
///
|
||||
pub fn lookup(&mut self) -> Result<String> {
|
||||
let current_head = sync::get_head_tuple(&self.repo.borrow())?;
|
||||
|
||||
if let Some((last_head, branch_name)) =
|
||||
self.last_result.as_ref()
|
||||
{
|
||||
if *last_head == current_head {
|
||||
return Ok(branch_name.clone());
|
||||
}
|
||||
}
|
||||
|
||||
self.fetch(current_head)
|
||||
}
|
||||
|
||||
///
|
||||
pub fn last(&self) -> Option<String> {
|
||||
self.last_result.as_ref().map(|last| last.1.clone())
|
||||
}
|
||||
|
||||
fn fetch(&mut self, head: Head) -> Result<String> {
|
||||
let name = get_branch_name(&self.repo.borrow())?;
|
||||
self.last_result = Some((head, name.clone()));
|
||||
Ok(name)
|
||||
}
|
||||
}
|
||||
|
|
@ -1,7 +0,0 @@
|
|||
//! cached lookups:
|
||||
//! parts of the sync api that might take longer
|
||||
//! to compute but change seldom so doing them async might be overkill
|
||||
|
||||
mod branchname;
|
||||
|
||||
pub use branchname::BranchName;
|
||||
|
|
@ -1,146 +1,101 @@
|
|||
use crate::{
|
||||
error::Result,
|
||||
sync::{self, commit_files::OldNew, CommitId, RepoPath},
|
||||
AsyncGitNotification, StatusItem,
|
||||
error::Result, sync, AsyncNotification, StatusItem, CWD,
|
||||
};
|
||||
use crossbeam_channel::Sender;
|
||||
use std::sync::{
|
||||
atomic::{AtomicUsize, Ordering},
|
||||
Arc, Mutex,
|
||||
atomic::{AtomicUsize, Ordering},
|
||||
Arc, Mutex,
|
||||
};
|
||||
use sync::CommitId;
|
||||
|
||||
type ResultType = Vec<StatusItem>;
|
||||
struct Request<R, A>(R, A);
|
||||
|
||||
///
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
|
||||
pub struct CommitFilesParams {
|
||||
///
|
||||
pub id: CommitId,
|
||||
///
|
||||
pub other: Option<CommitId>,
|
||||
}
|
||||
|
||||
impl From<CommitId> for CommitFilesParams {
|
||||
fn from(id: CommitId) -> Self {
|
||||
Self { id, other: None }
|
||||
}
|
||||
}
|
||||
|
||||
impl From<(CommitId, CommitId)> for CommitFilesParams {
|
||||
fn from((id, other): (CommitId, CommitId)) -> Self {
|
||||
Self {
|
||||
id,
|
||||
other: Some(other),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<OldNew<CommitId>> for CommitFilesParams {
|
||||
fn from(old_new: OldNew<CommitId>) -> Self {
|
||||
Self {
|
||||
id: old_new.new,
|
||||
other: Some(old_new.old),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
///
|
||||
pub struct AsyncCommitFiles {
|
||||
current:
|
||||
Arc<Mutex<Option<Request<CommitFilesParams, ResultType>>>>,
|
||||
sender: Sender<AsyncGitNotification>,
|
||||
pending: Arc<AtomicUsize>,
|
||||
repo: RepoPath,
|
||||
current: Arc<Mutex<Option<Request<CommitId, ResultType>>>>,
|
||||
sender: Sender<AsyncNotification>,
|
||||
pending: Arc<AtomicUsize>,
|
||||
}
|
||||
|
||||
impl AsyncCommitFiles {
|
||||
///
|
||||
pub fn new(
|
||||
repo: RepoPath,
|
||||
sender: &Sender<AsyncGitNotification>,
|
||||
) -> Self {
|
||||
Self {
|
||||
repo,
|
||||
current: Arc::new(Mutex::new(None)),
|
||||
sender: sender.clone(),
|
||||
pending: Arc::new(AtomicUsize::new(0)),
|
||||
}
|
||||
}
|
||||
///
|
||||
pub fn new(sender: &Sender<AsyncNotification>) -> Self {
|
||||
Self {
|
||||
current: Arc::new(Mutex::new(None)),
|
||||
sender: sender.clone(),
|
||||
pending: Arc::new(AtomicUsize::new(0)),
|
||||
}
|
||||
}
|
||||
|
||||
///
|
||||
pub fn current(
|
||||
&self,
|
||||
) -> Result<Option<(CommitFilesParams, ResultType)>> {
|
||||
let c = self.current.lock()?;
|
||||
///
|
||||
pub fn current(
|
||||
&mut self,
|
||||
) -> Result<Option<(CommitId, ResultType)>> {
|
||||
let c = self.current.lock()?;
|
||||
|
||||
c.as_ref()
|
||||
.map_or(Ok(None), |c| Ok(Some((c.0, c.1.clone()))))
|
||||
}
|
||||
if let Some(c) = c.as_ref() {
|
||||
Ok(Some((c.0, c.1.clone())))
|
||||
} else {
|
||||
Ok(None)
|
||||
}
|
||||
}
|
||||
|
||||
///
|
||||
pub fn is_pending(&self) -> bool {
|
||||
self.pending.load(Ordering::Relaxed) > 0
|
||||
}
|
||||
///
|
||||
pub fn is_pending(&self) -> bool {
|
||||
self.pending.load(Ordering::Relaxed) > 0
|
||||
}
|
||||
|
||||
///
|
||||
pub fn fetch(&self, params: CommitFilesParams) -> Result<()> {
|
||||
if self.is_pending() {
|
||||
return Ok(());
|
||||
}
|
||||
///
|
||||
pub fn fetch(&mut self, id: CommitId) -> Result<()> {
|
||||
if self.is_pending() {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
log::trace!("request: {params:?}");
|
||||
log::trace!("request: {}", id.to_string());
|
||||
|
||||
{
|
||||
let current = self.current.lock()?;
|
||||
if let Some(c) = &*current {
|
||||
if c.0 == params {
|
||||
return Ok(());
|
||||
}
|
||||
}
|
||||
}
|
||||
{
|
||||
let current = self.current.lock()?;
|
||||
if let Some(ref c) = *current {
|
||||
if c.0 == id {
|
||||
return Ok(());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let arc_current = Arc::clone(&self.current);
|
||||
let sender = self.sender.clone();
|
||||
let arc_pending = Arc::clone(&self.pending);
|
||||
let repo = self.repo.clone();
|
||||
let arc_current = Arc::clone(&self.current);
|
||||
let sender = self.sender.clone();
|
||||
let arc_pending = Arc::clone(&self.pending);
|
||||
|
||||
self.pending.fetch_add(1, Ordering::Relaxed);
|
||||
rayon_core::spawn(move || {
|
||||
arc_pending.fetch_add(1, Ordering::Relaxed);
|
||||
|
||||
rayon_core::spawn(move || {
|
||||
Self::fetch_helper(&repo, params, &arc_current)
|
||||
.expect("failed to fetch");
|
||||
Self::fetch_helper(id, arc_current)
|
||||
.expect("failed to fetch");
|
||||
|
||||
arc_pending.fetch_sub(1, Ordering::Relaxed);
|
||||
arc_pending.fetch_sub(1, Ordering::Relaxed);
|
||||
|
||||
sender
|
||||
.send(AsyncGitNotification::CommitFiles)
|
||||
.expect("error sending");
|
||||
});
|
||||
sender
|
||||
.send(AsyncNotification::CommitFiles)
|
||||
.expect("error sending");
|
||||
});
|
||||
|
||||
Ok(())
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn fetch_helper(
|
||||
repo_path: &RepoPath,
|
||||
params: CommitFilesParams,
|
||||
arc_current: &Arc<
|
||||
Mutex<Option<Request<CommitFilesParams, ResultType>>>,
|
||||
>,
|
||||
) -> Result<()> {
|
||||
let res = sync::get_commit_files(
|
||||
repo_path,
|
||||
params.id,
|
||||
params.other,
|
||||
)?;
|
||||
fn fetch_helper(
|
||||
id: CommitId,
|
||||
arc_current: Arc<
|
||||
Mutex<Option<Request<CommitId, ResultType>>>,
|
||||
>,
|
||||
) -> Result<()> {
|
||||
let res = sync::get_commit_files(CWD, id)?;
|
||||
|
||||
log::trace!("get_commit_files: {:?} ({})", params, res.len());
|
||||
{
|
||||
let mut last = arc_current.lock()?;
|
||||
*last = Some(Request(id, res));
|
||||
}
|
||||
|
||||
{
|
||||
let mut current = arc_current.lock()?;
|
||||
*current = Some(Request(params, res));
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,221 +1,186 @@
|
|||
use crate::{
|
||||
error::Result,
|
||||
hash,
|
||||
sync::{
|
||||
self, commit_files::OldNew, diff::DiffOptions, CommitId,
|
||||
RepoPath,
|
||||
},
|
||||
AsyncGitNotification, FileDiff,
|
||||
error::Result, hash, sync, AsyncNotification, FileDiff, CWD,
|
||||
};
|
||||
use crossbeam_channel::Sender;
|
||||
use std::{
|
||||
hash::Hash,
|
||||
sync::{
|
||||
atomic::{AtomicUsize, Ordering},
|
||||
Arc, Mutex,
|
||||
},
|
||||
hash::Hash,
|
||||
sync::{
|
||||
atomic::{AtomicUsize, Ordering},
|
||||
Arc, Mutex,
|
||||
},
|
||||
};
|
||||
use sync::CommitId;
|
||||
|
||||
///
|
||||
#[derive(Debug, Hash, Clone, PartialEq, Eq)]
|
||||
#[derive(Hash, Clone, PartialEq)]
|
||||
pub enum DiffType {
|
||||
/// diff two commits
|
||||
Commits(OldNew<CommitId>),
|
||||
/// diff in a given commit
|
||||
Commit(CommitId),
|
||||
/// diff against staged file
|
||||
Stage,
|
||||
/// diff against file in workdir
|
||||
WorkDir,
|
||||
/// diff in a given commit
|
||||
Commit(CommitId),
|
||||
/// diff against staged file
|
||||
Stage,
|
||||
/// diff against file in workdir
|
||||
WorkDir,
|
||||
}
|
||||
|
||||
///
|
||||
#[derive(Debug, Hash, Clone, PartialEq, Eq)]
|
||||
#[derive(Hash, Clone, PartialEq)]
|
||||
pub struct DiffParams {
|
||||
/// path to the file to diff
|
||||
pub path: String,
|
||||
/// what kind of diff
|
||||
pub diff_type: DiffType,
|
||||
/// diff options
|
||||
pub options: DiffOptions,
|
||||
/// path to the file to diff
|
||||
pub path: String,
|
||||
/// what kind of diff
|
||||
pub diff_type: DiffType,
|
||||
}
|
||||
|
||||
struct Request<R, A>(R, Option<A>);
|
||||
|
||||
#[derive(Default, Clone)]
|
||||
struct LastResult<P, R> {
|
||||
params: P,
|
||||
result: R,
|
||||
params: P,
|
||||
hash: u64,
|
||||
result: R,
|
||||
}
|
||||
|
||||
///
|
||||
pub struct AsyncDiff {
|
||||
current: Arc<Mutex<Request<u64, FileDiff>>>,
|
||||
last: Arc<Mutex<Option<LastResult<DiffParams, FileDiff>>>>,
|
||||
sender: Sender<AsyncGitNotification>,
|
||||
pending: Arc<AtomicUsize>,
|
||||
repo: RepoPath,
|
||||
current: Arc<Mutex<Request<u64, FileDiff>>>,
|
||||
last: Arc<Mutex<Option<LastResult<DiffParams, FileDiff>>>>,
|
||||
sender: Sender<AsyncNotification>,
|
||||
pending: Arc<AtomicUsize>,
|
||||
}
|
||||
|
||||
impl AsyncDiff {
|
||||
///
|
||||
pub fn new(
|
||||
repo: RepoPath,
|
||||
sender: &Sender<AsyncGitNotification>,
|
||||
) -> Self {
|
||||
Self {
|
||||
repo,
|
||||
current: Arc::new(Mutex::new(Request(0, None))),
|
||||
last: Arc::new(Mutex::new(None)),
|
||||
sender: sender.clone(),
|
||||
pending: Arc::new(AtomicUsize::new(0)),
|
||||
}
|
||||
}
|
||||
///
|
||||
pub fn new(sender: Sender<AsyncNotification>) -> Self {
|
||||
Self {
|
||||
current: Arc::new(Mutex::new(Request(0, None))),
|
||||
last: Arc::new(Mutex::new(None)),
|
||||
sender,
|
||||
pending: Arc::new(AtomicUsize::new(0)),
|
||||
}
|
||||
}
|
||||
|
||||
///
|
||||
pub fn last(&self) -> Result<Option<(DiffParams, FileDiff)>> {
|
||||
let last = self.last.lock()?;
|
||||
///
|
||||
pub fn last(&mut self) -> Result<Option<(DiffParams, FileDiff)>> {
|
||||
let last = self.last.lock()?;
|
||||
|
||||
Ok(last.clone().map(|res| (res.params, res.result)))
|
||||
}
|
||||
Ok(match last.clone() {
|
||||
Some(res) => Some((res.params, res.result)),
|
||||
None => None,
|
||||
})
|
||||
}
|
||||
|
||||
///
|
||||
pub fn refresh(&self) -> Result<()> {
|
||||
if let Ok(Some(param)) = self.get_last_param() {
|
||||
self.clear_current()?;
|
||||
self.request(param)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
///
|
||||
pub fn refresh(&mut self) -> Result<()> {
|
||||
if let Ok(Some(param)) = self.get_last_param() {
|
||||
self.clear_current()?;
|
||||
self.request(param)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
///
|
||||
pub fn is_pending(&self) -> bool {
|
||||
self.pending.load(Ordering::Relaxed) > 0
|
||||
}
|
||||
///
|
||||
pub fn is_pending(&self) -> bool {
|
||||
self.pending.load(Ordering::Relaxed) > 0
|
||||
}
|
||||
|
||||
///
|
||||
pub fn request(
|
||||
&self,
|
||||
params: DiffParams,
|
||||
) -> Result<Option<FileDiff>> {
|
||||
log::trace!("request {params:?}");
|
||||
///
|
||||
pub fn request(
|
||||
&mut self,
|
||||
params: DiffParams,
|
||||
) -> Result<Option<FileDiff>> {
|
||||
log::trace!("request");
|
||||
|
||||
let hash = hash(¶ms);
|
||||
let hash = hash(¶ms);
|
||||
|
||||
{
|
||||
let mut current = self.current.lock()?;
|
||||
{
|
||||
let mut current = self.current.lock()?;
|
||||
|
||||
if current.0 == hash {
|
||||
return Ok(current.1.clone());
|
||||
}
|
||||
if current.0 == hash {
|
||||
return Ok(current.1.clone());
|
||||
}
|
||||
|
||||
current.0 = hash;
|
||||
current.1 = None;
|
||||
}
|
||||
current.0 = hash;
|
||||
current.1 = None;
|
||||
}
|
||||
|
||||
let arc_current = Arc::clone(&self.current);
|
||||
let arc_last = Arc::clone(&self.last);
|
||||
let sender = self.sender.clone();
|
||||
let arc_pending = Arc::clone(&self.pending);
|
||||
let repo = self.repo.clone();
|
||||
let arc_current = Arc::clone(&self.current);
|
||||
let arc_last = Arc::clone(&self.last);
|
||||
let sender = self.sender.clone();
|
||||
let arc_pending = Arc::clone(&self.pending);
|
||||
rayon_core::spawn(move || {
|
||||
arc_pending.fetch_add(1, Ordering::Relaxed);
|
||||
|
||||
self.pending.fetch_add(1, Ordering::Relaxed);
|
||||
let notify = AsyncDiff::get_diff_helper(
|
||||
params,
|
||||
arc_last,
|
||||
arc_current,
|
||||
hash,
|
||||
)
|
||||
.expect("error getting diff");
|
||||
|
||||
rayon_core::spawn(move || {
|
||||
let notify = Self::get_diff_helper(
|
||||
&repo,
|
||||
params,
|
||||
&arc_last,
|
||||
&arc_current,
|
||||
hash,
|
||||
);
|
||||
arc_pending.fetch_sub(1, Ordering::Relaxed);
|
||||
|
||||
let notify = match notify {
|
||||
Err(e) => {
|
||||
log::error!("get_diff_helper error: {e}");
|
||||
true
|
||||
}
|
||||
Ok(notify) => notify,
|
||||
};
|
||||
if notify {
|
||||
sender
|
||||
.send(AsyncNotification::Diff)
|
||||
.expect("error sending diff");
|
||||
}
|
||||
});
|
||||
|
||||
arc_pending.fetch_sub(1, Ordering::Relaxed);
|
||||
Ok(None)
|
||||
}
|
||||
|
||||
sender
|
||||
.send(if notify {
|
||||
AsyncGitNotification::Diff
|
||||
} else {
|
||||
AsyncGitNotification::FinishUnchanged
|
||||
})
|
||||
.expect("error sending diff");
|
||||
});
|
||||
fn get_diff_helper(
|
||||
params: DiffParams,
|
||||
arc_last: Arc<
|
||||
Mutex<Option<LastResult<DiffParams, FileDiff>>>,
|
||||
>,
|
||||
arc_current: Arc<Mutex<Request<u64, FileDiff>>>,
|
||||
hash: u64,
|
||||
) -> Result<bool> {
|
||||
let res = match params.diff_type {
|
||||
DiffType::Stage => {
|
||||
sync::diff::get_diff(CWD, params.path.clone(), true)?
|
||||
}
|
||||
DiffType::WorkDir => {
|
||||
sync::diff::get_diff(CWD, params.path.clone(), false)?
|
||||
}
|
||||
DiffType::Commit(id) => sync::diff::get_diff_commit(
|
||||
CWD,
|
||||
id,
|
||||
params.path.clone(),
|
||||
)?,
|
||||
};
|
||||
|
||||
Ok(None)
|
||||
}
|
||||
let mut notify = false;
|
||||
{
|
||||
let mut current = arc_current.lock()?;
|
||||
if current.0 == hash {
|
||||
current.1 = Some(res.clone());
|
||||
notify = true;
|
||||
}
|
||||
}
|
||||
|
||||
fn get_diff_helper(
|
||||
repo_path: &RepoPath,
|
||||
params: DiffParams,
|
||||
arc_last: &Arc<
|
||||
Mutex<Option<LastResult<DiffParams, FileDiff>>>,
|
||||
>,
|
||||
arc_current: &Arc<Mutex<Request<u64, FileDiff>>>,
|
||||
hash: u64,
|
||||
) -> Result<bool> {
|
||||
let res = match params.diff_type {
|
||||
DiffType::Stage => sync::diff::get_diff(
|
||||
repo_path,
|
||||
¶ms.path,
|
||||
true,
|
||||
Some(params.options),
|
||||
)?,
|
||||
DiffType::WorkDir => sync::diff::get_diff(
|
||||
repo_path,
|
||||
¶ms.path,
|
||||
false,
|
||||
Some(params.options),
|
||||
)?,
|
||||
DiffType::Commit(id) => sync::diff::get_diff_commit(
|
||||
repo_path,
|
||||
id,
|
||||
params.path.clone(),
|
||||
Some(params.options),
|
||||
)?,
|
||||
DiffType::Commits(ids) => sync::diff::get_diff_commits(
|
||||
repo_path,
|
||||
ids,
|
||||
params.path.clone(),
|
||||
Some(params.options),
|
||||
)?,
|
||||
};
|
||||
{
|
||||
let mut last = arc_last.lock()?;
|
||||
*last = Some(LastResult {
|
||||
result: res,
|
||||
hash,
|
||||
params,
|
||||
});
|
||||
}
|
||||
|
||||
let mut notify = false;
|
||||
{
|
||||
let mut current = arc_current.lock()?;
|
||||
if current.0 == hash {
|
||||
current.1 = Some(res.clone());
|
||||
notify = true;
|
||||
}
|
||||
}
|
||||
Ok(notify)
|
||||
}
|
||||
|
||||
{
|
||||
let mut last = arc_last.lock()?;
|
||||
*last = Some(LastResult {
|
||||
result: res,
|
||||
params,
|
||||
});
|
||||
}
|
||||
fn get_last_param(&self) -> Result<Option<DiffParams>> {
|
||||
Ok(self.last.lock()?.clone().map(|e| e.params))
|
||||
}
|
||||
|
||||
Ok(notify)
|
||||
}
|
||||
|
||||
fn get_last_param(&self) -> Result<Option<DiffParams>> {
|
||||
Ok(self.last.lock()?.clone().map(|e| e.params))
|
||||
}
|
||||
|
||||
fn clear_current(&self) -> Result<()> {
|
||||
let mut current = self.current.lock()?;
|
||||
current.0 = 0;
|
||||
current.1 = None;
|
||||
Ok(())
|
||||
}
|
||||
fn clear_current(&mut self) -> Result<()> {
|
||||
let mut current = self.current.lock()?;
|
||||
current.0 = 0;
|
||||
current.1 = None;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,350 +1,24 @@
|
|||
use std::{
|
||||
num::TryFromIntError, path::StripPrefixError,
|
||||
string::FromUtf8Error,
|
||||
};
|
||||
use thiserror::Error;
|
||||
|
||||
///
|
||||
#[derive(Error, Debug)]
|
||||
pub enum GixError {
|
||||
///
|
||||
#[error("gix::discover error: {0}")]
|
||||
Discover(#[from] Box<gix::discover::Error>),
|
||||
|
||||
///
|
||||
#[error("gix::head::peel::to_commit error: {0}")]
|
||||
HeadPeelToCommit(#[from] gix::head::peel::to_commit::Error),
|
||||
|
||||
///
|
||||
#[error("gix::object::find::existing::with_conversion::Error error: {0}")]
|
||||
ObjectFindExistingWithConversion(
|
||||
#[from] gix::object::find::existing::with_conversion::Error,
|
||||
),
|
||||
|
||||
///
|
||||
#[error("gix::objs::decode::Error error: {0}")]
|
||||
ObjsDecode(#[from] gix::objs::decode::Error),
|
||||
|
||||
///
|
||||
#[error("gix::pathspec::init::Error error: {0}")]
|
||||
PathspecInit(#[from] Box<gix::pathspec::init::Error>),
|
||||
|
||||
///
|
||||
#[error("gix::reference::find::existing error: {0}")]
|
||||
ReferenceFindExisting(
|
||||
#[from] gix::reference::find::existing::Error,
|
||||
),
|
||||
|
||||
///
|
||||
#[error("gix::reference::head_tree_id::Error error: {0}")]
|
||||
ReferenceHeadTreeId(#[from] gix::reference::head_tree_id::Error),
|
||||
|
||||
///
|
||||
#[error("gix::reference::iter::Error error: {0}")]
|
||||
ReferenceIter(#[from] gix::reference::iter::Error),
|
||||
|
||||
///
|
||||
#[error("gix::reference::iter::init::Error error: {0}")]
|
||||
ReferenceIterInit(#[from] gix::reference::iter::init::Error),
|
||||
|
||||
///
|
||||
#[error("gix::revision::walk error: {0}")]
|
||||
RevisionWalk(#[from] gix::revision::walk::Error),
|
||||
|
||||
///
|
||||
#[error("gix::status::Error error: {0}")]
|
||||
Status(#[from] Box<gix::status::Error>),
|
||||
|
||||
///
|
||||
#[error("gix::status::index_worktree::Error error: {0}")]
|
||||
StatusIndexWorktree(
|
||||
#[from] Box<gix::status::index_worktree::Error>,
|
||||
),
|
||||
|
||||
///
|
||||
#[error("gix::status::into_iter::Error error: {0}")]
|
||||
StatusIntoIter(#[from] Box<gix::status::into_iter::Error>),
|
||||
|
||||
///
|
||||
#[error("gix::status::iter::Error error: {0}")]
|
||||
StatusIter(#[from] Box<gix::status::iter::Error>),
|
||||
|
||||
///
|
||||
#[error("gix::status::tree_index::Error error: {0}")]
|
||||
StatusTreeIndex(#[from] Box<gix::status::tree_index::Error>),
|
||||
|
||||
///
|
||||
#[error("gix::worktree::open_index::Error error: {0}")]
|
||||
WorktreeOpenIndex(#[from] Box<gix::worktree::open_index::Error>),
|
||||
}
|
||||
|
||||
///
|
||||
#[derive(Error, Debug)]
|
||||
pub enum Error {
|
||||
///
|
||||
#[error("`{0}`")]
|
||||
Generic(String),
|
||||
#[error("`{0}`")]
|
||||
Generic(String),
|
||||
|
||||
///
|
||||
#[error("git: no head found")]
|
||||
NoHead,
|
||||
#[error("git: no head found")]
|
||||
NoHead,
|
||||
|
||||
///
|
||||
#[error("git: conflict during rebase")]
|
||||
RebaseConflict,
|
||||
#[error("io error:{0}")]
|
||||
Io(#[from] std::io::Error),
|
||||
|
||||
///
|
||||
#[error("git: remote url not found")]
|
||||
UnknownRemote,
|
||||
|
||||
///
|
||||
#[error("git: inconclusive remotes")]
|
||||
NoDefaultRemoteFound,
|
||||
|
||||
///
|
||||
#[error("git: work dir error")]
|
||||
NoWorkDir,
|
||||
|
||||
///
|
||||
#[error("git: uncommitted changes")]
|
||||
UncommittedChanges,
|
||||
|
||||
///
|
||||
#[error("git: can\u{2019}t run blame on a binary file")]
|
||||
NoBlameOnBinaryFile,
|
||||
|
||||
///
|
||||
#[error("binary file")]
|
||||
BinaryFile,
|
||||
|
||||
///
|
||||
#[error("io error:{0}")]
|
||||
Io(#[from] std::io::Error),
|
||||
|
||||
///
|
||||
#[error("git error:{0}")]
|
||||
Git(#[from] git2::Error),
|
||||
|
||||
///
|
||||
#[error("git config error: {0}")]
|
||||
GitConfig(String),
|
||||
|
||||
///
|
||||
#[error("strip prefix error: {0}")]
|
||||
StripPrefix(#[from] StripPrefixError),
|
||||
|
||||
///
|
||||
#[error("utf8 error:{0}")]
|
||||
Utf8Conversion(#[from] FromUtf8Error),
|
||||
|
||||
///
|
||||
#[error("TryFromInt error:{0}")]
|
||||
IntConversion(#[from] TryFromIntError),
|
||||
|
||||
///
|
||||
#[error("EasyCast error:{0}")]
|
||||
EasyCast(#[from] easy_cast::Error),
|
||||
|
||||
///
|
||||
#[error("no parent of commit found")]
|
||||
NoParent,
|
||||
|
||||
///
|
||||
#[error("not on a branch")]
|
||||
NoBranch,
|
||||
|
||||
///
|
||||
#[error("rayon error: {0}")]
|
||||
ThreadPool(#[from] rayon_core::ThreadPoolBuildError),
|
||||
|
||||
///
|
||||
#[error("git hook error: {0}")]
|
||||
Hooks(#[from] git2_hooks::HooksError),
|
||||
|
||||
///
|
||||
#[error("sign builder error: {0}")]
|
||||
SignBuilder(#[from] crate::sync::sign::SignBuilderError),
|
||||
|
||||
///
|
||||
#[error("sign error: {0}")]
|
||||
Sign(#[from] crate::sync::sign::SignError),
|
||||
|
||||
///
|
||||
#[error("gix error:{0}")]
|
||||
Gix(#[from] GixError),
|
||||
|
||||
///
|
||||
#[error("amend error: config commit.gpgsign=true detected.\ngpg signing is not supported for amending non-last commits")]
|
||||
SignAmendNonLastCommit,
|
||||
|
||||
///
|
||||
#[error("reword error: config commit.gpgsign=true detected.\ngpg signing is not supported for rewording non-last commits")]
|
||||
SignRewordNonLastCommit,
|
||||
|
||||
///
|
||||
#[error("reword error: config commit.gpgsign=true detected.\ngpg signing is not supported for rewording commits with staged changes\ntry unstaging or stashing your changes")]
|
||||
SignRewordLastCommitStaged,
|
||||
#[error("git error:{0}")]
|
||||
Git(#[from] git2::Error),
|
||||
}
|
||||
|
||||
///
|
||||
pub type Result<T> = std::result::Result<T, Error>;
|
||||
|
||||
impl<T> From<std::sync::PoisonError<T>> for Error {
|
||||
fn from(error: std::sync::PoisonError<T>) -> Self {
|
||||
Self::Generic(format!("poison error: {error}"))
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> From<crossbeam_channel::SendError<T>> for Error {
|
||||
fn from(error: crossbeam_channel::SendError<T>) -> Self {
|
||||
Self::Generic(format!("send error: {error}"))
|
||||
}
|
||||
}
|
||||
|
||||
impl From<gix::discover::Error> for GixError {
|
||||
fn from(error: gix::discover::Error) -> Self {
|
||||
Self::Discover(Box::new(error))
|
||||
}
|
||||
}
|
||||
|
||||
impl From<gix::discover::Error> for Error {
|
||||
fn from(error: gix::discover::Error) -> Self {
|
||||
Self::Gix(GixError::from(error))
|
||||
}
|
||||
}
|
||||
|
||||
impl From<gix::head::peel::to_commit::Error> for Error {
|
||||
fn from(error: gix::head::peel::to_commit::Error) -> Self {
|
||||
Self::Gix(GixError::from(error))
|
||||
}
|
||||
}
|
||||
|
||||
impl From<gix::object::find::existing::with_conversion::Error>
|
||||
for Error
|
||||
{
|
||||
fn from(
|
||||
error: gix::object::find::existing::with_conversion::Error,
|
||||
) -> Self {
|
||||
Self::Gix(GixError::from(error))
|
||||
}
|
||||
}
|
||||
|
||||
impl From<gix::objs::decode::Error> for Error {
|
||||
fn from(error: gix::objs::decode::Error) -> Self {
|
||||
Self::Gix(GixError::from(error))
|
||||
}
|
||||
}
|
||||
|
||||
impl From<gix::pathspec::init::Error> for GixError {
|
||||
fn from(error: gix::pathspec::init::Error) -> Self {
|
||||
Self::PathspecInit(Box::new(error))
|
||||
}
|
||||
}
|
||||
|
||||
impl From<gix::pathspec::init::Error> for Error {
|
||||
fn from(error: gix::pathspec::init::Error) -> Self {
|
||||
Self::Gix(GixError::from(error))
|
||||
}
|
||||
}
|
||||
|
||||
impl From<gix::reference::find::existing::Error> for Error {
|
||||
fn from(error: gix::reference::find::existing::Error) -> Self {
|
||||
Self::Gix(GixError::from(error))
|
||||
}
|
||||
}
|
||||
|
||||
impl From<gix::reference::head_tree_id::Error> for Error {
|
||||
fn from(error: gix::reference::head_tree_id::Error) -> Self {
|
||||
Self::Gix(GixError::from(error))
|
||||
}
|
||||
}
|
||||
|
||||
impl From<gix::reference::iter::Error> for Error {
|
||||
fn from(error: gix::reference::iter::Error) -> Self {
|
||||
Self::Gix(GixError::from(error))
|
||||
}
|
||||
}
|
||||
|
||||
impl From<gix::reference::iter::init::Error> for Error {
|
||||
fn from(error: gix::reference::iter::init::Error) -> Self {
|
||||
Self::Gix(GixError::from(error))
|
||||
}
|
||||
}
|
||||
|
||||
impl From<gix::revision::walk::Error> for Error {
|
||||
fn from(error: gix::revision::walk::Error) -> Self {
|
||||
Self::Gix(GixError::from(error))
|
||||
}
|
||||
}
|
||||
|
||||
impl From<gix::status::Error> for GixError {
|
||||
fn from(error: gix::status::Error) -> Self {
|
||||
Self::Status(Box::new(error))
|
||||
}
|
||||
}
|
||||
|
||||
impl From<gix::status::Error> for Error {
|
||||
fn from(error: gix::status::Error) -> Self {
|
||||
Self::Gix(GixError::from(error))
|
||||
}
|
||||
}
|
||||
|
||||
impl From<gix::status::iter::Error> for GixError {
|
||||
fn from(error: gix::status::iter::Error) -> Self {
|
||||
Self::StatusIter(Box::new(error))
|
||||
}
|
||||
}
|
||||
|
||||
impl From<gix::status::iter::Error> for Error {
|
||||
fn from(error: gix::status::iter::Error) -> Self {
|
||||
Self::Gix(GixError::from(error))
|
||||
}
|
||||
}
|
||||
|
||||
impl From<gix::status::into_iter::Error> for GixError {
|
||||
fn from(error: gix::status::into_iter::Error) -> Self {
|
||||
Self::StatusIntoIter(Box::new(error))
|
||||
}
|
||||
}
|
||||
|
||||
impl From<gix::status::into_iter::Error> for Error {
|
||||
fn from(error: gix::status::into_iter::Error) -> Self {
|
||||
Self::Gix(GixError::from(error))
|
||||
}
|
||||
}
|
||||
|
||||
impl From<gix::status::index_worktree::Error> for GixError {
|
||||
fn from(error: gix::status::index_worktree::Error) -> Self {
|
||||
Self::StatusIndexWorktree(Box::new(error))
|
||||
}
|
||||
}
|
||||
|
||||
impl From<gix::status::index_worktree::Error> for Error {
|
||||
fn from(error: gix::status::index_worktree::Error) -> Self {
|
||||
Self::Gix(GixError::from(error))
|
||||
}
|
||||
}
|
||||
|
||||
impl From<gix::status::tree_index::Error> for GixError {
|
||||
fn from(error: gix::status::tree_index::Error) -> Self {
|
||||
Self::StatusTreeIndex(Box::new(error))
|
||||
}
|
||||
}
|
||||
|
||||
impl From<gix::status::tree_index::Error> for Error {
|
||||
fn from(error: gix::status::tree_index::Error) -> Self {
|
||||
Self::Gix(GixError::from(error))
|
||||
}
|
||||
}
|
||||
|
||||
impl From<gix::worktree::open_index::Error> for GixError {
|
||||
fn from(error: gix::worktree::open_index::Error) -> Self {
|
||||
Self::WorktreeOpenIndex(Box::new(error))
|
||||
}
|
||||
}
|
||||
|
||||
impl From<gix::worktree::open_index::Error> for Error {
|
||||
fn from(error: gix::worktree::open_index::Error) -> Self {
|
||||
Self::Gix(GixError::from(error))
|
||||
}
|
||||
fn from(error: std::sync::PoisonError<T>) -> Self {
|
||||
Error::Generic(format!("poison error: {}", error))
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,69 +0,0 @@
|
|||
//!
|
||||
|
||||
use crate::{
|
||||
asyncjob::{AsyncJob, RunParams},
|
||||
error::Result,
|
||||
sync::remotes::fetch_all,
|
||||
sync::{cred::BasicAuthCredential, RepoPath},
|
||||
AsyncGitNotification, ProgressPercent,
|
||||
};
|
||||
|
||||
use std::sync::{Arc, Mutex};
|
||||
|
||||
enum JobState {
|
||||
Request(Option<BasicAuthCredential>),
|
||||
Response(Result<()>),
|
||||
}
|
||||
|
||||
///
|
||||
#[derive(Clone)]
|
||||
pub struct AsyncFetchJob {
|
||||
state: Arc<Mutex<Option<JobState>>>,
|
||||
repo: RepoPath,
|
||||
}
|
||||
|
||||
///
|
||||
impl AsyncFetchJob {
|
||||
///
|
||||
pub fn new(
|
||||
repo: RepoPath,
|
||||
basic_credential: Option<BasicAuthCredential>,
|
||||
) -> Self {
|
||||
Self {
|
||||
repo,
|
||||
state: Arc::new(Mutex::new(Some(JobState::Request(
|
||||
basic_credential,
|
||||
)))),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl AsyncJob for AsyncFetchJob {
|
||||
type Notification = AsyncGitNotification;
|
||||
type Progress = ProgressPercent;
|
||||
|
||||
fn run(
|
||||
&mut self,
|
||||
_params: RunParams<Self::Notification, Self::Progress>,
|
||||
) -> Result<Self::Notification> {
|
||||
if let Ok(mut state) = self.state.lock() {
|
||||
*state = state.take().map(|state| match state {
|
||||
JobState::Request(basic_credentials) => {
|
||||
//TODO: support progress
|
||||
let result = fetch_all(
|
||||
&self.repo,
|
||||
&basic_credentials,
|
||||
&None,
|
||||
);
|
||||
|
||||
JobState::Response(result)
|
||||
}
|
||||
JobState::Response(result) => {
|
||||
JobState::Response(result)
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
Ok(AsyncGitNotification::Fetch)
|
||||
}
|
||||
}
|
||||