Open editor in diff view (#2904)
8
.cargo/config.toml
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
[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"
|
||||
2
.clippy.toml
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
msrv = "1.88.0"
|
||||
cognitive-complexity-threshold = 18
|
||||
3
.editorconfig
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
root = true
|
||||
[*.rs]
|
||||
indent_style = tab
|
||||
1
.github/FUNDING.yml
vendored
Normal file
|
|
@ -0,0 +1 @@
|
|||
github: extrawurst
|
||||
32
.github/ISSUE_TEMPLATE/bug_report.md
vendored
Normal file
|
|
@ -0,0 +1,32 @@
|
|||
---
|
||||
name: Bug report
|
||||
about: Create a report to help us improve
|
||||
title: ''
|
||||
labels: 'bug'
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
**Describe the bug**
|
||||
A clear and concise description of what the bug is.
|
||||
|
||||
**To Reproduce**
|
||||
Steps to reproduce the behavior:
|
||||
1. Go to '...'
|
||||
2. Click on '....'
|
||||
3. Scroll down to '....'
|
||||
4. See error
|
||||
|
||||
**Expected behavior**
|
||||
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`]
|
||||
|
||||
**Additional context**
|
||||
Add any other context about the problem here.
|
||||
20
.github/ISSUE_TEMPLATE/feature_request.md
vendored
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
---
|
||||
name: Feature request
|
||||
about: Suggest an idea for this project
|
||||
title: ''
|
||||
labels: 'feature-request'
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
**Is your feature request related to a problem? Please describe.**
|
||||
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
|
||||
|
||||
**Describe the solution you'd like**
|
||||
A clear and concise description of what you want to happen.
|
||||
|
||||
**Describe alternatives you've considered**
|
||||
A clear and concise description of any alternative solutions or features you've considered.
|
||||
|
||||
**Additional context**
|
||||
Add any other context or screenshots about the feature request here.
|
||||
16
.github/PULL_REQUEST_TEMPLATE.md
vendored
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
<!---
|
||||
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
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
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'
|
||||
18
.github/stale.yml
vendored
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
# Number of days of inactivity before an issue becomes stale
|
||||
daysUntilStale: 180
|
||||
# Number of days of inactivity before a stale issue is closed
|
||||
daysUntilClose: 14
|
||||
# Issues with these labels will never be considered stale
|
||||
exemptLabels:
|
||||
- pinned
|
||||
- security
|
||||
- nostale
|
||||
# Label to use when marking an issue as stale
|
||||
staleLabel: dormant
|
||||
# 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
|
||||
for your contributions.
|
||||
# Comment to post when closing a stale issue. Set to `false` to disable
|
||||
closeComment: false
|
||||
23
.github/workflows/brew.yml
vendored
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
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 }}
|
||||
134
.github/workflows/cd.yml
vendored
Normal file
|
|
@ -0,0 +1,134 @@
|
|||
name: CD
|
||||
|
||||
on:
|
||||
push:
|
||||
tags:
|
||||
- "*"
|
||||
workflow_dispatch:
|
||||
|
||||
permissions:
|
||||
contents: write
|
||||
|
||||
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: Get version
|
||||
id: get_version
|
||||
run: echo "version=${GITHUB_REF/refs\/tags\//}" >> $GITHUB_OUTPUT
|
||||
|
||||
- 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
|
||||
|
||||
- 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: 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
|
||||
325
.github/workflows/ci.yml
vendored
Normal file
|
|
@ -0,0 +1,325 @@
|
|||
name: CI
|
||||
|
||||
on:
|
||||
schedule:
|
||||
- cron: "0 2 * * *"
|
||||
push:
|
||||
branches: ["*"]
|
||||
pull_request:
|
||||
branches: [master]
|
||||
|
||||
env:
|
||||
CARGO_TERM_COLOR: always
|
||||
|
||||
jobs:
|
||||
build:
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
os: [ubuntu-latest, macos-latest, windows-latest]
|
||||
rust: [nightly, stable, "1.88"]
|
||||
runs-on: ${{ matrix.os }}
|
||||
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:
|
||||
shared-key: ${{ matrix.os }}-${{ env.cache-name }}-${{ matrix.rust }}
|
||||
|
||||
- name: MacOS Workaround
|
||||
if: matrix.os == 'macos-latest'
|
||||
run: cargo clean -p serde_derive -p thiserror
|
||||
|
||||
- name: Install Rust
|
||||
uses: dtolnay/rust-toolchain@master
|
||||
with:
|
||||
toolchain: ${{ matrix.rust }}
|
||||
components: clippy
|
||||
|
||||
- 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
|
||||
|
||||
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
|
||||
|
||||
- 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 }}
|
||||
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
|
||||
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 }}-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
|
||||
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
|
||||
|
||||
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
|
||||
125
.github/workflows/nightly.yml
vendored
Normal file
|
|
@ -0,0 +1,125 @@
|
|||
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
|
||||
5
.gitignore
vendored
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
/target
|
||||
/release
|
||||
.DS_Store
|
||||
/.idea/
|
||||
flamegraph.svg
|
||||
13
.vscode/launch.json
vendored
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
{
|
||||
"version": "0.2.0",
|
||||
"configurations": [
|
||||
{
|
||||
"name": "(OSX) Launch",
|
||||
"type": "lldb",
|
||||
"request": "launch",
|
||||
"program": "${workspaceRoot}/target/debug/gitui",
|
||||
"args": [],
|
||||
"cwd": "${workspaceRoot}",
|
||||
}
|
||||
]
|
||||
}
|
||||
4
.vscode/settings.json
vendored
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
{
|
||||
"editor.formatOnSave": true,
|
||||
"workbench.settings.enableNaturalLanguageSearch": false,
|
||||
}
|
||||
1018
CHANGELOG.md
Normal file
26
CONTRIBUTING.md
Normal file
|
|
@ -0,0 +1,26 @@
|
|||
# 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
|
||||
5203
Cargo.lock
generated
Normal file
110
Cargo.toml
Normal file
|
|
@ -0,0 +1,110 @@
|
|||
[package]
|
||||
name = "gitui"
|
||||
version = "0.28.1"
|
||||
authors = ["extrawurst <mail@rusticorn.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"
|
||||
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",
|
||||
]
|
||||
|
||||
[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" }
|
||||
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" }
|
||||
|
||||
[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"]
|
||||
|
||||
# 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
|
||||
|
||||
[profile.release]
|
||||
opt-level = "z" # Optimize for size.
|
||||
strip = "debuginfo"
|
||||
lto = true
|
||||
codegen-units = 1
|
||||
26
FAQ.md
Normal file
|
|
@ -0,0 +1,26 @@
|
|||
|
||||
|
||||
## <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.
|
||||
51
KEY_CONFIG.md
Normal file
|
|
@ -0,0 +1,51 @@
|
|||
# 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))
|
||||
21
LICENSE.md
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
MIT License
|
||||
|
||||
Copyright (c) 2025 gitui-org
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
120
Makefile
Normal file
|
|
@ -0,0 +1,120 @@
|
|||
|
||||
.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}
|
||||
|
||||
debug:
|
||||
RUST_BACKTRACE=true cargo run --features=timing -- ${ARGS}
|
||||
|
||||
build-release:
|
||||
cargo build --release --locked
|
||||
|
||||
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
|
||||
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
|
||||
|
||||
test:
|
||||
cargo nextest run --workspace
|
||||
|
||||
fmt:
|
||||
cargo fmt -- --check
|
||||
|
||||
clippy:
|
||||
cargo clippy --workspace --all-features
|
||||
|
||||
clippy-nightly:
|
||||
cargo +nightly clippy --workspace --all-features
|
||||
|
||||
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
|
||||
|
||||
install:
|
||||
cargo install --path "." --offline --locked
|
||||
|
||||
install-timing:
|
||||
cargo install --features=timing --path "." --offline --locked
|
||||
|
||||
licenses:
|
||||
cargo bundle-licenses --format toml --output THIRDPARTY.toml
|
||||
|
||||
clean:
|
||||
cargo clean
|
||||
14
NIGHTLIES.md
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
# 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)
|
||||
301
README.md
Normal file
|
|
@ -0,0 +1,301 @@
|
|||
<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]
|
||||
|
||||
</h1>
|
||||
|
||||
[s0]: https://github.com/gitui-org/gitui/workflows/CI/badge.svg
|
||||
[l0]: https://github.com/gitui-org/gitui/actions
|
||||
[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
|
||||
|
||||
<h5 align="center">GitUI provides you with the comfort of a git GUI but right in your terminal</h1>
|
||||
|
||||

|
||||
|
||||
## <a name="table-of-contents"></a> Table of Contents
|
||||
|
||||
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)
|
||||
|
||||
## 1. <a name="features"></a> Features <small><sup>[Top ▲](#table-of-contents)</sup></small>
|
||||
|
||||
- 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)))
|
||||
|
||||
## 2. <a name="motivation"></a> Motivation <small><sup>[Top ▲](#table-of-contents)</sup></small>
|
||||
|
||||
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.
|
||||
|
||||
Unfortunately popular git GUIs all fail on giant repositories or become unresponsive and unusable.
|
||||
|
||||
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.
|
||||
|
||||
## 3. <a name="bench"></a> Benchmarks <small><sup>[Top ▲](#table-of-contents)</sup></small>
|
||||
|
||||
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):
|
||||
|
||||
| | 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** ✅ |
|
||||
|
||||
## 4. <a name="roadmap"></a> Road(map) to 1.0 <small><sup>[Top ▲](#table-of-contents)</sup></small>
|
||||
|
||||
These are the high level goals before calling out `1.0`:
|
||||
|
||||
* 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))
|
||||
|
||||
## 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/)
|
||||
|
||||
```sh
|
||||
pacman -S gitui
|
||||
```
|
||||
|
||||
##### 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)
|
||||
|
||||
```
|
||||
winget install gitui
|
||||
```
|
||||
|
||||
##### [Scoop](https://github.com/ScoopInstaller/Main/blob/master/bucket/gitui.json) (Windows)
|
||||
|
||||
```
|
||||
scoop install gitui
|
||||
```
|
||||
|
||||
##### [Chocolatey](https://chocolatey.org/packages/gitui) (Windows)
|
||||
|
||||
```
|
||||
choco install gitui
|
||||
```
|
||||
|
||||
##### [Mise](https://github.com/jdx/mise)
|
||||
|
||||
```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>
|
||||
|
||||

|
||||
|
||||
`gitui` should automatically work on both light and dark terminal themes.
|
||||
|
||||
However, you can customize everything to your liking: See [Themes](THEMES.md).
|
||||
|
||||
## 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>
|
||||
90
THEMES.md
Normal file
|
|
@ -0,0 +1,90 @@
|
|||
# Themes
|
||||
|
||||
default on light terminal:
|
||||

|
||||
|
||||
## Configuration
|
||||
|
||||
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)
|
||||
* `$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`.
|
||||
BIN
assets/add-remote.png
Normal file
|
After Width: | Height: | Size: 67 KiB |
BIN
assets/amend.gif
Normal file
|
After Width: | Height: | Size: 153 KiB |
BIN
assets/bad-credentials.png
Normal file
|
After Width: | Height: | Size: 69 KiB |
BIN
assets/binary_diff.png
Normal file
|
After Width: | Height: | Size: 31 KiB |
BIN
assets/blame-goto-line.png
Normal file
|
After Width: | Height: | Size: 302 KiB |
BIN
assets/blame.gif
Normal file
|
After Width: | Height: | Size: 1.5 MiB |
BIN
assets/branch-validation.gif
Normal file
|
After Width: | Height: | Size: 318 KiB |
BIN
assets/branches.gif
Normal file
|
After Width: | Height: | Size: 148 KiB |
BIN
assets/by-line-ops.gif
Normal file
|
After Width: | Height: | Size: 527 KiB |
BIN
assets/chars_and_branchname.gif
Normal file
|
After Width: | Height: | Size: 98 KiB |
BIN
assets/checkout-remote.gif
Normal file
|
After Width: | Height: | Size: 127 KiB |
BIN
assets/cmdbar.gif
Normal file
|
After Width: | Height: | Size: 122 KiB |
BIN
assets/commit-details.gif
Normal file
|
After Width: | Height: | Size: 913 KiB |
BIN
assets/commit-msg-history.gif
Normal file
|
After Width: | Height: | Size: 481 KiB |
BIN
assets/commit-msg-length-limit.gif
Normal file
|
After Width: | Height: | Size: 82 KiB |
BIN
assets/compact-tree.png
Normal file
|
After Width: | Height: | Size: 38 KiB |
BIN
assets/compare.gif
Normal file
|
After Width: | Height: | Size: 653 KiB |
BIN
assets/delete-tag-remote.gif
Normal file
|
After Width: | Height: | Size: 243 KiB |
BIN
assets/diff-empty-line.png
Normal file
|
After Width: | Height: | Size: 136 KiB |
BIN
assets/discard-changes-on-checkout.png
Normal file
|
After Width: | Height: | Size: 96 KiB |
BIN
assets/drop-multiple-stashes.gif
Normal file
|
After Width: | Height: | Size: 98 KiB |
BIN
assets/emojified-commit-message.png
Normal file
|
After Width: | Height: | Size: 299 KiB |
1
assets/expandable-commands.drawio
Normal file
|
|
@ -0,0 +1 @@
|
|||
<mxfile host="app.diagrams.net" modified="2020-05-23T13:11:59.516Z" agent="5.0 (Macintosh; Intel Mac OS X 10_15_4) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/13.1 Safari/605.1.15" etag="nKCWIzIC1T2-_TwD7yCE" version="13.1.3" type="device"><diagram id="KDklGPkv8WkujI4HOg4Q" name="Page-1">5Zhdr5MwAIZ/DZcnGe2Acek+1GiciYsx8a6HdtBYKJZOxvn1tqOMsXLiNGESdrPRtx/Q9wH6Fgeu0uM7gfLkE8eEOWCGjw5cOwAEC1/9aqGqBQ+GtRALimvJbYUdfSFGnBn1QDEpOg0l50zSvCtGPMtIJDsaEoKX3WZ7zrpnzVFMLGEXIWar3yiWSa0uQNDq7wmNk+bMrm/ml6KmsZlJkSDMywsJbhy4EpzL+ig9rgjT3jW+1P3evlJ7vjBBMnlLh+rj9oO/3e23n+HL5vv8iX3FX57MKL8QO5gJm4uVVeOA4IcMEz3IzIHLMqGS7HIU6dpSIVdaIlOmSq46NMMRIcnx1et0z7NXdw3hKZGiUk2aDg366qpctv5Dz2jJhfdgZkRkmMfnsVtb1IFx5i9cAiN0CVy5BHpcgj0uucFQLkHLpYinKZVKc7xl5HhryzU1f9m1ppCC/yArzrhQSsYz1XK5p4xdSYjROFPFSPlFlL7UblL13L4xFSnFWJ+ml0WX1gA4QGjjCHrv2YFgzC0YgugZ1jDW04YRjgyGZ8EopF53Tixqz6bMA/gj4+FbPFIuDI7iZMaUccDgzzgW98QRjG95nfujCyGLEboU/GsI8YZyKXzgEHKN4/xS/V/v2eahecgU4s3HRsPe7D1UDPHdsQGx95WMFMWj5JDghj3TXXOIa+d0hLGjvzjprnF2Cok+SrUd2XOh/xQpOmlK1qLSQym8KyU7vSeE5fVTk0yahXfDRqo/lA7Fwo7uPw9N2HK8zbTzlrWgDLevVcX2S/Gp7uJzO9z8Bg==</diagram></mxfile>
|
||||
BIN
assets/fuzzy-find-branch.gif
Normal file
|
After Width: | Height: | Size: 900 KiB |
BIN
assets/fuzzy-find-matches.gif
Normal file
|
After Width: | Height: | Size: 444 KiB |
BIN
assets/fuzzy-find.gif
Normal file
|
After Width: | Height: | Size: 415 KiB |
BIN
assets/gitui-signing.png
Normal file
|
After Width: | Height: | Size: 22 KiB |
BIN
assets/light-theme.png
Normal file
|
After Width: | Height: | Size: 418 KiB |
1
assets/log-commit-info.drawio
Normal file
|
|
@ -0,0 +1 @@
|
|||
<mxfile host="app.diagrams.net" modified="2020-06-02T16:58:00.925Z" agent="5.0 (Macintosh; Intel Mac OS X 10_15_4) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/13.1 Safari/605.1.15" etag="XbmHwEoPIuusTyLd3JQZ" version="13.1.13" type="device"><diagram id="t1-bsFE1bwoXy9pVcyMy" name="Page-1">7VzbcuI4EP0aV+0+QNmyxeUxQJjULsluLTNJzb6kFCzAE9vy2CKQfP1KvttSKmQLYQ+Qh2BaQrLPabVa3Q2aOfZ2X0IUrG+JjV0N6PZOMycaAEA3DfbCJa+JpG8ME8EqdOxEZBSCufOGU6GeSjeOjaNKR0qIS52gKlwQ38cLWpGhMCTbarclcauzBmiFBcF8gVxR+uDYdJ1IB6BfyG+ws1pnMxu99Pk8lHVOnyRaI5tsSyLzWjPHISE0ufJ2Y+xy8DJcks9N32nNbyzEPt3nA4PHR3ezfQBvsEP/Ivff/ny5HnZ6ySgvyN2kD5zeLH3NEAjJxrcxH0TXzNF27VA8D9CCt24Z50y2pp7L3hnsUryp9D5fcEjxriRKb/ILJh6m4SvrkumMlQKWaoyVqcK2wN/op7J1GftBKkQp56t87AIWdpEi8wmUjBaiZFRRMiUomQMJSmCoCiWzhSiBGkqWRJekKFmqUIICSlcbuiahZl4x8ZQQjd9zD3kcDZfdzugJhZrFLBfpLoiXN61oDFEv6RKyq1gyQRQnQwGLveo6/2eI/eY3V0k33UBP5vIHeo5s1B9E9lAgjWFNq8xENCTPeExcftsTn/is52jpuG5NhFxn5bO3Ll7yEThvDjOtV6mYkuAdLajqScQaHX81i0eZwELylQ/QuK5AVarSb/+CsvSmQRoIIN3iKOK7+gH0WAPmMv77WJk9x7b5THvo8yF4MGvKOhR5gDK7poqGoUDDPY5v2CX+ik/o2+w/3gUhY8d5wbHL5nkO5a5SRpje7Xbjj4TY4xNGwYa/2pwN5gS2zjApWVBwvwWlbIMygEDlOGWqmQWlfvGY4uKxjrl4DNEpGLsk4itCg6PraKHBiQLtXzAIcdi8LastATgQ6RgelQ5xT2nfxgv33HgNUxlMos2fOu5h7PQvs/FaEi/RPKayZgPXWDBO2V7AKgW9hs03EE++nALRaJwuBRKTrYqCO++Ph1v9bvbPj+nu+/zf2f3wedQRfZbGLbZVj9DIzpOyONZBXDspSqKeThxmYk/Fr7PqJ3iJXweOqZVW+7Sy7kYcNbgqBUn0fRsHibk2TUZXpTC1MFLfyZWikfCqFKY2xczag4p4oEnCvlo9TKzPKQ7WyGdXE2be+VNMs+BxFMTyAsnezw1PXo0WySbAo8nh6gn9pvNzK2B3qkuvfueXHFl9SXzaWSLPYRPFH/eIT6KYi0qXKE4LxtHqYFfMm8WyNQDrGUjIwOHSOL2Wv8vAgjFcTDLh1/zGIMcHMow/6mvkfTOl+F/DgGKYhJS8pWhIgM4bMrXlAj5VRXW5MFZeLo/Vl0uM+G1pqkRn93mYvCl/jkKxIbcAec9h8dz6ayGGJXGi+MXYg1JbugAKZKxSI1sIecOqNH8d5fhtDnVZWFWAtJ+gKYmCs6WV6HimVgd331txhBXN9r6xBGWOkhhJuCTFmk2KNZEEk6pG5nm1aUeXBX3kwTdlO7oh1iScQ96rdqa2JCc8VWkvOQ1i4OGS9zroElK3gsRoyInlueoBqCOmueSIi/GQBPHGUgYKfKv+x6Af10KJ8RWIlj08uJz9Lme/y9nv7M9+/T33YXUGSoxszphjpMW1D0m665SrHwRGjln+ICekhTFUuG+dobI0haQo5PSrHerHLUmVoapiBzkJ8pKTUyx2yBRfknk6bmZdXl8imoTTgbzpcxIQYzvNGeBsm4LtS4lmsJTrGTBFjns+zgNsm/MgqYVqge7ukac2ezKYlEWfwBnEQmDbYiFA9GxTH46bi04tjXTShsPQPzYc0uI0ZdyI/vWcIrpJyTFiOlJ+/KcoUHNIbws9gsXKotUleqQGSxk9oued50+zoMmMrBKyQEJWFlh5KqIq50NgvRaxaQJN0Y8/4/UlhMEap0fm81/W1/sENri+kD79ubu9ebv7vvW+RnT+2Pu2lf5GQDl3kjC0f+rlyid0zZAG+hT5Cy7JspN6nuh/J3R9SdlcUjYnnLJBEbJtZP96mRvBvEmM4CdcQknmRn6IVWXyxKP+J6qKPzBqF+t1sV4nab3O3mxJio2Pa7bEyFviWM+c6CC1Xwo8Zm6Y0t9G42AfhBirV/0OJ5B8jdaQZfcO8YVyKTFipVKZGDYrt9F5hP9siBr2uzWmIOhKfiVkCEWuLKiIK7FoI9/J/ybBJjgffkwdHJUfLd1nirbSDmNe/wc=</diagram></mxfile>
|
||||
BIN
assets/log-search.gif
Normal file
|
After Width: | Height: | Size: 2.7 MiB |
BIN
assets/logo.png
Normal file
|
After Width: | Height: | Size: 36 KiB |
1
assets/logo.svg
Normal file
|
|
@ -0,0 +1 @@
|
|||
<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>
|
||||
|
After Width: | Height: | Size: 3.5 KiB |
BIN
assets/long-msg-indicator.gif
Normal file
|
After Width: | Height: | Size: 136 KiB |
BIN
assets/merge-commit-abort.gif
Normal file
|
After Width: | Height: | Size: 639 KiB |
BIN
assets/msg-scrolling.gif
Normal file
|
After Width: | Height: | Size: 219 KiB |
BIN
assets/multiline-texteditor.gif
Normal file
|
After Width: | Height: | Size: 1 MiB |
BIN
assets/newlines.gif
Normal file
|
After Width: | Height: | Size: 6.6 KiB |
1
assets/options.drawio
Normal file
|
|
@ -0,0 +1 @@
|
|||
<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>
|
||||
BIN
assets/options.gif
Normal file
|
After Width: | Height: | Size: 203 KiB |
BIN
assets/popup-stacking.gif
Normal file
|
After Width: | Height: | Size: 781 KiB |
BIN
assets/pull.gif
Normal file
|
After Width: | Height: | Size: 18 KiB |
BIN
assets/push.gif
Normal file
|
After Width: | Height: | Size: 50 KiB |
BIN
assets/push_tags.gif
Normal file
|
After Width: | Height: | Size: 155 KiB |
BIN
assets/rebase.png
Normal file
|
After Width: | Height: | Size: 275 KiB |
BIN
assets/reset_in_log.gif
Normal file
|
After Width: | Height: | Size: 1.2 MiB |
BIN
assets/revert-commit.gif
Normal file
|
After Width: | Height: | Size: 682 KiB |
BIN
assets/revision-file-tree.gif
Normal file
|
After Width: | Height: | Size: 1.8 MiB |
BIN
assets/reword.gif
Normal file
|
After Width: | Height: | Size: 2.6 MiB |
BIN
assets/screenshots/s00-diff.png
Normal file
|
After Width: | Height: | Size: 511 KiB |
BIN
assets/screenshots/s01-log.png
Normal file
|
After Width: | Height: | Size: 1,000 KiB |
BIN
assets/screenshots/s02-revert.png
Normal file
|
After Width: | Height: | Size: 421 KiB |
BIN
assets/screenshots/s03-commit.png
Normal file
|
After Width: | Height: | Size: 492 KiB |
BIN
assets/scrollbar.gif
Normal file
|
After Width: | Height: | Size: 392 KiB |
BIN
assets/select-copy.gif
Normal file
|
After Width: | Height: | Size: 207 KiB |
BIN
assets/spinner.gif
Normal file
|
After Width: | Height: | Size: 61 KiB |
BIN
assets/stash_pop.gif
Normal file
|
After Width: | Height: | Size: 277 KiB |
1
assets/stashing.drawio
Normal file
BIN
assets/stashing.gif
Normal file
|
After Width: | Height: | Size: 1.2 MiB |
BIN
assets/submodules.gif
Normal file
|
After Width: | Height: | Size: 640 KiB |
BIN
assets/syntax-highlighting-blame.png
Normal file
|
After Width: | Height: | Size: 750 KiB |
BIN
assets/tag-annotation.gif
Normal file
|
After Width: | Height: | Size: 285 KiB |
BIN
assets/tag-remote-marker.gif
Normal file
|
After Width: | Height: | Size: 285 KiB |
BIN
assets/tagging.gif
Normal file
|
After Width: | Height: | Size: 257 KiB |
BIN
assets/tags-list-popup.gif
Normal file
|
After Width: | Height: | Size: 1.4 MiB |
BIN
assets/termux-android.jpg
Normal file
|
After Width: | Height: | Size: 641 KiB |
BIN
assets/undo-last-commit.gif
Normal file
|
After Width: | Height: | Size: 1,019 KiB |
BIN
assets/vi_support.gif
Normal file
|
After Width: | Height: | Size: 135 KiB |
52
asyncgit/Cargo.toml
Normal file
|
|
@ -0,0 +1,52 @@
|
|||
[package]
|
||||
name = "asyncgit"
|
||||
version = "0.28.1"
|
||||
authors = ["extrawurst <mail@rusticorn.com>"]
|
||||
edition = "2021"
|
||||
description = "allow using git2 in a asynchronous context"
|
||||
homepage = "https://github.com/gitui-org/gitui"
|
||||
repository = "https://github.com/gitui-org/gitui"
|
||||
readme = "README.md"
|
||||
license = "MIT"
|
||||
categories = ["asynchronous", "concurrency"]
|
||||
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",
|
||||
] }
|
||||
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"
|
||||
|
||||
[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"]
|
||||
1
asyncgit/LICENSE.md
Symbolic link
|
|
@ -0,0 +1 @@
|
|||
../LICENSE.md
|
||||
12
asyncgit/README.md
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
# 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.
|
||||
|
||||
302
asyncgit/src/asyncjob/mod.rs
Normal file
|
|
@ -0,0 +1,302 @@
|
|||
//! 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
|
||||
);
|
||||
}
|
||||
}
|
||||
188
asyncgit/src/blame.rs
Normal file
|
|
@ -0,0 +1,188 @@
|
|||
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(())
|
||||
}
|
||||
}
|
||||
77
asyncgit/src/branches.rs
Normal file
|
|
@ -0,0 +1,77 @@
|
|||
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)
|
||||
}
|
||||
}
|
||||