Showing preview only (1,253K chars total). Download the full file or copy to clipboard to get everything.
Repository: gitui-org/gitui
Branch: master
Commit: 49555ce96687
Files: 211
Total size: 1.2 MB
Directory structure:
gitextract_1za1en4i/
├── .cargo/
│ └── config.toml
├── .clippy.toml
├── .editorconfig
├── .github/
│ ├── FUNDING.yml
│ ├── ISSUE_TEMPLATE/
│ │ ├── bug_report.md
│ │ └── feature_request.md
│ ├── PULL_REQUEST_TEMPLATE.md
│ ├── dependabot.yml
│ ├── stale.yml
│ └── workflows/
│ ├── brew.yml
│ ├── cd.yml
│ ├── ci.yml
│ └── nightly.yml
├── .gitignore
├── .vscode/
│ ├── launch.json
│ └── settings.json
├── CHANGELOG.md
├── CONTRIBUTING.md
├── Cargo.toml
├── FAQ.md
├── KEY_CONFIG.md
├── LICENSE.md
├── Makefile
├── NIGHTLIES.md
├── README.md
├── THEMES.md
├── assets/
│ ├── expandable-commands.drawio
│ ├── log-commit-info.drawio
│ ├── options.drawio
│ └── stashing.drawio
├── asyncgit/
│ ├── Cargo.toml
│ ├── README.md
│ └── src/
│ ├── asyncjob/
│ │ └── mod.rs
│ ├── blame.rs
│ ├── branches.rs
│ ├── cached/
│ │ ├── branchname.rs
│ │ └── mod.rs
│ ├── commit_files.rs
│ ├── diff.rs
│ ├── error.rs
│ ├── fetch_job.rs
│ ├── filter_commits.rs
│ ├── lib.rs
│ ├── progress.rs
│ ├── pull.rs
│ ├── push.rs
│ ├── push_tags.rs
│ ├── remote_progress.rs
│ ├── remote_tags.rs
│ ├── revlog.rs
│ ├── status.rs
│ ├── sync/
│ │ ├── blame.rs
│ │ ├── branch/
│ │ │ ├── merge_commit.rs
│ │ │ ├── merge_ff.rs
│ │ │ ├── merge_rebase.rs
│ │ │ ├── mod.rs
│ │ │ └── rename.rs
│ │ ├── commit.rs
│ │ ├── commit_details.rs
│ │ ├── commit_files.rs
│ │ ├── commit_filter.rs
│ │ ├── commit_revert.rs
│ │ ├── commits_info.rs
│ │ ├── config.rs
│ │ ├── cred.rs
│ │ ├── diff.rs
│ │ ├── hooks.rs
│ │ ├── hunks.rs
│ │ ├── ignore.rs
│ │ ├── logwalker.rs
│ │ ├── merge.rs
│ │ ├── mod.rs
│ │ ├── patches.rs
│ │ ├── rebase.rs
│ │ ├── remotes/
│ │ │ ├── callbacks.rs
│ │ │ ├── mod.rs
│ │ │ ├── push.rs
│ │ │ └── tags.rs
│ │ ├── repository.rs
│ │ ├── reset.rs
│ │ ├── reword.rs
│ │ ├── sign.rs
│ │ ├── staging/
│ │ │ ├── discard_tracked.rs
│ │ │ ├── mod.rs
│ │ │ └── stage_tracked.rs
│ │ ├── stash.rs
│ │ ├── state.rs
│ │ ├── status.rs
│ │ ├── submodules.rs
│ │ ├── tags.rs
│ │ ├── tree.rs
│ │ └── utils.rs
│ ├── tags.rs
│ └── treefiles.rs
├── build.rs
├── deny.toml
├── filetreelist/
│ ├── Cargo.toml
│ ├── README.md
│ └── src/
│ ├── error.rs
│ ├── filetree.rs
│ ├── filetreeitems.rs
│ ├── item.rs
│ ├── lib.rs
│ ├── tree_iter.rs
│ └── treeitems_iter.rs
├── git2-hooks/
│ ├── Cargo.toml
│ ├── README.md
│ └── src/
│ ├── error.rs
│ ├── hookspath.rs
│ └── lib.rs
├── git2-testing/
│ ├── Cargo.toml
│ ├── README.md
│ └── src/
│ └── lib.rs
├── invalidstring/
│ ├── Cargo.toml
│ ├── README.md
│ └── src/
│ └── lib.rs
├── rust-toolchain.toml
├── rustfmt.toml
├── scopetime/
│ ├── Cargo.toml
│ ├── README.md
│ └── src/
│ └── lib.rs
├── src/
│ ├── app.rs
│ ├── args.rs
│ ├── bug_report.rs
│ ├── clipboard.rs
│ ├── cmdbar.rs
│ ├── components/
│ │ ├── changes.rs
│ │ ├── command.rs
│ │ ├── commit_details/
│ │ │ ├── compare_details.rs
│ │ │ ├── details.rs
│ │ │ ├── mod.rs
│ │ │ └── style.rs
│ │ ├── commitlist.rs
│ │ ├── cred.rs
│ │ ├── diff.rs
│ │ ├── mod.rs
│ │ ├── revision_files.rs
│ │ ├── status_tree.rs
│ │ ├── syntax_text.rs
│ │ ├── textinput.rs
│ │ └── utils/
│ │ ├── emoji.rs
│ │ ├── filetree.rs
│ │ ├── logitems.rs
│ │ ├── mod.rs
│ │ ├── scroll_horizontal.rs
│ │ ├── scroll_vertical.rs
│ │ └── statustree.rs
│ ├── input.rs
│ ├── keys/
│ │ ├── key_config.rs
│ │ ├── key_list.rs
│ │ ├── mod.rs
│ │ └── symbols.rs
│ ├── main.rs
│ ├── notify_mutex.rs
│ ├── options.rs
│ ├── popup_stack.rs
│ ├── popups/
│ │ ├── blame_file.rs
│ │ ├── branchlist.rs
│ │ ├── checkout_option.rs
│ │ ├── commit.rs
│ │ ├── compare_commits.rs
│ │ ├── confirm.rs
│ │ ├── create_branch.rs
│ │ ├── create_remote.rs
│ │ ├── externaleditor.rs
│ │ ├── fetch.rs
│ │ ├── file_revlog.rs
│ │ ├── fuzzy_find.rs
│ │ ├── goto_line.rs
│ │ ├── help.rs
│ │ ├── inspect_commit.rs
│ │ ├── log_search.rs
│ │ ├── mod.rs
│ │ ├── msg.rs
│ │ ├── options.rs
│ │ ├── pull.rs
│ │ ├── push.rs
│ │ ├── push_tags.rs
│ │ ├── remotelist.rs
│ │ ├── rename_branch.rs
│ │ ├── rename_remote.rs
│ │ ├── reset.rs
│ │ ├── revision_files.rs
│ │ ├── stashmsg.rs
│ │ ├── submodules.rs
│ │ ├── tag_commit.rs
│ │ ├── taglist.rs
│ │ └── update_remote_url.rs
│ ├── queue.rs
│ ├── spinner.rs
│ ├── string_utils.rs
│ ├── strings.rs
│ ├── tabs/
│ │ ├── files.rs
│ │ ├── mod.rs
│ │ ├── revlog.rs
│ │ ├── stashing.rs
│ │ ├── stashlist.rs
│ │ └── status.rs
│ ├── ui/
│ │ ├── mod.rs
│ │ ├── reflow.rs
│ │ ├── scrollbar.rs
│ │ ├── scrolllist.rs
│ │ ├── stateful_paragraph.rs
│ │ ├── style.rs
│ │ └── syntax_text.rs
│ └── watcher.rs
├── typos.toml
├── vim_style_key_config.ron
└── wix/
├── License.rtf
├── Microsoft_VC142_CRT_x64.msm
└── main.wxs
================================================
FILE CONTENTS
================================================
================================================
FILE: .cargo/config.toml
================================================
[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"
================================================
FILE: .clippy.toml
================================================
msrv = "1.88.0"
cognitive-complexity-threshold = 18
================================================
FILE: .editorconfig
================================================
root = true
[*.rs]
indent_style = tab
================================================
FILE: .github/FUNDING.yml
================================================
github: extrawurst
================================================
FILE: .github/ISSUE_TEMPLATE/bug_report.md
================================================
---
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.
================================================
FILE: .github/ISSUE_TEMPLATE/feature_request.md
================================================
---
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.
================================================
FILE: .github/PULL_REQUEST_TEMPLATE.md
================================================
<!---
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
================================================
FILE: .github/dependabot.yml
================================================
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'
================================================
FILE: .github/stale.yml
================================================
# 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
================================================
FILE: .github/workflows/brew.yml
================================================
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 }}
================================================
FILE: .github/workflows/cd.yml
================================================
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
================================================
FILE: .github/workflows/ci.yml
================================================
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: cargo-sort
run: |
cargo install cargo-sort --force
cargo sort -c -w
- 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
================================================
FILE: .github/workflows/nightly.yml
================================================
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
================================================
FILE: .gitignore
================================================
/target
/release
.DS_Store
/.idea/
flamegraph.svg
================================================
FILE: .vscode/launch.json
================================================
{
"version": "0.2.0",
"configurations": [
{
"name": "(OSX) Launch",
"type": "lldb",
"request": "launch",
"program": "${workspaceRoot}/target/debug/gitui",
"args": [],
"cwd": "${workspaceRoot}",
}
]
}
================================================
FILE: .vscode/settings.json
================================================
{
"editor.formatOnSave": true,
"workbench.settings.enableNaturalLanguageSearch": false,
}
================================================
FILE: CHANGELOG.md
================================================
# Changelog
All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
## Unreleased
### Changed
* improve `gitui --version` message [[@hlsxx](https://github.com/hlsxx)] ([#2838](https://github.com/gitui-org/gitui/issues/2838))
* rust msrv bumped to `1.88`
### Fixed
* fix extremely slow status loading in large repositories by replacing time-based cache invalidation with generation counter [[@DannyStoll1](https://github.com/DannyStoll1)] ([#2823](https://github.com/gitui-org/gitui/issues/2823))
* fix panic when renaming or updating remote URL with no remotes configured [[@xvchris](https://github.com/xvchris)] ([#2868](https://github.com/gitui-org/gitui/issues/2868))
## [0.28.0] - 2025-12-14
**discard changes on checkout**

**go to line in blame**

### Added
* support choosing checkout branch method when status is not empty [[@fatpandac](https://github.com/fatpandac)] ([#2404](https://github.com/extrawurst/gitui/issues/2404))
* support pre-push hook [[@xlai89](https://github.com/xlai89)] ([#1933](https://github.com/extrawurst/gitui/issues/1933))
* message tab supports pageUp and pageDown [[@xlai89](https://github.com/xlai89)] ([#2623](https://github.com/extrawurst/gitui/issues/2623))
* files and status tab support pageUp and pageDown [[@fatpandac](https://github.com/fatpandac)] ([#1951](https://github.com/extrawurst/gitui/issues/1951))
* support loading custom syntax highlighting themes from a file [[@acuteenvy](https://github.com/acuteenvy)] ([#2565](https://github.com/gitui-org/gitui/pull/2565))
* select syntax highlighting theme out of the defaults from syntect [[@vasilismanol](https://github.com/vasilismanol)] ([#1931](https://github.com/extrawurst/gitui/issues/1931))
* new command-line option to override the default log file path (`--logfile`) [[@acuteenvy](https://github.com/acuteenvy)] ([#2539](https://github.com/gitui-org/gitui/pull/2539))
* dx: `make check` checks Cargo.toml dependency ordering using `cargo sort` [[@naseschwarz](https://github.com/naseschwarz)]
* add `use_selection_fg` to theme file to allow customizing selection foreground color [[@Upsylonbare](https://github.com/Upsylonbare)] ([#2515](https://github.com/gitui-org/gitui/pull/2515))
* add "go to line" command for the blame view [[@andrea-berling](https://github.com/andrea-berling)] ([#2262](https://github.com/extrawurst/gitui/pull/2262))
* add `--file` cli flag to open the files tab with the given file already selected [[@laktak](https://github.com/laktak)] ([#2510](https://github.com/gitui-org/gitui/issues/2510))
* add the ability to specify a custom keybinding/symbols file via the cli [[@0x61nas](https://github.com/0x61nas)] ([#2731](https://github.com/gitui-org/gitui/pull/2731))
### Changed
* execute git-hooks directly if possible (on *nix) else use sh instead of bash (without reading SHELL variable) [[@Joshix](https://github.com/Joshix-1)] ([#2483](https://github.com/extrawurst/gitui/pull/2483))
* improve error messages [[@acuteenvy](https://github.com/acuteenvy)] ([#2617](https://github.com/gitui-org/gitui/pull/2617))
* improve syntax highlighting file detection [[@acuteenvy](https://github.com/acuteenvy)] ([#2524](https://github.com/extrawurst/gitui/pull/2524))
* after commit: jump back to unstaged area [[@tommady](https://github.com/tommady)] ([#2476](https://github.com/extrawurst/gitui/issues/2476))
* the default key to close the commit error message popup is now the Escape key [[@wessamfathi](https://github.com/wessamfathi)] ([#2552](https://github.com/extrawurst/gitui/issues/2552))
* use OSC52 copying in case other methods fail [[@naseschwarz](https://github.com/naseschwarz)] ([#2366](https://github.com/gitui-org/gitui/issues/2366))
* push: respect `branch.*.merge` when push default is upstream [[@vlad-anger](https://github.com/vlad-anger)] ([#2542](https://github.com/gitui-org/gitui/pull/2542))
* set the terminal title to `gitui ({repo_path})` [[@acuteenvy](https://github.com/acuteenvy)] ([#2462](https://github.com/gitui-org/gitui/issues/2462))
* respect `.mailmap` [[@acuteenvy](https://github.com/acuteenvy)] ([#2406](https://github.com/gitui-org/gitui/issues/2406))
* use `gitoxide` for `get_tags` [[@cruessler](https://github.com/cruessler)] ([#2664](https://github.com/gitui-org/gitui/issues/2664))
* increase MSRV to 1.82
### Fixes
* resolve `core.hooksPath` relative to `GIT_WORK_TREE` [[@naseschwarz](https://github.com/naseschwarz)] ([#2571](https://github.com/gitui-org/gitui/issues/2571))
* yanking commit ranges no longer generates incorrect dotted range notations, but lists each individual commit [[@naseschwarz](https://github.com/naseschwarz)] ([#2576](https://github.com/gitui-org/gitui/issues/2576))
* print slightly nicer errors when failing to create a directory [[@linkmauve](https://github.com/linkmauve)] ([#2728](https://github.com/gitui-org/gitui/pull/2728))
* when the terminal is insufficient to display all the commands, the cmdbar_bg configuration color does not fully take effect. ([#2347](https://github.com/extrawurst/gitui/issues/2347))
* disable blame and history popup keybinds for untracked files [[@kpbaks](https://github.com/kpbaks)] ([#2489](https://github.com/gitui-org/gitui/pull/2489))
* overwrites committer on amend of unsigned commits [[@cruessler](https://github.com/cruessler)] ([#2784](https://github.com/gitui-org/gitui/issues/2784))
* Updated project links to point to `gitui-org` instead of `extrawurst` [[@vasleymus](https://github.com/vasleymus)] ([#2538](https://github.com/gitui-org/gitui/pull/2538))
## [0.27.0] - 2025-01-14
**new: manage remotes**

### Breaking Changes
* use default shell instead of bash on Unix-like OS [[@yerke](https://github.com/yerke)] ([#2343](https://github.com/gitui-org/gitui/pull/2343))
### Added
* add popups for viewing, adding, updating and removing remotes [[@robin-thoene](https://github.com/robin-thoene)] ([#2172](https://github.com/gitui-org/gitui/issues/2172))
* support for `Copy Path` action in WSL [[@johnDeSilencio](https://github.com/johnDeSilencio)] ([#2413](https://github.com/gitui-org/gitui/pull/2413))
* help popup scrollbar [[@wugeer](https://github.com/wugeer)] ([#2388](https://github.com/gitui-org/gitui/pull/2388))
### Fixes
* respect env vars like `GIT_CONFIG_GLOBAL` ([#2298](https://github.com/gitui-org/gitui/issues/2298))
* Set `CREATE_NO_WINDOW` flag when executing Git hooks on Windows ([#2371](https://github.com/gitui-org/gitui/pull/2371))
## [0.26.3] - 2024-06-02
### Breaking Changes
#### Theme file format
**note:** this actually applied to the previous release already: `0.26.2`
Ratatui (upstream terminal rendering crate) changed its serialization format for Colors. So the theme files have to be adjusted.
`selection_fg: Some(White)` -> `selection_fg: Some("White")`
but this also allows us now to define colors in the common hex format:
`selection_fg: Some(Rgb(0,255,0))` -> `selection_fg: Some("#00ff00")`
Checkout [THEMES.md](./THEMES.md) for more info.
### Added
* due to github runner changes, the regular mac build is now arm64, so we added support for intel x86 apple build in nightlies and releases (via separate artifact)
* support `BUILD_GIT_COMMIT_ID` enabling builds from `git archive` generated source tarballs or other outside a git repo [[@alerque](https://github.com/alerque)] ([#2187](https://github.com/gitui-org/gitui/pull/2187))
### Fixes
* update yanked dependency to `libc` to fix building with `--locked`.
* document breaking change in theme file format.
## [0.26.2] - 2024-04-17
**note:** this release introduced a breaking change documented in the following release: `0.26.3`
### Fixes
* fix `cargo install` without `--locked` ([#2098](https://github.com/gitui-org/gitui/issues/2098))
* respect configuration for remote when fetching (also applies to pulling) [[@cruessler](https://github.com/cruessler)] ([#1093](https://github.com/gitui-org/gitui/issues/1093))
* add `:` character to sign-off trailer to comply with Conventinoal Commits standard [@semioticrobotic](https://github.com/semioticrobotic) ([#2196](https://github.com/gitui-org/gitui/issues/2196))
### Added
* support overriding `build_date` for [reproducible builds](https://reproducible-builds.org/) [[@bmwiedemann](https://github.com/bmwiedemann)] ([#2202](https://github.com/gitui-org/gitui/pull/2202))
## [0.26.0+1] - 2024-04-14
**0.26.1**
this release has no changes to `0.26.0` but provides windows binaries that were missing before.
**commit signing**

### Added
* sign commits using openpgp [[@hendrikmaus](https://github.com/hendrikmaus)] ([#97](https://github.com/gitui-org/gitui/issues/97))
* support ssh commit signing (when `user.signingKey` and `gpg.format = ssh` of gitconfig are set; ssh-agent isn't yet supported) [[@yanganto](https://github.com/yanganto)] ([#1149](https://github.com/gitui-org/gitui/issues/1149))
* provide nightly builds (see [NIGHTLIES.md](./NIGHTLIES.md)) ([#2083](https://github.com/gitui-org/gitui/issues/2083))
* more version info in `gitui -V` and `help popup` (including git hash)
* support `core.commitChar` filtering [[@concelare](https://github.com/concelare)] ([#2136](https://github.com/gitui-org/gitui/issues/2136))
* allow reset in branch popup ([#2170](https://github.com/gitui-org/gitui/issues/2170))
* respect configuration for remote when pushing [[@cruessler](https://github.com/cruessler)] ([#2156](https://github.com/gitui-org/gitui/issues/2156))
### Changed
* Make info and error message popups scrollable [[@MichaelAug](https://github.com/MichaelAug)] ([#1138](https://github.com/gitui-org/gitui/issues/1138))
* clarify `x86_64` linux binary in artifact names: `gitui-linux-x86_64.tar.gz` (formerly known as `musl`) ([#2148](https://github.com/gitui-org/gitui/issues/2148))
### Fixes
* add syntax highlighting support for more file types, e.g. Typescript, TOML, etc. [[@martihomssoler](https://github.com/martihomssoler)] ([#2005](https://github.com/gitui-org/gitui/issues/2005))
* windows release deployment was broken (reason for release `0.26.1`) [218d739](https://github.com/gitui-org/gitui/commit/218d739b035a034b7bf547629d24787909f467bf)
## [0.25.2] - 2024-03-22
### Fixes
* blame sometimes crashed due to new syntax highlighting [[@tdtrung17693](https://github.com/tdtrung17693)] ([#2130](https://github.com/gitui-org/gitui/issues/2130))
* going to file tree view at certin commit from the commit-details view broke [[@martihomssoler](https://github.com/martihomssoler)] ([#2114](https://github.com/gitui-org/gitui/issues/2114))
* `0.25` broke creating annotated tags ([#2126](https://github.com/gitui-org/gitui/issues/2126))
### Changed
* re-enable clippy `missing_const_for_fn` linter warning and added const to functions where applicable ([#2116](https://github.com/gitui-org/gitui/issues/2116))
## [0.25.1] - 2024-02-23
### Fixes
* bump yanked dependency `bumpalo` to fix build from source ([#2087](https://github.com/gitui-org/gitui/issues/2087))
* pin `ratatui` version to fix building without locked `cargo install gitui` ([#2090](https://github.com/gitui-org/gitui/issues/2090))
## [0.25.0] - 2024-02-21
** multiline text editor **

** syntax highlighting in blame **

### Breaking Change
#### commit key binding
The Commit message popup now supports multiline editing! Inserting a **newline** defaults to `enter`. This comes with a new default to confirm the commit message (`ctrl+d`).
Both commands can be overwritten via `newline` and `commit` in the key bindings. see [KEY_CONFIG](./KEY_CONFIG.md) on how.
These defaults require some adoption from existing users but feel more natural to new users.
#### key binding bitflags
Modifiers like `SHIFT` or `CONTROL` are no longer configured via magic bitflags but via strings thanks to changes in the [bitflags crate](https://github.com/bitflags/bitflags/blob/main/CHANGELOG.md#changes-to-serde-serialization) we depend on. Please see [KEY_CONFIG.md](./KEY_CONFIG.md) or [vim_style_key_config.ron](./vim_style_key_config.ron) for more info and examples.
### Added
* support for new-line in text-input (e.g. commit message editor) [[@pm100]](https://github/pm100) ([#1662](https://github.com/gitui-org/gitui/issues/1662)).
* add syntax highlighting for blame view [[@tdtrung17693](https://github.com/tdtrung17693)] ([#745](https://github.com/gitui-org/gitui/issues/745))
* allow aborting pending commit log search [[@StemCll](https://github.com/StemCll)] ([#1860](https://github.com/gitui-org/gitui/issues/1860))
* `theme.ron` now supports customizing line break symbol ([#1894](https://github.com/gitui-org/gitui/issues/1894))
* add confirmation for dialog for undo commit [[@TeFiLeDo](https://github.com/TeFiLeDo)] ([#1912](https://github.com/gitui-org/gitui/issues/1912))
* support `prepare-commit-msg` hook ([#1873](https://github.com/gitui-org/gitui/issues/1873))
* new style `block_title_focused` to allow customizing title text of focused frame/block ([#2052](https://github.com/gitui-org/gitui/issues/2052)).
* allow `fetch` command in both tabs of branchlist popup ([#2067](https://github.com/gitui-org/gitui/issues/2067))
* check branch name validity while typing [[@sainad2222](https://github.com/sainad2222)] ([#2062](https://github.com/gitui-org/gitui/issues/2062))
### Changed
* do not allow tagging when `tag.gpgsign` enabled until gpg-signing is [supported](https://github.com/gitui-org/gitui/issues/97) [[@TeFiLeDo](https://github.com/TeFiLeDo)] ([#1915](https://github.com/gitui-org/gitui/pull/1915))
### Fixes
* stash window empty after file history popup closes ([#1986](https://github.com/gitui-org/gitui/issues/1986))
* allow push to empty remote ([#1919](https://github.com/gitui-org/gitui/issues/1919))
* better diagnostics for theme file loading ([#2007](https://github.com/gitui-org/gitui/issues/2007))
* fix ordering of commits in diff view [[@Joshix-1](https://github.com/Joshix-1)]([#1747](https://github.com/gitui-org/gitui/issues/1747))
## [0.24.3] - 2023-09-09
### Fixes
* log: major lag when going beyond last search hit ([#1876](https://github.com/gitui-org/gitui/issues/1876))
### Changed
* parallelise log search - performance gain ~100% ([#1869](https://github.com/gitui-org/gitui/issues/1869))
* search message body/summary separately ([#1875](https://github.com/gitui-org/gitui/issues/1875))
## [0.24.2] - 2023-09-03
### Fixes
* fix commit log not updating after branch switch ([#1862](https://github.com/gitui-org/gitui/issues/1862))
* fix stashlist not updating after pop/drop ([#1864](https://github.com/gitui-org/gitui/issues/1864))
* fix commit log corruption when tabbing in/out while parsing log ([#1866](https://github.com/gitui-org/gitui/issues/1866))
## [0.24.1] - 2023-08-30
### Fixes
* fix performance problem in big repo with a lot of incoming commits ([#1845](https://github.com/gitui-org/gitui/issues/1845))
* fix error switching to a branch with '/' in the name ([#1851](https://github.com/gitui-org/gitui/issues/1851))
## [0.24.0] - 2023-08-27
**search commits**

**visualize empty lines in diff better**

### Breaking Changes
* Do you use a custom theme?
The way themes work got changed and simplified ([see docs](https://github.com/gitui-org/gitui/blob/master/THEMES.md) for more info):
* The format of `theme.ron` has changed: you only specify the colors etc. that should differ from their default value
* Future additions of colors etc. will not break existing themes anymore
### Added
* search commits by message, author or files in diff ([#1791](https://github.com/gitui-org/gitui/issues/1791))
* support 'n'/'p' key to move to the next/prev hunk in diff component [[@hamflx](https://github.com/hamflx)] ([#1523](https://github.com/gitui-org/gitui/issues/1523))
* simplify theme overrides [[@cruessler](https://github.com/cruessler)] ([#1367](https://github.com/gitui-org/gitui/issues/1367))
* support for sign-off of commits [[@domtac](https://github.com/domtac)]([#1757](https://github.com/gitui-org/gitui/issues/1757))
* switched from textwrap to bwrap for text wrapping [[@TheBlackSheep3](https://github.com/TheBlackSheep3/)] ([#1762](https://github.com/gitui-org/gitui/issues/1762))
* more logging diagnostics when a repo cannot be opened
* added to [anaconda](https://anaconda.org/conda-forge/gitui) [[@TheBlackSheep3](https://github.com/TheBlackSheep3/)] ([#1626](https://github.com/gitui-org/gitui/issues/1626))
* visualize empty line substituted with content in diff better ([#1359](https://github.com/gitui-org/gitui/issues/1359))
* checkout branch works with non-empty status report [[@lightsnowball](https://github.com/lightsnowball)] ([#1399](https://github.com/gitui-org/gitui/issues/1399))
* jump to commit by SHA [[@AmmarAbouZor](https://github.com/AmmarAbouZor)] ([#1818](https://github.com/gitui-org/gitui/pull/1818))
### Fixes
* fix commit dialog char count for multibyte characters ([#1726](https://github.com/gitui-org/gitui/issues/1726))
* fix wrong hit highlighting in fuzzy find popup [[@UUGTech](https://github.com/UUGTech)] ([#1731](https://github.com/gitui-org/gitui/pull/1731))
* fix symlink support for configuration files [[@TheBlackSheep3](https://github.com/TheBlackSheep3)] ([#1751](https://github.com/gitui-org/gitui/issues/1751))
* fix expansion of `~` in `commit.template` ([#1745](https://github.com/gitui-org/gitui/pull/1745))
* fix hunk (un)staging/reset for # of context lines != 3 ([#1746](https://github.com/gitui-org/gitui/issues/1746))
* fix delay when opening external editor ([#1506](https://github.com/gitui-org/gitui/issues/1506))
### Changed
* Copy full Commit Hash by default [[@AmmarAbouZor](https://github.com/AmmarAbouZor)] ([#1836](https://github.com/gitui-org/gitui/issues/1836))
## [0.23.0] - 2023-06-19
**reset to commit**

**reword commit**

**fuzzy find branch**

### Breaking Change
* `focus_XYZ` key bindings are merged into the `move_XYZ` set, so only one way to bind arrow-like keys from now on ([#1539](https://github.com/gitui-org/gitui/issues/1539))
### Added
* allow reset (soft,mixed,hard) from commit log ([#1500](https://github.com/gitui-org/gitui/issues/1500))
* support **reword** of commit from log ([#829](https://github.com/gitui-org/gitui/pull/829))
* fuzzy find branch [[@UUGTech](https://github.com/UUGTech)] ([#1350](https://github.com/gitui-org/gitui/issues/1350))
* list changes in commit message inside external editor [[@bc-universe]](https://github.com/bc-universe) ([#1420](https://github.com/gitui-org/gitui/issues/1420))
* allow detaching HEAD and checking out specific commit from log view [[@fralcow]](https://github.com/fralcow) ([#1499](https://github.com/gitui-org/gitui/pull/1499))
* add no-verify option on commits to not run hooks [[@dam5h]](https://github.com/dam5h) ([#1374](https://github.com/gitui-org/gitui/issues/1374))
* allow `fetch` on status tab [[@alensiljak]](https://github.com/alensiljak) ([#1471](https://github.com/gitui-org/gitui/issues/1471))
* allow `copy` file path on revision files and status tree [[@yanganto]](https://github.com/yanganto) ([#1516](https://github.com/gitui-org/gitui/pull/1516))
* print message of where log will be written if `-l` is set ([#1472](https://github.com/gitui-org/gitui/pull/1472))
* show remote branches in log [[@cruessler](https://github.com/cruessler)] ([#1501](https://github.com/gitui-org/gitui/issues/1501))
* scrolling functionality to fuzzy-find [[@AmmarAbouZor](https://github.com/AmmarAbouZor)] ([#1732](https://github.com/gitui-org/gitui/issues/1732))
### Fixes
* fixed side effect of crossterm 0.26 on windows that caused double input of all keys [[@pm100]](https://github/pm100) ([#1686](https://github.com/gitui-org/gitui/pull/1686))
* commit msg history ordered the wrong way ([#1445](https://github.com/gitui-org/gitui/issues/1445))
* improve help documentation for amend cmd ([#1448](https://github.com/gitui-org/gitui/issues/1448))
* lag issue when showing files tab ([#1451](https://github.com/gitui-org/gitui/issues/1451))
* fix key binding shown in bottom bar for `stash_open` ([#1454](https://github.com/gitui-org/gitui/issues/1454))
* `--bugreport` does not require param ([#1466](https://github.com/gitui-org/gitui/issues/1466))
* `edit`-file command shown on commits msg ([#1461](https://github.com/gitui-org/gitui/issues/1461))
* crash on branches popup in small terminal ([#1470](https://github.com/gitui-org/gitui/issues/1470))
* `edit` command duplication ([#1489](https://github.com/gitui-org/gitui/issues/1489))
* syntax errors in `key_bindings.ron` will be logged ([#1491](https://github.com/gitui-org/gitui/issues/1491))
* Fix UI freeze when copying with xclip installed on Linux ([#1497](https://github.com/gitui-org/gitui/issues/1497))
* Fix UI freeze when copying with wl-copy installed on Linux ([#1497](https://github.com/gitui-org/gitui/issues/1497))
* commit hooks report "command not found" on Windows with wsl2 installed ([#1528](https://github.com/gitui-org/gitui/issues/1528))
* crashes on entering submodules ([#1510](https://github.com/gitui-org/gitui/issues/1510))
* fix race issue: revlog messages sometimes appear empty ([#1473](https://github.com/gitui-org/gitui/issues/1473))
* default to tick-based updates [[@cruessler](https://github.com/cruessler)] ([#1444](https://github.com/gitui-org/gitui/issues/1444))
* add support for options handling in log and stashes views [[@kamillo](https://github.com/kamillo)] ([#1661](https://github.com/gitui-org/gitui/issues/1661))
### Changed
* minimum supported rust version bumped to 1.65 (thank you `time` crate)
## [0.22.1] - 2022-11-22
Bugfix followup release - check `0.22.0` notes for more infos!
### Added
* new arg `--polling` to use poll-based change detection and not filesystem watcher (use if you see problems running into file descriptor limits)
### Fixes
* improve performance by requesting branches info asynchronous ([92f63d1](https://github.com/gitui-org/gitui/commit/92f63d107c1dca1f10139668ff5b3ca752261b0f))
* fix app startup delay due to using file watcher ([#1436](https://github.com/gitui-org/gitui/issues/1436))
* make git tree file fetch async ([#734](https://github.com/gitui-org/gitui/issues/734))
## [0.22.0] - 2022-11-19
**submodules view**

**commit message history**

### Added
* submodules support ([#1087](https://github.com/gitui-org/gitui/issues/1087))
* remember tab between app starts ([#1338](https://github.com/gitui-org/gitui/issues/1338))
* repo specific gitui options saved in `.git/gitui.ron` ([#1340](https://github.com/gitui-org/gitui/issues/1340))
* commit msg history ([#1345](https://github.com/gitui-org/gitui/issues/1345))
* customizable `cmdbar_bg` theme color & screen spanning selected line bg [[@gigitsu](https://github.com/gigitsu)] ([#1299](https://github.com/gitui-org/gitui/pull/1299))
* word motions to text input [[@Rodrigodd](https://github.com/Rodrigodd)] ([#1256](https://github.com/gitui-org/gitui/issues/1256))
* file blame at right revision from commit-details [[@heiskane](https://github.com/heiskane)] ([#1122](https://github.com/gitui-org/gitui/issues/1122))
* dedicated selection foreground theme color `selection_fg` ([#1365](https://github.com/gitui-org/gitui/issues/1365))
* add `regex-fancy` and `regex-onig` features to allow building Syntect with Onigumara regex engine instead of the default engine based on fancy-regex [[@jirutka](https://github.com/jirutka)]
* add `vendor-openssl` feature to allow building without vendored openssl [[@jirutka](https://github.com/jirutka)]
* allow copying marked commits [[@remique](https://github.com/remique)] ([#1288](https://github.com/gitui-org/gitui/issues/1288))
* feedback for success/failure of copying hash commit [[@sergioribera](https://github.com/sergioribera)]([#1160](https://github.com/gitui-org/gitui/issues/1160))
* display tags and branches in the log view [[@alexmaco](https://github.com/alexmaco)] ([#1371](https://github.com/gitui-org/gitui/pull/1371))
* display current repository path in the top-right corner [[@alexmaco](https://github.com/alexmaco)]([#1387](https://github.com/gitui-org/gitui/pull/1387))
* add Linux targets for ARM, ARMv7 and AARCH64 [[@adur1990](https://github.com/adur1990)] ([#1419](https://github.com/gitui-org/gitui/pull/1419))
* display commit description in file view [[@alexmaco](https://github.com/alexmaco)] ([#1380](https://github.com/gitui-org/gitui/pull/1380))
* allow launching editor from Compare Commits view ([#1409](https://github.com/gitui-org/gitui/pull/1409))
### Fixes
* remove insecure dependency `ansi_term` ([#1290](https://github.com/gitui-org/gitui/issues/1290))
* use filewatcher instead of polling updates ([#1](https://github.com/gitui-org/gitui/issues/1))
## [0.21.0] - 2022-08-17
**popup stacking**

**termux android support**

### Added
* stack popups ([#846](https://github.com/gitui-org/gitui/issues/846))
* file history log [[@cruessler](https://github.com/cruessler)] ([#381](https://github.com/gitui-org/gitui/issues/381))
* termux support on android [[@PeroSar](https://github.com/PeroSar)] ([#1139](https://github.com/gitui-org/gitui/issues/1139))
* use `GIT_DIR` and `GIT_WORK_DIR` from environment if set ([#1191](https://github.com/gitui-org/gitui/pull/1191))
* new [FAQ](./FAQ.md)s page
* mention macports in install section [[@fs111](https://github.com/fs111)]([#1237](https://github.com/gitui-org/gitui/pull/1237))
* support copy to clipboard on wayland [[@JayceFayne](https://github.com/JayceFayne)] ([#397](https://github.com/gitui-org/gitui/issues/397))
### Fixed
* opening tags list without remotes ([#1111](https://github.com/gitui-org/gitui/issues/1111))
* tabs indentation in blame [[@fersilva16](https://github.com/fersilva16)] ([#1117](https://github.com/gitui-org/gitui/issues/1117))
* switch focus to index after staging last file ([#1169](https://github.com/gitui-org/gitui/pull/1169))
* fix stashlist multi marking not updated after dropping ([#1207](https://github.com/gitui-org/gitui/pull/1207))
* exact matches have a higher priority and are placed to the top of the list when fuzzily finding files ([#1183](https://github.com/gitui-org/gitui/pull/1183))
* support horizontal scrolling in diff view ([#1017](https://github.com/gitui-org/gitui/issues/1017))
### Changed
* minimum supported rust version bumped to 1.60 ([#1279](https://github.com/gitui-org/gitui/pull/1279))
## [0.20.1] - 2022-01-26
This is was a immediate followup patch release to `0.20` see [release notes](https://github.com/gitui-org/gitui/releases/tag/v0.20.0) for the whole list of goodies in `0.20`.
### Added
* support proxy auto detection via env's like `HTTP_PROXY` ([#994](https://github.com/gitui-org/gitui/issues/994))
### Fixed
* severe performance regression in `0.20` ([#1102](https://github.com/gitui-org/gitui/issues/1102))
* several smaller performance improvements via caching ([#1104](https://github.com/gitui-org/gitui/issues/1104))
* windows release deployment via CD broken
## [0.20] - 2022-01-25 - Tag Annotations
**support tag annotations**

**delete tag on remote**

**revert commit from rev log**

### Added
- support `core.hooksPath` ([#1044](https://github.com/gitui-org/gitui/issues/1044))
- allow reverting a commit from the commit log ([#927](https://github.com/gitui-org/gitui/issues/927))
- disable pull cmd on local-only branches ([#1047](https://github.com/gitui-org/gitui/issues/1047))
- support adding annotations to tags ([#747](https://github.com/gitui-org/gitui/issues/747))
- support inspecting annotation of tag ([#1076](https://github.com/gitui-org/gitui/issues/1076))
- support deleting tag on remote ([#1074](https://github.com/gitui-org/gitui/issues/1074))
- support git credentials helper (https) ([#800](https://github.com/gitui-org/gitui/issues/800))
### Fixed
- Keep commit message when pre-commit hook fails ([#1035](https://github.com/gitui-org/gitui/issues/1035))
- honor `pushurl` when checking credentials for pushing ([#953](https://github.com/gitui-org/gitui/issues/953))
- use git-path instead of workdir finding hooks ([#1046](https://github.com/gitui-org/gitui/issues/1046))
- only enable remote actions (fetch/pull/push) if there are remote branches ([#1047](https://github.com/gitui-org/gitui/issues/1047))
### Key binding notes
- added `gg`/`G` vim bindings to `vim_style_key_config.ron` ([#1039](https://github.com/gitui-org/gitui/issues/1039))
## [0.19] - 2021-12-08 - Bare Repo Support
**finder highlighting matches**

### Breaking Change
Have you used `key_config.ron` for custom key bindings before?
The way this works got changed and simplified ([See docs](https://github.com/gitui-org/gitui/blob/master/KEY_CONFIG.md) for more info):
* You only define the keys that should differ from the default.
* The file is renamed to `key_bindings.ron`
* Future addition of new keys will not break anymore
### Added
- add fetch/update command all remote branches ([#998](https://github.com/gitui-org/gitui/issues/998))
- add `trace-libgit` feature to make git tracing optional [[@dm9pZCAq](https://github.com/dm9pZCAq)] ([#902](https://github.com/gitui-org/gitui/issues/902))
- support merging and rebasing remote branches [[@R0nd](https://github.com/R0nd)] ([#920](https://github.com/gitui-org/gitui/issues/920))
- add highlighting matches in fuzzy finder [[@Mifom](https://github.com/Mifom)] ([#893](https://github.com/gitui-org/gitui/issues/893))
- support `home` and `end` keys in branchlist ([#957](https://github.com/gitui-org/gitui/issues/957))
- add `ghemoji` feature to make gh-emoji (GitHub emoji) optional [[@jirutka](https://github.com/jirutka)] ([#954](https://github.com/gitui-org/gitui/pull/954))
- allow customizing key symbols like `⏎` & `⇧` ([see docs](https://github.com/gitui-org/gitui/blob/master/KEY_CONFIG.md#key-symbols)) ([#465](https://github.com/gitui-org/gitui/issues/465))
- simplify key overrides ([see docs](https://github.com/gitui-org/gitui/blob/master/KEY_CONFIG.md)) ([#946](https://github.com/gitui-org/gitui/issues/946))
- dedicated fuzzy finder up/down keys to allow vim overrides ([#993](https://github.com/gitui-org/gitui/pull/993))
- pull will also download tags ([#1013](https://github.com/gitui-org/gitui/pull/1013))
- allow editing file from filetree ([#989](https://github.com/gitui-org/gitui/pull/989))
- support bare repos (new `workdir` argument) ([#1026](https://github.com/gitui-org/gitui/pull/1026))
### Fixed
- honor options (for untracked files) in `stage_all` command ([#933](https://github.com/gitui-org/gitui/issues/933))
- improved file diff speed dramatically ([#976](https://github.com/gitui-org/gitui/issues/976))
- blaming files in sub-folders on windows ([#981](https://github.com/gitui-org/gitui/issues/981))
- push failing due to tracing error in upstream ([#881](https://github.com/gitui-org/gitui/issues/881))
## [0.18] - 2021-10-11
**rebase merge with conflicts**

### Added
- support rebasing branches with conflicts ([#895](https://github.com/gitui-org/gitui/issues/895))
- add a key binding to stage / unstage items [[@alessandroasm](https://github.com/alessandroasm)] ([#909](https://github.com/gitui-org/gitui/issues/909))
- switch to status tab after merging or rebasing with conflicts ([#926](https://github.com/gitui-org/gitui/issues/926))
### Fixed
- fix supported checkout of hierarchical branchnames ([#921](https://github.com/gitui-org/gitui/issues/921))
- appropriate error message when pulling deleted remote branch ([#911](https://github.com/gitui-org/gitui/issues/911))
- improved color contrast in branches popup for light themes [[@Cottser](https://github.com/Cottser)] ([#922](https://github.com/gitui-org/gitui/issues/922))
- use git_message_prettify for commit messages ([#917](https://github.com/gitui-org/gitui/issues/917))
## [0.17.1] - 2021-09-10
**fuzzy find files**

**emojified commit message**

### Added
- add supporting rebasing on branch (if conflict-free) ([#816](https://github.com/gitui-org/gitui/issues/816))
- fuzzy find files ([#891](https://github.com/gitui-org/gitui/issues/891))
- visualize progress during async syntax highlighting ([#889](https://github.com/gitui-org/gitui/issues/889))
- added support for markdown emoji's in commits [[@andrewpollack](https://github.com/andrewpollack)] ([#768](https://github.com/gitui-org/gitui/issues/768))
- added scrollbar to revlog [[@ashvin021](https://github.com/ashvin021)] ([#868](https://github.com/gitui-org/gitui/issues/868))
### Fixed
- fix build when system level libgit2 version was used ([#883](https://github.com/gitui-org/gitui/issues/883))
- fix merging branch not closing branch window [[@andrewpollack](https://github.com/andrewpollack)] ([#876](https://github.com/gitui-org/gitui/issues/876))
- fix commit msg being broken inside tag list ([#871](https://github.com/gitui-org/gitui/issues/871))
- fix filetree file content not showing tabs correctly ([#874](https://github.com/gitui-org/gitui/issues/874))
### Key binding notes
- new keys: `rebase_branch` [`R`], `file_find` [`f`]
see `vim_style_key_config.ron` for their default vim binding
## [0.17.0] - 2021-08-21
**compare commits**

**options**

**drop multiple stashes**

**branch name validation**

### Added
- allow inspecting top commit of a branch from list
- compare commits in revlog and head against branch ([#852](https://github.com/gitui-org/gitui/issues/852))
- new options popup (show untracked files, diff settings) ([#849](https://github.com/gitui-org/gitui/issues/849))
- mark and drop multiple stashes ([#854](https://github.com/gitui-org/gitui/issues/854))
- check branch name validity while typing ([#559](https://github.com/gitui-org/gitui/issues/559))
- support deleting remote branch [[@zcorniere](https://github.com/zcorniere)] ([#622](https://github.com/gitui-org/gitui/issues/622))
- mark remote branches that have local tracking branch [[@jedel1043](https://github.com/jedel1043)] ([#861](https://github.com/gitui-org/gitui/issues/861))
### Fixed
- error viewing filetree in empty repo ([#859](https://github.com/gitui-org/gitui/issues/859))
- do not allow to ignore .gitignore files ([#825](https://github.com/gitui-org/gitui/issues/825))
- crash in shallow repo ([#836](https://github.com/gitui-org/gitui/issues/836))
- fixed performance regression in revlog ([#850](https://github.com/gitui-org/gitui/issues/850))
- fixed performance degradation when quitting on Windows ([#823](https://github.com/gitui-org/gitui/issues/823))
## [0.16.2] - 2021-07-10
**undo last commit**

**mark local tags**

### Added
- taglist: show arrow-symbol on tags not present on origin [[@cruessler](https://github.com/cruessler)] ([#776](https://github.com/gitui-org/gitui/issues/776))
- new `undo-last-commit` command [[@remique](https://github.com/remique)] ([#758](https://github.com/gitui-org/gitui/issues/758))
- new quit key `[q]` ([#771](https://github.com/gitui-org/gitui/issues/771))
- proper error message if remote rejects force push ([#801](https://github.com/gitui-org/gitui/issues/801))
### Fixed
- openssl vendoring broken on macos ([#772](https://github.com/gitui-org/gitui/issues/772))
- amend and other commands not shown in help ([#778](https://github.com/gitui-org/gitui/issues/778))
- focus locked on commit msg details in narrow term sizes ([#780](https://github.com/gitui-org/gitui/issues/780))
- non-utf8 file/path names broke filetree ([#802](https://github.com/gitui-org/gitui/issues/802))
## [0.16.1] - 2021-06-06
### Added
- honor `config.showUntrackedFiles` improving speed with a lot of untracked items ([#752](https://github.com/gitui-org/gitui/issues/752))
- improve performance when opening filetree-tab ([#756](https://github.com/gitui-org/gitui/issues/756))
- indicator for longer commit message than displayed ([#773](https://github.com/gitui-org/gitui/issues/773))

### Fixed
- wrong file with same name shown in file tree ([#748](https://github.com/gitui-org/gitui/issues/748))
- filetree collapsing broken on windows ([#761](https://github.com/gitui-org/gitui/issues/761))
- unnecessary overdraw of the spinner on each redraw ([#764](https://github.com/gitui-org/gitui/issues/764))
### Internal
- use git_repository_message [[@kosayoda](https://github.com/kosayoda)] ([#751](https://github.com/gitui-org/gitui/issues/751))
## [0.16.0] - 2021-05-28
**merge branch, merge commit**

**tag list popup**

**revision file tree**

**commit subject length warning**

### Added
- merging branches, pull-merge with conflicts, commit merges ([#485](https://github.com/gitui-org/gitui/issues/485))
- tags-list-popup (delete-tag, go to tagged commit) [[@cruessler](https://github.com/cruessler)] ([#483](https://github.com/gitui-org/gitui/issues/483))
- inspect file tree tab ([#743](https://github.com/gitui-org/gitui/issues/743))
- file tree popup (for a specific revision) ([#714](https://github.com/gitui-org/gitui/issues/714))
- warning if commit subject line gets too long ([#478](https://github.com/gitui-org/gitui/issues/478))
- `--bugreport` cmd line arg to help diagnostics [[@zcorniere](https://github.com/zcorniere)] ([#695](https://github.com/gitui-org/gitui/issues/695))
### Changed
- smarter log timestamps ([#682](https://github.com/gitui-org/gitui/issues/682))
- create-branch popup aligned with rename-branch [[@bruceCoelho](https://github.com/bruceCoelho)] ([#679](https://github.com/gitui-org/gitui/issues/679))
- smart focus change after staging all files ([#706](https://github.com/gitui-org/gitui/issues/706))
- do not allow to commit when `gpgsign` enabled ([#740](https://github.com/gitui-org/gitui/issues/740))
### Fixed
- selected-tab color broken in light theme [[@Cottser](https://github.com/Cottser)] ([#719](https://github.com/gitui-org/gitui/issues/719))
- proper tmp file location to externally edit commit msg ([#518](https://github.com/gitui-org/gitui/issues/518))
## [0.15.0] - 2021-04-27
**file blame**

### Added
- blame a file [[@cruessler](https://github.com/cruessler)] ([#484](https://github.com/gitui-org/gitui/issues/484))
- support commit.template [[@wandernauta](https://github.com/wandernauta)] ([#546](https://github.com/gitui-org/gitui/issues/546))
### Fixed
- debug print when adding a file to ignore
- fix scrolling long messages in commit details view ([#663](https://github.com/gitui-org/gitui/issues/663))
- limit log messages in log tab ([#652](https://github.com/gitui-org/gitui/issues/652))
- fetch crashed when no upstream of branch is set ([#637](https://github.com/gitui-org/gitui/issues/637))
- `enter` key panics in empty remote branch list ([#643](https://github.com/gitui-org/gitui/issues/643))
### Internal
- cleanup some stringly typed code [[@wandernauta](https://github.com/wandernauta)] ([#655](https://github.com/gitui-org/gitui/issues/655))
- introduce EventState enum (removing bool for even propagation) [[@tisorlawan](https://github.com/tisorlawan)] ([#665](https://github.com/gitui-org/gitui/issues/665))
## [0.14.0] - 2021-04-11
### Added
- `[w]` key to toggle between staging/workdir [[@terhechte](https://github.com/terhechte)] ([#595](https://github.com/gitui-org/gitui/issues/595))
- view/checkout remote branches ([#617](https://github.com/gitui-org/gitui/issues/617))

### Changed
- ask to pop stash by default (*apply* using `[a]` now) [[@brunogouveia](https://github.com/brunogouveia)] ([#574](https://github.com/gitui-org/gitui/issues/574))

### Fixed
- push branch to its tracking remote ([#597](https://github.com/gitui-org/gitui/issues/597))
- fixed panic when staging lines involving missing newline eof ([#605](https://github.com/gitui-org/gitui/issues/605))
- fixed pull/fetch deadlocking when it fails ([#624](https://github.com/gitui-org/gitui/issues/624))
## [0.13.0] - 2021-03-15 - Happy Birthday GitUI 🥳
Thanks for your interest and support over this year! Read more about the 1 year anniversary reflections of this project on my [blog](https://blog.extrawurst.org/general/programming/rust/2021/03/15/gitui-a-year-in-opensource.html).
**stage/unstage/discard by line**

**push tags**

### Changed
- `[s]` key repurposed to trigger line based (un)stage
- cleanup status/diff commands to be more context sensitive ([#572](https://github.com/gitui-org/gitui/issues/572))
### Added
- support pull via rebase (using config `pull.rebase`) ([#566](https://github.com/gitui-org/gitui/issues/566))
- support stage/unstage selected lines ([#59](https://github.com/gitui-org/gitui/issues/59))
- support discarding selected lines ([#59](https://github.com/gitui-org/gitui/issues/59))
- support for pushing tags ([#568](https://github.com/gitui-org/gitui/issues/568))
- visualize *conflicted* files differently ([#576](https://github.com/gitui-org/gitui/issues/576))
### Fixed
- keep diff line selection after staging/unstaging/discarding ([#583](https://github.com/gitui-org/gitui/issues/583))
- fix pull deadlocking when aborting credentials input ([#586](https://github.com/gitui-org/gitui/issues/586))
- error diagnostics for config loading ([#589](https://github.com/gitui-org/gitui/issues/589))
## [0.12.0] - 2021-03-03
**pull support (ff-merge or conflict-free merge-commit)**

**more info in commit popup**

### Breaking Change
- MacOS config directory now uses `~/.config/gitui` [[@remique](https://github.com/remique)] ([#317](https://github.com/gitui-org/gitui/issues/317))
### Added
- support for pull (fetch + simple merging) ([#319](https://github.com/gitui-org/gitui/issues/319))
- show used char count in input texts ([#466](https://github.com/gitui-org/gitui/issues/466))
- support smoother left/right toggle/keys for commit details ([#418](https://github.com/gitui-org/gitui/issues/418))
- support *force push* command [[@WizardOhio24](https://github.com/WizardOhio24)] ([#274](https://github.com/gitui-org/gitui/issues/274))
### Fixed
- don't close branchlist every time ([#550](https://github.com/gitui-org/gitui/issues/550))
- fixed key binding for *external exitor* in vim key bindings [[@yanganto](https://github.com/yanganto)] ([#549](https://github.com/gitui-org/gitui/issues/549))
- fix some potential errors when deleting files while they are being diffed ([#490](https://github.com/gitui-org/gitui/issues/490))
- push defaults to 'origin' remote if it exists ([#494](https://github.com/gitui-org/gitui/issues/494))
- support missing pageUp/down support in branchlist ([#519](https://github.com/gitui-org/gitui/issues/519))
- don't hide branch name while in commit dialog ([#529](https://github.com/gitui-org/gitui/issues/529))
- don't discard commit message without confirmation ([#530](https://github.com/gitui-org/gitui/issues/530))
- compilation broken on freebsd ([#461](https://github.com/gitui-org/gitui/issues/461))
- don’t fail if `user.name` is not set [[@cruessler](https://github.com/cruessler)] ([#79](https://github.com/gitui-org/gitui/issues/79)) ([#228](https://github.com/gitui-org/gitui/issues/228))
## [0.11.0] - 2021-12-20
### Added
- push to remote ([#265](https://github.com/gitui-org/gitui/issues/265)) ([#267](https://github.com/gitui-org/gitui/issues/267))

- number of incoming/outgoing commits to upstream ([#362](https://github.com/gitui-org/gitui/issues/362))
- new branch list popup incl. checkout/delete/rename [[@WizardOhio24](https://github.com/WizardOhio24)] ([#303](https://github.com/gitui-org/gitui/issues/303)) ([#323](https://github.com/gitui-org/gitui/issues/323))

- compact treeview [[@WizardOhio24](https://github.com/WizardOhio24)] ([#192](https://github.com/gitui-org/gitui/issues/192))

- scrollbar in long commit messages [[@timaliberdov](https://github.com/timaliberdov)] ([#308](https://github.com/gitui-org/gitui/issues/308))
- added windows scoop recipe ([#164](https://github.com/gitui-org/gitui/issues/164))
- added gitui to [chocolatey](https://chocolatey.org/packages/gitui) on windows by [@nils-a](https://github.com/nils-a)
- added gitui gentoo instructions to readme [[@dm9pZCAq](https://github.com/dm9pZCAq)] ([#430](https://github.com/gitui-org/gitui/pull/430))
- added windows installer (msi) to release [[@pm100](https://github.com/pm100)] ([#360](https://github.com/gitui-org/gitui/issues/360))
- command to copy commit hash [[@yanganto](https://github.com/yanganto)] ([#281](https://github.com/gitui-org/gitui/issues/281))
### Changed
- upgrade `dirs` to `dirs-next` / remove cfg migration code ([#351](https://github.com/gitui-org/gitui/issues/351)) ([#366](https://github.com/gitui-org/gitui/issues/366))
- do not highlight selection in diff view when not focused ([#270](https://github.com/gitui-org/gitui/issues/270))
- copy to clipboard using `xclip`(linux), `pbcopy`(mac) or `clip`(win) [[@cruessler](https://github.com/cruessler)] ([#262](https://github.com/gitui-org/gitui/issues/262))
### Fixed
- crash when changing git repo while gitui is open ([#271](https://github.com/gitui-org/gitui/issues/271))
- remove workaround for color serialization [[@1wilkens](https://github.com/1wilkens)] ([#149](https://github.com/gitui-org/gitui/issues/149))
- crash on small terminal size ([#307](https://github.com/gitui-org/gitui/issues/307))
- fix vim keybindings uppercase handling [[@yanganto](https://github.com/yanganto)] ([#286](https://github.com/gitui-org/gitui/issues/286))
- remove shift tab windows workaround [[@nils-a](https://github.com/nils-a)] ([#112](https://github.com/gitui-org/gitui/issues/112))
- core.editor is ignored [[@pm100](https://github.com/pm100)] ([#414](https://github.com/gitui-org/gitui/issues/414))
## [0.10.1] - 2020-09-01
### Fixed
- static linux binaries broke due to new clipboard feature which is disabled on linux for now ([#259](https://github.com/gitui-org/gitui/issues/259))
## [0.10.0] - 2020-08-29
### Added
- fully **customizable key bindings** (see [KEY_CONFIG.md](KEY_CONFIG.md)) [[@yanganto](https://github.com/yanganto)] ([#109](https://github.com/gitui-org/gitui/issues/109)) ([#57](https://github.com/gitui-org/gitui/issues/57))
- support scrolling in long commit messages [[@cruessler](https://github.com/cruessler)]([#208](https://github.com/gitui-org/gitui/issues/208))

- copy lines from diffs to clipboard [[@cruessler](https://github.com/cruessler)]([#229](https://github.com/gitui-org/gitui/issues/229))

- scrollbar in long diffs ([#204](https://github.com/gitui-org/gitui/issues/204))

- allow creating new branch ([#253](https://github.com/gitui-org/gitui/issues/253))
### Fixed
- selection error in stashlist when deleting last element ([#223](https://github.com/gitui-org/gitui/issues/223))
- git hooks broke ci build on windows [[@dr-BEat](https://github.com/dr-BEat)] ([#235](https://github.com/gitui-org/gitui/issues/235))
## [0.9.1] - 2020-07-30
### Added
- move to (un)staged when the current selection is empty [[@jonstodle](https://github.com/jonstodle)]([#215](https://github.com/gitui-org/gitui/issues/215))
- pending load of a diff/status is visualized ([#160](https://github.com/gitui-org/gitui/issues/160))
- entry on [git-scm.com](https://git-scm.com/downloads/guis) in the list of GUI tools [[@Vidar314](https://github.com/Vidar314)] (see [PR](https://github.com/git/git-scm.com/pull/1485))
- commits can be tagged in revlog [[@cruessler](https://github.com/cruessler)]([#103](https://github.com/gitui-org/gitui/issues/103))

### Changed
- async fetching tags to improve reactivity in giant repos ([#170](https://github.com/gitui-org/gitui/issues/170))
### Fixed
- removed unmaintained dependency `spin` ([#172](https://github.com/gitui-org/gitui/issues/172))
- opening relative paths in external editor may fail in subpaths ([#184](https://github.com/gitui-org/gitui/issues/184))
- crashes in revlog with utf8 commit messages ([#188](https://github.com/gitui-org/gitui/issues/188))
- `add_to_ignore` failed on files without a newline at EOF ([#191](https://github.com/gitui-org/gitui/issues/191))
- new tags were not picked up in revlog view ([#190](https://github.com/gitui-org/gitui/issues/190))
- tags not shown in commit details popup ([#193](https://github.com/gitui-org/gitui/issues/193))
- min size for relative popups on small terminals ([#179](https://github.com/gitui-org/gitui/issues/179))
- fix crash on resizing terminal to very small width ([#198](https://github.com/gitui-org/gitui/issues/198))
- fix broken tags when using a different internal representation ([#206](https://github.com/gitui-org/gitui/issues/206))
- tags are not cleanly separated in details view ([#212](https://github.com/gitui-org/gitui/issues/212))
## [0.8.1] - 2020-07-07
### Added
- open file in editor [[@jonstodle](https://github.com/jonstodle)]([#166](https://github.com/gitui-org/gitui/issues/166))
### Fixed
- switch deprecated transitive dependency `net2`->`socket2` [in `crossterm`->`mio`]([#66](https://github.com/gitui-org/gitui/issues/66))
- crash diffing a stash that was created via cli ([#178](https://github.com/gitui-org/gitui/issues/178))
- zero delta file size in diff of untracked binary file ([#171](https://github.com/gitui-org/gitui/issues/171))
- newlines not visualized correctly in commit editor ([#169](https://github.com/gitui-org/gitui/issues/169))

## [0.8.0] - 2020-07-06
### Added
- core homebrew [formulae](https://formulae.brew.sh/formula/gitui#default): `brew install gitui` [[@vladimyr](https://github.com/vladimyr)](<[#137](https://github.com/gitui-org/gitui/issues/137)>)
- show file sizes and delta on binary diffs ([#141](https://github.com/gitui-org/gitui/issues/141))

- external editor support for commit messages [[@jonstodle](https://github.com/jonstodle)]([#46](https://github.com/gitui-org/gitui/issues/46))

### Changed
- use terminal blue as default selection background ([#129](https://github.com/gitui-org/gitui/issues/129))
- author column in revlog is now fixed width for better alignment ([#148](https://github.com/gitui-org/gitui/issues/148))
- cleaner tab bar and background work indicating spinner:

### Fixed
- clearer help headers ([#131](https://github.com/gitui-org/gitui/issues/131))
- display non-utf8 commit messages at least partially ([#150](https://github.com/gitui-org/gitui/issues/150))
- hooks ignored when running `gitui` in subfolder of workdir ([#151](https://github.com/gitui-org/gitui/issues/151))
- better scrolling in file-trees [[@tisorlawan](https://github.com/tisorlawan)]([#144](https://github.com/gitui-org/gitui/issues/144))
- show untracked files in stash commit details [[@MCord](https://github.com/MCord)]([#130](https://github.com/gitui-org/gitui/issues/130))
- in some repos looking up the branch name was a bottleneck ([#159](https://github.com/gitui-org/gitui/issues/159))
- some optimizations in reflog
- fix arrow utf8 encoding in help window [[@daober](https://github.com/daober)]([#142](https://github.com/gitui-org/gitui/issues/142))
## [0.7.0] - 2020-06-15
### Added
- Inspect stash commit in detail ([#121](https://github.com/gitui-org/gitui/issues/121))
- Support reset/revert individual hunks ([#11](https://github.com/gitui-org/gitui/issues/11))
- Commit Amend (`ctrl+a`) when in commit popup ([#89](https://github.com/gitui-org/gitui/issues/89))

### Changed
- file trees: `arrow-right` on expanded folder moves down into folder
- better scrolling in diff ([#52](https://github.com/gitui-org/gitui/issues/52))
- display current branch in status/log ([#115](https://github.com/gitui-org/gitui/issues/115))
- commit msg popup: add cursor and more controls (`arrow-left/right`, `delete` & `backspace`) [[@alistaircarscadden](https://github.com/alistaircarscadden)]([#46](https://github.com/gitui-org/gitui/issues/46))
- moved `theme.ron` from `XDG_CACHE_HOME` to `XDG_CONFIG_HOME` [[@jonstodle](https://github.com/jonstodle)](<[#98](https://github.com/gitui-org/gitui/issues/98)>)
### Fixed
- reset file inside folder failed when running `gitui` in a subfolder too ([#118](https://github.com/gitui-org/gitui/issues/118))
- selection could disappear into collapsed folder ([#120](https://github.com/gitui-org/gitui/issues/120))
- `Files: loading` sometimes wrong ([#119](https://github.com/gitui-org/gitui/issues/119))
## [0.6.0] - 2020-06-09

### Changed
- changed hotkeys for selecting stage/workdir (**Note:** use `[w]`/`[s]` to change between workdir and stage) and added hotkeys (`[1234]`) to switch to tabs directly ([#92](https://github.com/gitui-org/gitui/issues/92))
- `arrow-up`/`down` on bottom/top of status file list switches focus ([#105](https://github.com/gitui-org/gitui/issues/105))
- highlight tags in revlog better
### Added
- New `Stage all [a]`/`Unstage all [a]` in changes lists ([#82](https://github.com/gitui-org/gitui/issues/82))
- add `-d`, `--directory` options to set working directory via program arg [[@alistaircarscadden](https://github.com/alistaircarscadden)]([#73](https://github.com/gitui-org/gitui/issues/73))
- commit detail view in revlog ([#80](https://github.com/gitui-org/gitui/issues/80))
### Fixed
- app closes when staging invalid file/path ([#108](https://github.com/gitui-org/gitui/issues/108))
- `shift+tab` not working on windows [[@MCord](https://github.com/MCord)]([#111](https://github.com/gitui-org/gitui/issues/111))
## [0.5.0] - 2020-06-01
### Changed
- support more commands allowing optional multiline commandbar ([#83](https://github.com/gitui-org/gitui/issues/83))

### Added
- support adding untracked file/folder to `.gitignore` ([#44](https://github.com/gitui-org/gitui/issues/44))
- support reverse tabbing using shift+tab ([#92](https://github.com/gitui-org/gitui/issues/92))
- switch to using cmd line args instead of `ENV` (`-l` for logging and `--version`) **please convert your GITUI_LOGGING usage** [[@shenek](https://github.com/shenek)]([#88](https://github.com/gitui-org/gitui/issues/88))
- added missing LICENSE.md files in sub-crates [[@ignatenkobrain](https://github.com/ignatenkobrain)]([#94](https://github.com/gitui-org/gitui/pull/94))
### Fixed
- error when diffing huge files ([#96](https://github.com/gitui-org/gitui/issues/96))
- expressive error when run in bare repos ([#100](https://github.com/gitui-org/gitui/issues/100))
## [0.4.0] - 2020-05-25
### Added
- stashing support (save,apply,drop) ([#3](https://github.com/gitui-org/gitui/issues/3))
### Changed
- log tab refreshes when head changes ([#78](https://github.com/gitui-org/gitui/issues/78))
- performance optimization of the log tab in big repos
- more readable default color for the commit hash in the log tab
- more error/panic resilience (`unwrap`/`panic` denied by clippy now) [[@MCord](https://github.com/MCord)](<[#77](https://github.com/gitui-org/gitui/issues/77)>)
### Fixes
- panic on small terminal width ([#72](https://github.com/gitui-org/gitui/issues/72))

## [0.3.0] - 2020-05-20
### Added
- support color themes and light mode [[@MCord](https://github.com/MCord)]([#28](https://github.com/gitui-org/gitui/issues/28))
### Changed
- more natural scrolling in log tab ([#52](https://github.com/gitui-org/gitui/issues/52))
### Fixed
- crash on commit when git name was not set ([#74](https://github.com/gitui-org/gitui/issues/74))
- log tab shown empty in single commit repos ([#75](https://github.com/gitui-org/gitui/issues/75))

## [0.2.6] - 2020-05-18
### Fixed
- fix crash help in small window size ([#63](https://github.com/gitui-org/gitui/issues/63))
## [0.2.5] - 2020-05-16
### Added
- introduced proper changelog
- hook support on windows [[@MCord](https://github.com/MCord)]([#14](https://github.com/gitui-org/gitui/issues/14))
### Changed
- show longer commit messages in log view
- introduce proper error handling in `asyncgit` [[@MCord](https://github.com/MCord)]([#53](https://github.com/gitui-org/gitui/issues/53))
- better error message when trying to run outside of a valid git repo ([#56](https://github.com/gitui-org/gitui/issues/56))
- improve ctrl+c handling so it is checked first and no component needs to worry of blocking it
### Fixed
- support multiple tags per commit in log ([#61](https://github.com/gitui-org/gitui/issues/61))
## [0.2.3] - 2020-05-12
### Added
- support more navigation keys: home/end/pageUp/pageDown ([#43](https://github.com/gitui-org/gitui/issues/43))
- highlight current tab a bit better
## [0.2.2] - 2020-05-10
### Added
- show tags in commit log ([#47](https://github.com/gitui-org/gitui/issues/47))
- support home/end key in diff ([#43](https://github.com/gitui-org/gitui/issues/43))
### Changed
- close application shortcut is now the standard `ctrl+c`
- some diff improvements ([#42](https://github.com/gitui-org/gitui/issues/42))
### Fixed
- document tab key to switch tabs ([#48](https://github.com/gitui-org/gitui/issues/48))
================================================
FILE: CONTRIBUTING.md
================================================
# 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
================================================
FILE: Cargo.toml
================================================
[package]
name = "gitui"
version = "0.28.0"
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",
]
[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"]
[dependencies]
anyhow = "1.0"
asyncgit = { path = "./asyncgit", version = "0.28.0", 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.28", features = ["serde"] }
dirs = "6.0"
easy-cast = "0.5"
filetreelist = { path = "./filetreelist", version = "0.5" }
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.29", default-features = false, features = [
"crossterm",
"serde",
] }
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",
] }
tui-textarea = "0.7"
two-face = { version = "0.4.4", default-features = false }
unicode-segmentation = "1.12"
unicode-truncate = "2.0"
unicode-width = "0.2"
which = "8.0"
[build-dependencies]
chrono = { version = "0.4", default-features = false, features = ["clock"] }
[dev-dependencies]
env_logger = "0.11"
pretty_assertions = "1.4"
tempfile = "3"
[badges]
maintenance = { status = "actively-developed" }
# 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
================================================
FILE: FAQ.md
================================================
## <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.
================================================
FILE: KEY_CONFIG.md
================================================
# 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))
================================================
FILE: LICENSE.md
================================================
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.
================================================
FILE: Makefile
================================================
.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:
cargo sort -c -w "."
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
================================================
FILE: NIGHTLIES.md
================================================
# 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)
================================================
FILE: README.md
================================================
<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
```
##### [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>
================================================
FILE: THEMES.md
================================================
# 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`.
================================================
FILE: assets/expandable-commands.drawio
================================================
<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>
================================================
FILE: assets/log-commit-info.drawio
================================================
<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>
================================================
FILE: assets/options.drawio
================================================
<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>
================================================
FILE: assets/stashing.drawio
================================================
<mxfile host="app.diagrams.net" modified="2020-05-19T12:41:55.023Z" 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="G_7kN2nuUboj_f0S3bPh" version="13.1.3" type="device" pages="2"><diagram id="hAseTAaTBIQ-p2lVXsqS" name="Page-1">7Vlbk5owFP41zLQPO0NAUB+97LZ92Je1l+coEegGQiGu2l/f3CBEcNzuiLK2L5p8JyTk+87JSYLlzpLdpxxm0SMJELYcO9hZ7txynOHIZ78c2EvAc8cSCPM4kBDQwCL+jRRoK3QTB6gwGlJCMI0zE1yRNEUramAwz8nWbLYm2Bw1gyFqAIsVxE30RxzQSKIjZ6jxzygOo3Jk4Kv5JbBsrGZSRDAg2xrk3lvuLCeEylKymyHMuSt5kc89HLFWL5ajlL7mgadvOMToyV8+foFPYA6+D4Y/71QvLxBv1ITVy9J9yUBONmmAeCe25U63UUzRIoMrbt0yyRkW0QSzGmBF1R3KKdodfU9QzZ55DSIJovmeNSkfsBVhpceo6lbT7/oKi2rUOyUIleRh1bVmhRUUMX9BktNDkoBJ0qhJUuV4BkmgK5LcBkkLCummaFDFJk1NPgqak2c0I5jkDElJylpO1zHGBxDEcZiy6oqRhBg+5RTGLFYnypDEQcCHaRXAlKgDDVocddAmQVcKDNoUKKI4DXuqwZqkdKFeCpxDEO+kIK0LR1eC+E1B4AvqqRgdBIQzvnJEjPu3cA/8Yc+yWylar1gamiy1pDe31ZM6S2+guVO69fx2KMK18xto24f5mJNdZDBl5ZCKmUtsWQIiC7IzhMLZ2MvDtgwzu+ilpvV8aZ9BYO+0wBfNl6C5h4yYdJbDurO/xgmSpcmGRoxiURbapjDpIqtitKa9C8K2jf5lo7C5zZxkGd6boVhTwv+14Ufc6UqyPGHGPFzCD+wFbSlia+kjL3IKbe72d2uYxGwQ8XhCUlII3o0mhbg84A3sbKfH1UHtHV5TeGzeHBWH8KpW8uAJJhgy52X+Yh6fucfoO9UWVG1Lqd/UjaO7kXRXljL3lGZJd2UWzs9q0v05DES1HgIcFR7PDToMDFiGAofU+sVBMyC4UYUEN9aCgltEWHC8DAwO2gKpzU0GyGvYq0wVcTpkPB40uiXQTNt7jXs1WAZRZRrUTCqWtBQ1Gyw0HtaGP1RVVCtp66DpcKpdwzPfU27qYKm7+vEBeI2lbp6T7HYV8MBpBbyLKtA8Qj/EGIFbzfavuda7bAgMWwVonhBvVIDqMvp6Coz+b4pPZYrrq1R2fLZLlAO+z0BadWFyfG2pvL/O2bgzzlo+PnnTneXNBW/Cye1NSnO4euY7x9v05bfK0p0rt1yzNGRh/+G/o0nb+nJhUVo/r8lrrVvdjTqnv69ddDPqvOsPbGe5MASXujC01BlZ22qnY/f+Dw==</diagram><diagram id="o9nFayAMEWxc9pClOnIG" name="Page-2">7Vxbm5rKEv01edzn46KT4dEr4BZmvKK8nI+LERDEM6ICv/6s4qbOOMkkMdl7f5skRm2a7urqqlWrurE/8Z0gFl+MnaOE9sr/xDF2/InvfuK4z48P+J8KkrygyQt5wfrFtfMi9lwwcdNVUcgUpQfXXu2vKkZh6Efu7rrQCrfblRVdlRkvL+HputqX0L/udWesV28KJpbhvy3VXDty8tJH7vO5XFq5a6fsmX0oxhcYZeViJHvHsMPTRRHf+8R3XsIwyj8FcWflk+5KvSi7l9Pmj6cvf3Y1o20vV8J/48MfeWP977mlGsLLahvdt2kub/po+IdCX8VYo6RUIIa9o49ukGm6fVy9RC70OzTMlf8c7t3IDbe4boZRFAao4NOFtmFt1i/hYWt3Qj98yZriv2R/Ltpo+e6a7o3CHUqN/S63gC9uvILU7azLVlnKlCX4bBuR8Ylv5V+5/v64/sS14wDj7TxLKqcn7YapxQcrZVxDGjNWNzwOeZu3kyavJM2jFVhHxWudlI6Q2oHlypITmWIzfdo6e0NrvjxPBqEtjU9P7uMRd/HDrZUOAyHRk8f4abppDvm8nuy2OX0xSA1NODxP5Hjo9dCWvdOlcYjvvOI5p1VHXhvifKdzDoOyRPX6WZkd+L7NDI6rLuMOp62DOmlFSqfByl0reeq0UI/aUpmVFvuQx11q6ovOD4621twMNfVoikLy5LYTnZNDxZuxQ7pnOtvLXZlVJ5vPstd41MV5YCWP0M2zaNH/0sBf8qM19alAhtU0f7c5f2OLa0H2FE55I2/LlUV9Z4onQXbVQM/+LjPZrEB40dGGnEKXXdKnvLZEYWdux77lsr4dzA+2pAhy0N+bHfn47MWJrqmMLFFfvRP010Qf6bUMcvJaBmX6y2Wgd97QxowB3ahd6s/xDc0ObdLVpQ6C5tEMZld9m4Fw0Cff7HcrS+NkqTVTHbZk4j5bGjjmVt0vF2P/zw5zGnqzdW4LGxf1U+iEWXLrbP50WC1ppPm8DHbZLFr8uGmKM/QgkBUGsAgeFsEZC3W3DGL/KWkz+sJhhlof1mofcG2vazrzlLwnaTmyyoJjaNSHZkJd87eGNMo0pnSozsC3FvMdZNqMg7ljS/NEX1AbDmNLrYdhIsBvrIOdKgeTH2yH7HgyZFVtnPbiIaf7GDFrBTNvDPl0aMTQBtCuz6wmcjba8c52P+DD8MWnidDA9cikea80Mt5ZfBt63Txg3vb6Yv1ZFnMtDDUfPbIptMFC944ezNZLDhoQ5wk09j97oTKGpjczH4OGzO3oQZbmB4vrB/qk7UFaaFX+XGrpyW2lT1K7gXtLLT3IXfh4dw8t9ze66B/0hGVMvoXy3smSSBbWsQJ7Z7psPiNuK1YW1MbF6FNb8vdkXctAaAwxp6sALaXheklIBGmHGqHHfg1rOtgdNjUWOx8tnW7M+1nqzq0eIGNqibGzmoZr4Bphm4/W9yanvqC1wOSaTC7BjMaQ5GPQoZ9RpAf9yNDiJnqRdS2GJY53S66H6yVGtbzML7uZZEXbpW+Fa2XSIL2fTM7P5mCozTmS1qKRavEe13gDlqFPWMjY93TIaJL/YHR0Dda1GS7sZLmAfkWyC9YxoRXZa05tsc/YC+VxyGG8wMO372+8KkPJsb+SCpRM4fnShuyegR8EGOkeflGNiMoy7Wi6Y2r+nhB1Is738ClYkepU13PtUTukPbSZ9REBDTwDWgLCQ4vjDazXffZVJvOP9HSERhp2F5LyKm8sxp5Bn7nXWK5OSylbl977Bsvpux74e7N7gVn8ODG5iGLMAZKSt9+YC1ghRi67RSxZF8gVQPsR7gQS9uEhsWOLs/Aymv2Z+7N0lp6wj/zE3AiuEcw9u3O2Q5PXfSvQ9yZvwef0ncX5LjTy+aytGGNpYJ5jX9+OPr8nazm2c0wpUOsS06et04DGH8xjW/Mh01jUNbIXINtXMGfel48zVpeUae+ol8g3UfsribCCkNffY6zNPP76XMktPsApQltkoePbfqkD0cnbCx1E6MM3F8ptbBJ3LOZ1p2830ZITSD8H8jVwh3Sp6T5808/Q39uXOoJfzxq5X1/4ZoZJ7dMSlgm/3Q4X46NF5dN2hm/wQfAPH5iazQe1EWdtVGM/8xeTX0eYzwdqB1jhkt9C1ojQ7S3GtG7M+VnmG+1vEPVOptgnHNqZQYzXDG3HxyW3r7CK+if8UKaD0wVyBkuN3ZkSYkXP8U1J923R95ZT6JoTYIvyg9xRToXHo6287dKvCLuHHsWD9tHI9R/pC51kpRjjgDNU/dtceQ/Gz8Wbn8S21KDYApQgXTx7N95f+xr8L0OUTrsBPGhUHHA6uuBX1Vjd1xgPPV9ivHutV2pnkHPbrA/IGqgh9Id4zO50cbwnnyx0zleMIhggTowK1FNDso/njpBFMBqDxds7W1TDbDxp49HKMaORsZNSfm9U87eav/0G/iazSl43Wi4GW0NrANvnQYaV0+wemndg0fJiLGBpOTsDbiCuiQOMPZs5wsS32IR+K/8R4bteeDG+1o32L3XlH0ySd4K2RcEz0jfsMFa27TPeVqwlXF+yFlz3gE9HC31XeRXhfd72hZ56B8Xb37LGB5ovwiqbc+C/swgyOktudoVjNgd8AhaAPSAutsnSgPengqXOuD6nT38Y3dIzOsx+J3dz7GAGvpbVIV+gNYYzDnSE7XUGqnBq8ldn4gpfyZBeMcj0LZJmGXOlS72QImctg6PBza4jyI2oCH1RVHSH12ieqql1i8UyNYutWezHWaxyBxar8HlEqLPxv1s2vuGL9bpYvUCBS9R7xekFeXPB6c/8M5ufjCEhHqLNco4OS85B3IP3aT5if+xfSf5hVB/xt1F9ebpmY/IFmlbIdUbSitF9HUV12FiOXaRBRpC3OU9FPoSZWufryjk/3ZFHnzl1TNhHs3+YcoP/5fJXOdURUSOBxx5tzL4d+MTyYKFv+UnOgfbvyVmOq+LnJWpdoHrJVMFC+6wB9NJ7JcucfwV1hNnMFabzjRUrEyEokU9mIJ9IiOK70O3OljYVb1oBncztV5hu2msCyY4r0WFM7eS+k2OCrc8JVXIduCz0OY/IS26hk7Gdp7C/YDVhPfJe8qh3+Guho3CtTp0MnS7zzMzTpbaDXIq4ZGSLwinjdO9wUrVrNep8/EY+LtkhRR6MD1FB9a+4p2TDh5Cr8wOHosQZ3TLuTah3wHuVr+oZoskFJuRXf5izskrX+ovWG4uRfJy3vscZkzr7rrPvd7LvDDVhm7C3AXOJdor3cd6mdr/C237lLkox//+mHNygVcbOaT0X/UifnNajBaQDfgw77WIFsgnpZz/F6axmwemYu3I6rxkSqkHSmaGxPt7b9mJM65F7uZ8jngm2TCsMH9tlgaQl+8wxrs5R6xz1V+eo3uy9HeNUTZ16xfGuaDdawMqlzSPdaV7GjpJ3SoPtMuiFSqe1BydeDztgZEBHpbNZ3xqhqQnoIeeY8o0YQ8zs2Z2lI45tjIlvio5qcrC2ziwdT1DOD44mP/IVZoaXGq16sCbx5Ctee6dTnc4sAbvCd9lXNstv1Ovl9TxYGTwPFoC6Iyo7DaZyMpgOxMFU7ePVNRdOVwcKofxkaFW9JK8nn2ywMlubFeWDMj8u28/1Nin63WaIgrqDzOvQfk+hsW0LBjjLVyaoPEcTXAuILcbdzCKgn7GYcStqo8i3qKzwLNLfFmi2pZWO/n6Uey3qjkuGS/flqxbzfC6or4zfd+je/JkqPR8ndKF2zuOcJ+QZVJ/KIHeiuHghB7PI6l7rttK/7ZscLFMaNKmd1blerHTKNjalXtlzWR5Ryv4QZUqdlh5QzUV1D6JwWV/n5kdTmzPWdvPKJjA3G/1sF+cyRCSBKeedbABzL5F3GD0WWcuc5qt/h8wmfZqWmY3M/sa1m7YltferyewwdBvfs34TV9nN673FaZ3d1NnNh/cWmXvsLardXr2Oc891nA2YON/6iaxF7Y6SKhf4fU9PQIqxBDbX/XDOUj0ZplyhWIFq9Up0vRJ9tRKd5T6IsGB0id29zFnk73hKYvT+U67ben3mnhnLOCDt9X5m/WUqFwgxuuuK8/OGEHEWf++zXPmzrPVqcs23ftNqsvfeanKxs/5LOFexqvZv2v8XIRey0h/OIGPFLRDCuyvfqlBU/qHdrxqvarz6e+BVvfv1j9794v7mu195xnjj95q9+NXuV/Ny90vmimdE3csdsCt++a1dsB+Tot6Dq/fgivw3Q3H4/9ixSI/n1TnEqA8/ZcW8++S/N6qZ4j2ZornqM+tstrg1zSL5m2dw8w14GEWenJHd+vRmDQmxKotjOWuTmYpDdu/NITXesWnmCVnJSgkzlkAa/eNrdWdsY2tsq7HtN2IbV2PbX4Ft9K7fF9funhtf4xreNft7MG36zgkb9S5qnSX/kl3Ur5ywUf9C896/0MxwzPGtH/8Neqyks3/IE+8Xz4TUv+mpd1L/st/01M+C3PVZkOtzJ36Kh42YCiF+6zMhBQ6T/XwDz4p91zNWbH8hzyBG5nyVkXVb++EU+EVMBJHLSHch5AhKbIDvHYcc7JHbH3TcB2ayMxOMip7T5QcMuOfRlCgOCAcrEZz8GdDYt5CJqV6mcQYZTINmhZ5oANcGLg2+wGahyYwJFLP3fqx4OyPtbEaKc5f88veoGdZ5Tc3Q5jzsc62LAuQgGyQeOT8hAieIeYFN2Xcf+kvIpnvX8+W83iW3eH37vKbjE+lfuziucfUSreJX51t+43BItjqxMojFVRisopcE9xWtlGdcFod8fi6+ns4nZjaFosy5OC2TY9n/MI3isM7ioM511fj5MEp8KM6jLL+ej83Mrl2cPcr3/g8=</diagram></mxfile>
================================================
FILE: asyncgit/Cargo.toml
================================================
[package]
name = "asyncgit"
version = "0.28.0"
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 = ["concurrency", "asynchronous"]
keywords = ["git"]
[features]
default = ["trace-libgit"]
trace-libgit = []
vendor-openssl = ["openssl-sys"]
[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.6" }
gix = { version = "0.78.0", default-features = false, features = [
"max-performance",
"revision",
"mailmap",
"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"
================================================
FILE: asyncgit/README.md
================================================
# 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.
================================================
FILE: asyncgit/src/asyncjob/mod.rs
================================================
//! 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
);
}
}
================================================
FILE: asyncgit/src/blame.rs
================================================
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(())
}
}
================================================
FILE: asyncgit/src/branches.rs
================================================
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)
}
}
================================================
FILE: asyncgit/src/cached/branchname.rs
================================================
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)
}
}
================================================
FILE: asyncgit/src/cached/mod.rs
================================================
//! 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;
================================================
FILE: asyncgit/src/commit_files.rs
================================================
use crate::{
error::Result,
sync::{self, commit_files::OldNew, CommitId, RepoPath},
AsyncGitNotification, StatusItem,
};
use crossbeam_channel::Sender;
use std::sync::{
atomic::{AtomicUsize, Ordering},
Arc, Mutex,
};
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,
}
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 current(
&self,
) -> Result<Option<(CommitFilesParams, ResultType)>> {
let c = self.current.lock()?;
c.as_ref()
.map_or(Ok(None), |c| Ok(Some((c.0, c.1.clone()))))
}
///
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(());
}
log::trace!("request: {params:?}");
{
let current = self.current.lock()?;
if let Some(c) = &*current {
if c.0 == params {
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();
self.pending.fetch_add(1, Ordering::Relaxed);
rayon_core::spawn(move || {
Self::fetch_helper(&repo, params, &arc_current)
.expect("failed to fetch");
arc_pending.fetch_sub(1, Ordering::Relaxed);
sender
.send(AsyncGitNotification::CommitFiles)
.expect("error sending");
});
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,
)?;
log::trace!("get_commit_files: {:?} ({})", params, res.len());
{
let mut current = arc_current.lock()?;
*current = Some(Request(params, res));
}
Ok(())
}
}
================================================
FILE: asyncgit/src/diff.rs
================================================
use crate::{
error::Result,
hash,
sync::{
self, commit_files::OldNew, diff::DiffOptions, CommitId,
RepoPath,
},
AsyncGitNotification, FileDiff,
};
use crossbeam_channel::Sender;
use std::{
hash::Hash,
sync::{
atomic::{AtomicUsize, Ordering},
Arc, Mutex,
},
};
///
#[derive(Debug, Hash, Clone, PartialEq, Eq)]
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,
}
///
#[derive(Debug, Hash, Clone, PartialEq, Eq)]
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,
}
struct Request<R, A>(R, Option<A>);
#[derive(Default, Clone)]
struct LastResult<P, R> {
params: P,
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,
}
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 last(&self) -> Result<Option<(DiffParams, FileDiff)>> {
let last = self.last.lock()?;
Ok(last.clone().map(|res| (res.params, res.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: DiffParams,
) -> Result<Option<FileDiff>> {
log::trace!("request {params:?}");
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_diff_helper(
&repo,
params,
&arc_last,
&arc_current,
hash,
);
let notify = match notify {
Err(e) => {
log::error!("get_diff_helper error: {e}");
true
}
Ok(notify) => notify,
};
arc_pending.fetch_sub(1, Ordering::Relaxed);
sender
.send(if notify {
AsyncGitNotification::Diff
} else {
AsyncGitNotification::FinishUnchanged
})
.expect("error sending diff");
});
Ok(None)
}
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 notify = false;
{
let mut current = arc_current.lock()?;
if current.0 == hash {
current.1 = Some(res.clone());
notify = true;
}
}
{
let mut last = arc_last.lock()?;
*last = Some(LastResult {
result: res,
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(())
}
}
================================================
FILE: asyncgit/src/error.rs
================================================
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("git: no head found")]
NoHead,
///
#[error("git: conflict during rebase")]
RebaseConflict,
///
#[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,
}
///
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))
}
}
================================================
FILE: asyncgit/src/fetch_job.rs
================================================
//!
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)
}
}
================================================
FILE: asyncgit/src/filter_commits.rs
================================================
use rayon::{
prelude::ParallelIterator,
slice::{ParallelSlice, ParallelSliceMut},
};
use crate::{
asyncjob::{AsyncJob, RunParams},
error::Result,
sync::{self, CommitId, RepoPath, SharedCommitFilterFn},
AsyncGitNotification, ProgressPercent,
};
use std::{
sync::{
atomic::{AtomicBool, AtomicUsize, Ordering},
Arc, Mutex,
},
time::{Duration, Instant},
};
///
pub struct CommitFilterResult {
///
pub result: Vec<CommitId>,
///
pub duration: Duration,
}
enum JobState {
Request {
commits: Vec<CommitId>,
repo_path: RepoPath,
},
Response(Result<CommitFilterResult>),
}
///
#[derive(Clone)]
pub struct AsyncCommitFilterJob {
state: Arc<Mutex<Option<JobState>>>,
filter: SharedCommitFilterFn,
cancellation_flag: Arc<AtomicBool>,
}
///
impl AsyncCommitFilterJob {
///
pub fn new(
repo_path: RepoPath,
commits: Vec<CommitId>,
filter: SharedCommitFilterFn,
cancellation_flag: Arc<AtomicBool>,
) -> Self {
Self {
state: Arc::new(Mutex::new(Some(JobState::Request {
repo_path,
commits,
}))),
filter,
cancellation_flag,
}
}
///
pub fn result(&self) -> Option<Result<CommitFilterResult>> {
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
}
fn run_request(
&self,
repo_path: &RepoPath,
commits: Vec<CommitId>,
params: &RunParams<AsyncGitNotification, ProgressPercent>,
) -> JobState {
let result = self
.filter_commits(repo_path, commits, params)
.map(|(start, result)| CommitFilterResult {
result,
duration: start.elapsed(),
});
JobState::Response(result)
}
fn filter_commits(
&self,
repo_path: &RepoPath,
commits: Vec<CommitId>,
params: &RunParams<AsyncGitNotification, ProgressPercent>,
) -> Result<(Instant, Vec<CommitId>)> {
scopetime::scope_time!("filter_commits");
let total_amount = commits.len();
let start = Instant::now();
//note: for some reason >4 threads degrades search performance
let pool =
rayon::ThreadPoolBuilder::new().num_threads(4).build()?;
let idx = AtomicUsize::new(0);
let mut result = pool.install(|| {
commits
.into_iter()
.enumerate()
.collect::<Vec<(usize, CommitId)>>()
.par_chunks(1000)
.filter_map(|c| {
//TODO: error log repo open errors
sync::repo(repo_path).ok().map(|repo| {
c.iter()
.filter_map(|(e, c)| {
let idx = idx.fetch_add(
1,
std::sync::atomic::Ordering::Relaxed,
);
if self
.cancellation_flag
.load(Ordering::Relaxed)
{
return None;
}
Self::update_progress(
params,
ProgressPercent::new(
idx,
total_amount,
),
);
(*self.filter)(&repo, c)
.ok()
.and_then(|res| {
res.then_some((*e, *c))
})
})
.collect::<Vec<_>>()
})
})
.flatten()
.collect::<Vec<_>>()
});
result.par_sort_by(|a, b| a.0.cmp(&b.0));
let result = result.into_iter().map(|c| c.1).collect();
Ok((start, result))
}
fn update_progress(
params: &RunParams<AsyncGitNotification, ProgressPercent>,
new_progress: ProgressPercent,
) {
match params.set_progress(new_progress) {
Err(e) => log::error!("progress error: {e}"),
Ok(result) if result => {
if let Err(e) =
params.send(AsyncGitNotification::CommitFilter)
{
log::error!("send error: {e}");
}
}
_ => (),
}
}
}
impl AsyncJob for AsyncCommitFilterJob {
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 { commits, repo_path } => {
self.run_request(&repo_path, commits, ¶ms)
}
JobState::Response(result) => {
JobState::Response(result)
}
});
}
Ok(AsyncGitNotification::CommitFilter)
}
}
================================================
FILE: asyncgit/src/lib.rs
================================================
/*!
`AsyncGit` is a library that provides non-blocking access to Git
operations, enabling `GitUI` to perform potentially slow Git operations
in the background while keeping the user interface responsive.
It also provides synchronous Git operations.
It wraps libraries like git2 and gix.
*/
#![forbid(missing_docs)]
#![deny(
mismatched_lifetime_syntaxes,
unused_imports,
unused_must_use,
dead_code,
unstable_name_collisions,
unused_assignments,
deprecated
)]
#![deny(clippy::all, clippy::perf, clippy::nursery, clippy::pedantic)]
#![deny(
clippy::filetype_is_file,
clippy::cargo,
clippy::unwrap_used,
clippy::panic,
clippy::match_like_matches_macro,
clippy::needless_update
//TODO: get this in someday since expect still leads us to crashes sometimes
// clippy::expect_used
)]
#![allow(
clippy::module_name_repetitions,
clippy::must_use_candidate,
clippy::missing_errors_doc,
clippy::empty_docs,
clippy::unnecessary_debug_formatting
)]
//TODO:
#![allow(
clippy::significant_drop_tightening,
clippy::missing_panics_doc,
clippy::multiple_crate_versions
)]
pub mod asyncjob;
mod blame;
mod branches;
pub mod cached;
mod commit_files;
mod diff;
mod error;
mod fetch_job;
mod filter_commits;
mod progress;
mod pull;
mod push;
mod push_tags;
pub mod remote_progress;
pub mod remote_tags;
mod revlog;
mod status;
pub mod sync;
mod tags;
mod treefiles;
pub use crate::{
blame::{AsyncBlame, BlameParams},
branches::AsyncBranchesJob,
commit_files::{AsyncCommitFiles, CommitFilesParams},
diff::{AsyncDiff, DiffParams, DiffType},
error::{Error, Result},
fetch_job::AsyncFetchJob,
filter_commits::{AsyncCommitFilterJob, CommitFilterResult},
progress::ProgressPercent,
pull::{AsyncPull, FetchRequest},
push::{AsyncPush, PushRequest},
push_tags::{AsyncPushTags, PushTagsRequest},
remote_progress::{RemoteProgress, RemoteProgressState},
revlog::{AsyncLog, FetchStatus},
status::{AsyncStatus, StatusParams},
sync::{
diff::{DiffLine, DiffLineType, FileDiff},
remotes::push::PushType,
status::{StatusItem, StatusItemType},
},
tags::AsyncTags,
treefiles::AsyncTreeFilesJob,
};
pub use git2::message_prettify;
use std::{
collections::hash_map::DefaultHasher,
hash::{Hash, Hasher},
};
/// this type is used to communicate events back through the channel
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub enum AsyncGitNotification {
/// this indicates that no new state was fetched but that a async process finished
FinishUnchanged,
///
Status,
///
Diff,
///
Log,
///
FileLog,
///
CommitFiles,
///
Tags,
///
Push,
///
PushTags,
///
Pull,
///
Blame,
///
RemoteTags,
///
Fetch,
///
Branches,
///
TreeFiles,
///
CommitFilter,
}
/// helper function to calculate the hash of an arbitrary type that implements the `Hash` trait
pub fn hash<T: Hash + ?Sized>(v: &T) -> u64 {
let mut hasher = DefaultHasher::new();
v.hash(&mut hasher);
hasher.finish()
}
///
#[cfg(feature = "trace-libgit")]
pub fn register_tracing_logging() -> bool {
fn git_trace(level: git2::TraceLevel, msg: &[u8]) {
log::info!("[{:?}]: {}", level, String::from_utf8_lossy(msg));
}
git2::trace_set(git2::TraceLevel::Trace, git_trace).is_ok()
}
///
#[cfg(not(feature = "trace-libgit"))]
pub fn register_tracing_logging() -> bool {
true
}
================================================
FILE: asyncgit/src/progress.rs
================================================
//!
use easy_cast::{Conv, ConvFloat};
use std::cmp;
///
#[derive(Clone, Copy, Default, Debug, PartialEq, Eq)]
pub struct ProgressPercent {
/// percent 0..100
pub progress: u8,
}
impl ProgressPercent {
///
pub fn new(current: usize, total: usize) -> Self {
let total = f64::conv(cmp::max(current, total));
let progress = f64::conv(current) / total * 100.0;
let progress = u8::try_conv_nearest(progress).unwrap_or(100);
Self { progress }
}
///
pub const fn empty() -> Self {
Self { progress: 0 }
}
///
pub const fn full() -> Self {
Self { progress: 100 }
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_progress_zero_total() {
let prog = ProgressPercent::new(1, 0);
assert_eq!(prog.progress, 100);
}
#[test]
fn test_progress_zero_all() {
let prog = ProgressPercent::new(0, 0);
assert_eq!(prog.progress, 100);
}
#[test]
fn test_progress_rounding() {
let prog = ProgressPercent::new(2, 10);
assert_eq!(prog.progress, 20);
}
}
================================================
FILE: asyncgit/src/pull.rs
================================================
use crate::{
error::{Error, Result},
sync::{
cred::BasicAuthCredential,
remotes::{fetch, push::ProgressNotification},
RepoPath,
},
AsyncGitNotification, RemoteProgress,
};
use crossbeam_channel::{unbounded, Sender};
use std::{
sync::{Arc, Mutex},
thread,
};
///
#[derive(Default, Clone, Debug)]
pub struct FetchRequest {
///
pub remote: String,
///
pub branch: String,
///
pub basic_credential: Option<BasicAuthCredential>,
}
//TODO: since this is empty we can go with a simple AtomicBool to mark that we are fetching or not
#[derive(Default, Clone, Debug)]
struct FetchState {}
///
pub struct AsyncPull {
state: Arc<Mutex<Option<FetchState>>>,
last_result: Arc<Mutex<Option<(usize, String)>>>,
progress: Arc<Mutex<Option<ProgressNotification>>>,
sender: Sender<AsyncGitNotification>,
repo: RepoPath,
}
impl AsyncPull {
///
pub fn new(
repo: RepoPath,
sender: &Sender<AsyncGitNotification>,
) -> Self {
Self {
repo,
state: Arc::new(Mutex::new(None)),
last_result: Arc::new(Mutex::new(None)),
progress: Arc::new(Mutex::new(None)),
sender: sender.clone(),
}
}
///
pub fn is_pending(&self) -> Result<bool> {
let state = self.state.lock()?;
Ok(state.is_some())
}
///
pub fn last_result(&self) -> Result<Option<(usize, String)>> {
let res = self.last_result.lock()?;
Ok(res.clone())
}
///
pub fn progress(&self) -> Result<Option<RemoteProgress>> {
let res = self.progress.lock()?;
Ok(res.as_ref().map(|progress| progress.clone().into()))
}
///
pub fn request(&self, params: FetchRequest) -> Result<()> {
log::trace!("request");
if self.is_pending()? {
return Ok(());
}
self.set_request(¶ms)?;
RemoteProgress::set_progress(&self.progress, None)?;
let arc_state = Arc::clone(&self.state);
let arc_res = Arc::clone(&self.last_result);
let arc_progress = Arc::clone(&self.progress);
let sender = self.sender.clone();
let repo = self.repo.clone();
thread::spawn(move || {
let (progress_sender, receiver) = unbounded();
let handle = RemoteProgress::spawn_receiver_thread(
AsyncGitNotification::Pull,
sender.clone(),
receiver,
arc_progress,
);
let res = fetch(
&repo,
¶ms.branch,
params.basic_credential,
Some(progress_sender.clone()),
);
progress_sender
.send(ProgressNotification::Done)
.expect("closing send failed");
handle.join().expect("joining thread failed");
Self::set_result(&arc_res, res).expect("result error");
Self::clear_request(&arc_state).expect("clear error");
sender
.send(AsyncGitNotification::Pull)
.expect("AsyncNotification error");
});
Ok(())
}
fn set_request(&self, _params: &FetchRequest) -> Result<()> {
let mut state = self.state.lock()?;
if state.is_some() {
return Err(Error::Generic("pending request".into()));
}
*state = Some(FetchState {});
Ok(())
}
fn clear_request(
state: &Arc<Mutex<Option<FetchState>>>,
) -> Result<()> {
let mut state = state.lock()?;
*state = None;
Ok(())
}
fn set_result(
arc_result: &Arc<Mutex<Option<(usize, String)>>>,
res: Result<usize>,
) -> Result<()> {
let mut last_res = arc_result.lock()?;
*last_res = match res {
Ok(bytes) => Some((bytes, String::new())),
Err(e) => {
log::error!("fetch error: {e}");
Some((0, e.to_string()))
}
};
Ok(())
}
}
================================================
FILE: asyncgit/src/push.rs
================================================
use crate::{
error::{Error, Result},
sync::{
cred::BasicAuthCredential,
remotes::push::push_raw,
remotes::push::{ProgressNotification, PushType},
RepoPath,
},
AsyncGitNotification, RemoteProgress,
};
use crossbeam_channel::{unbounded, Sender};
use std::{
sync::{Arc, Mutex},
thread,
};
///
#[derive(Default, Clone, Debug)]
pub struct PushRequest {
///
pub remote: String,
///
pub branch: String,
///
pub push_type: PushType,
///
pub force: bool,
///
pub delete: bool,
///
pub basic_credential: Option<BasicAuthCredential>,
}
//TODO: since this is empty we can go with a simple AtomicBool to mark that we are fetching or not
#[derive(Default, Clone, Debug)]
struct PushState {}
///
pub struct AsyncPush {
state: Arc<Mutex<Option<PushState>>>,
last_result: Arc<Mutex<Option<String>>>,
progress: Arc<Mutex<Option<ProgressNotification>>>,
sender: Sender<AsyncGitNotification>,
repo: RepoPath,
}
impl AsyncPush {
///
pub fn new(
repo: RepoPath,
sender: &Sender<AsyncGitNotification>,
) -> Self {
Self {
repo,
state: Arc::new(Mutex::new(None)),
last_result: Arc::new(Mutex::new(None)),
progress: Arc::new(Mutex::new(None)),
sender: sender.clone(),
}
}
///
pub fn is_pending(&self) -> Result<bool> {
let state = self.state.lock()?;
Ok(state.is_some())
}
///
pub fn last_result(&self) -> Result<Option<String>> {
let res = self.last_result.lock()?;
Ok(res.clone())
}
///
pub fn progress(&self) -> Result<Option<RemoteProgress>> {
let res = self.progress.lock()?;
Ok(res.as_ref().map(|progress| progress.clone().into()))
}
///
pub fn request(&self, params: PushRequest) -> Result<()> {
log::trace!("request");
if self.is_pending()? {
return Ok(());
}
self.set_request(¶ms)?;
RemoteProgress::set_progress(&self.progress, None)?;
let arc_state = Arc::clone(&self.state);
let arc_res = Arc::clone(&self.last_result);
let arc_progress = Arc::clone(&self.progress);
let sender = self.sender.clone();
let repo = self.repo.clone();
thread::spawn(move || {
let (progress_sender, receiver) = unbounded();
let handle = RemoteProgress::spawn_receiver_thread(
AsyncGitNotification::Push,
sender.clone(),
receiver,
arc_progress,
);
let res = push_raw(
&repo,
params.remote.as_str(),
params.branch.as_str(),
params.push_type,
params.force,
params.delete,
params.basic_credential.clone(),
Some(progress_sender.clone()),
);
progress_sender
.send(ProgressNotification::Done)
.expect("closing send failed");
handle.join().expect("joining thread failed");
Self::set_result(&arc_res, res).expect("result error");
Self::clear_request(&arc_state).expect("clear error");
sender
.send(AsyncGitNotification::Push)
.expect("error sending push");
});
Ok(())
}
fn set_request(&self, _params: &PushRequest) -> Result<()> {
let mut state = self.state.lock()?;
if state.is_some() {
return Err(Error::Generic("pending request".into()));
}
*state = Some(PushState {});
Ok(())
}
fn clear_request(
state: &Arc<Mutex<Option<PushState>>>,
) -> Result<()> {
let mut state = state.lock()?;
*state = None;
Ok(())
}
fn set_result(
arc_result: &Arc<Mutex<Option<String>>>,
res: Result<()>,
) -> Result<()> {
let mut last_res = arc_result.lock()?;
*last_res = match res {
Ok(()) => None,
Err(e) => {
log::error!("push error: {e}");
Some(e.to_string())
}
};
Ok(())
}
}
================================================
FILE: asyncgit/src/push_tags.rs
================================================
use crate::{
error::{Error, Result},
sync::{
cred::BasicAuthCredential,
remotes::tags::{push_tags, PushTagsProgress},
RepoPath,
},
AsyncGitNotification, RemoteProgress,
};
use crossbeam_channel::{unbounded, Sender};
use std::{
sync::{Arc, Mutex},
thread,
};
///
#[derive(Default, Clone, Debug)]
pub struct PushTagsRequest {
///
pub remote: String,
///
pub basic_credential: Option<BasicAuthCredential>,
}
//TODO: since this is empty we can go with a simple AtomicBool to mark that we are fetching or not
#[derive(Default, Clone, Debug)]
struct PushState {}
///
pub struct AsyncPushTags {
state: Arc<Mutex<Option<PushState>>>,
last_result: Arc<Mutex<Option<String>>>,
progress: Arc<Mutex<Option<PushTagsProgress>>>,
sender: Sender<AsyncGitNotification>,
repo: RepoPath,
}
impl AsyncPushTags {
///
pub fn new(
repo: RepoPath,
sender: &Sender<AsyncGitNotification>,
) -> Self {
Self {
repo,
state: Arc::new(Mutex::new(None)),
last_result: Arc::new(Mutex::new(None)),
progress: Arc::new(Mutex::new(None)),
sender: sender.clone(),
}
}
///
pub fn is_pending(&self) -> Result<bool> {
let state = self.state.lock()?;
Ok(state.is_some())
}
///
pub fn last_result(&self) -> Result<Option<String>> {
let res = self.last_result.lock()?;
Ok(res.clone())
}
///
pub fn progress(&self) -> Result<Option<PushTagsProgress>> {
let res = self.progress.lock()?;
Ok(*res)
}
///
pub fn request(&self, params: PushTagsRequest) -> Result<()> {
log::trace!("request");
if self.is_pending()? {
return Ok(());
}
self.set_request(¶ms)?;
RemoteProgress::set_progress(&self.progress, None)?;
let arc_state = Arc::clone(&self.state);
let arc_res = Arc::clone(&self.last_result);
let arc_progress = Arc::clone(&self.progress);
let sender = self.sender.clone();
let repo = self.repo.clone();
thread::spawn(move || {
let (progress_sender, receiver) = unbounded();
let handle = RemoteProgress::spawn_receiver_thread(
AsyncGitNotification::PushTags,
sender.clone(),
receiver,
arc_progress,
);
let res = push_tags(
&repo,
params.remote.as_str(),
params.basic_credential.clone(),
Some(progress_sender),
);
handle.join().expect("joining thread failed");
Self::set_result(&arc_res, res).expect("result error");
Self::clear_request(&arc_state).expect("clear error");
sender
.send(AsyncGitNotification::PushTags)
.expect("error sending push");
});
Ok(())
}
fn set_request(&self, _params: &PushTagsRequest) -> Result<()> {
let mut state = self.state.lock()?;
if state.is_some() {
return Err(Error::Generic("pending request".into()));
}
*state = Some(PushState {});
Ok(())
}
fn clear_request(
state: &Arc<Mutex<Option<PushState>>>,
) -> Result<()> {
let mut state = state.lock()?;
*state = None;
Ok(())
}
fn set_result(
arc_result: &Arc<Mutex<Option<String>>>,
res: Result<()>,
) -> Result<()> {
let mut last_res = arc_result.lock()?;
*last_res = match res {
Ok(()) => None,
Err(e) => {
log::error!("push error: {e}");
Some(e.to_string())
}
};
Ok(())
}
}
================================================
FILE: asyncgit/src/remote_progress.rs
================================================
//!
use crate::{
error::Result,
progress::ProgressPercent,
sync::remotes::push::{AsyncProgress, ProgressNotification},
AsyncGitNotification,
};
use crossbeam_channel::{Receiver, Sender};
use git2::PackBuilderStage;
use std::{
sync::{Arc, Mutex},
thread::{self, JoinHandle},
};
/// used for push/pull
#[derive(Clone, Debug)]
pub enum RemoteProgressState {
///
PackingAddingObject,
///
PackingDeltafiction,
///
Pushing,
/// fetch progress
Transfer,
/// remote progress done
Done,
}
///
#[derive(Clone, Debug)]
pub struct RemoteProgress {
///
pub state: RemoteProgressState,
///
pub progress: ProgressPercent,
}
impl RemoteProgress {
///
pub fn new(
state: RemoteProgressState,
current: usize,
total: usize,
) -> Self {
Self {
state,
progress: ProgressPercent::new(current, total),
}
}
///
pub const fn get_progress_percent(&self) -> u8 {
self.progress.progress
}
pub(crate) fn set_progress<T>(
progress: &Arc<Mutex<Option<T>>>,
state: Option<T>,
) -> Result<()> {
let mut progress = progress.lock()?;
*progress = state;
Ok(())
}
/// spawn thread to listen to progress notifications coming in from blocking remote git method (fetch/push)
pub(crate) fn spawn_receiver_thread<
T: 'static + AsyncProgress,
>(
notification_type: AsyncGitNotification,
sender: Sender<AsyncGitNotification>,
receiver: Receiver<T>,
progress: Arc<Mutex<Option<T>>>,
) -> JoinHandle<()> {
thread::spawn(move || loop {
let incoming = receiver.recv();
match incoming {
Ok(update) => {
Self::set_progress(
&progress,
Some(update.clone()),
)
.expect("set progress failed");
sender
.send(notification_type)
.expect("Notification error");
thread::yield_now();
if update.is_done() {
break;
}
}
Err(e) => {
log::error!(
"remote progress receiver error: {e}",
);
break;
}
}
})
}
}
impl From<ProgressNotification> for RemoteProgress {
fn from(progress: ProgressNotification) -> Self {
match progress {
ProgressNotification::Packing {
stage,
current,
total,
} => match stage {
PackBuilderStage::AddingObjects => Self::new(
RemoteProgressState::PackingAddingObject,
current,
total,
),
PackBuilderStage::Deltafication => Self::new(
RemoteProgressState::PackingDeltafiction,
current,
total,
),
},
ProgressNotification::PushTransfer {
current,
total,
..
} => Self::new(
RemoteProgressState::Pushing,
current,
total,
),
ProgressNotification::Transfer {
objects,
total_objects,
..
} => Self::new(
RemoteProgressState::Transfer,
objects,
total_objects,
),
_ => Self::new(RemoteProgressState::Done, 1, 1),
}
}
}
================================================
FILE: asyncgit/src/remote_tags.rs
================================================
//!
use crate::{
asyncjob::{AsyncJob, RunParams},
error::Result,
sync::cred::BasicAuthCredential,
sync::{
remotes::{get_default_remote, tags_missing_remote},
RepoPath,
},
AsyncGitNotification,
};
use std::sync::{Arc, Mutex};
enum JobState {
Request(Option<BasicAuthCredential>),
Response(Result<Vec<String>>),
}
///
#[derive(Clone)]
pub struct AsyncRemoteTagsJob {
state: Arc<Mutex<Option<JobState>>>,
repo: RepoPath,
}
///
impl AsyncRemoteTagsJob {
///
pub fn new(
repo: RepoPath,
basic_credential: Option<BasicAuthCredential>,
) -> Self {
Self {
repo,
state: Arc::new(Mutex::new(Some(JobState::Request(
basic_credential,
)))),
}
}
///
pub fn result(&self) -> Option<Result<Vec<String>>> {
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 AsyncRemoteTagsJob {
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(basic_credential) => {
let result = get_default_remote(&self.repo)
.and_then(|remote| {
tags_missing_remote(
&self.repo,
&remote,
basic_credential,
)
});
JobState::Response(result)
}
JobState::Response(result) => {
JobState::Response(result)
}
});
}
Ok(AsyncGitNotification::RemoteTags)
}
}
================================================
FILE: asyncgit/src/revlog.rs
================================================
use crate::{
error::Result,
sync::{
gix_repo, repo, CommitId, LogWalker, LogWalkerWithoutFilter,
RepoPath, SharedCommitFilterFn,
},
AsyncGitNotification, Error,
};
use crossbeam_channel::Sender;
use scopetime::scope_time;
use std::{
sync::{
atomic::{AtomicBool, Ordering},
Arc, Mutex,
},
thread,
time::{Duration, Instant},
};
///
#[derive(PartialEq, Eq, Debug)]
pub enum FetchStatus {
/// previous fetch still running
Pending,
/// no change expected
NoChange,
/// new walk was started
Started,
}
///
pub struct AsyncLogResult {
///
pub commits: Vec<CommitId>,
///
pub duration: Duration,
}
///
pub struct AsyncLog {
current: Arc<Mutex<AsyncLogResult>>,
current_head: Arc<Mutex<Option<CommitId>>>,
sender: Sender<AsyncGitNotification>,
pending: Arc<AtomicBool>,
background: Arc<AtomicBool>,
filter: Option<SharedCommitFilterFn>,
partial_extract: AtomicBool,
repo: RepoPath,
}
static LIMIT_COUNT: usize = 3000;
static SLEEP_FOREGROUND: Duration = Duration::from_millis(2);
static SLEEP_BACKGROUND: Duration = Duration::from_secs(1);
impl AsyncLog {
///
pub fn new(
repo: RepoPath,
sender: &Sender<AsyncGitNotification>,
filter: Option<SharedCommitFilterFn>,
) -> Self {
Self {
repo,
current: Arc::new(Mutex::new(AsyncLogResult {
commits: Vec::new(),
duration: Duration::default(),
})),
current_head: Arc::new(Mutex::new(None)),
sender: sender.clone(),
pending: Arc::new(AtomicBool::new(false)),
background: Arc::new(AtomicBool::new(false)),
filter,
partial_extract: AtomicBool::new(false),
}
}
///
pub fn count(&self) -> Result<usize> {
Ok(self.current.lock()?.commits.len())
}
///
pub fn get_slice(
&self,
start_index: usize,
amount: usize,
) -> Result<Vec<CommitId>> {
if self.partial_extract.load(Ordering::Relaxed) {
return Err(Error::Generic(String::from("Faulty usage of AsyncLog: Cannot partially extract items and rely on get_items slice to still work!")));
}
let list = &self.current.lock()?.commits;
let list_len = list.len();
let min = start_index.min(list_len);
let max = min + amount;
let max = max.min(list_len);
Ok(list[min..max].to_vec())
}
///
pub fn get_items(&self) -> Result<Vec<CommitId>> {
if self.partial_extract.load(Ordering::Relaxed) {
return Err(Error::Generic(String::from("Faulty usage of AsyncLog: Cannot partially extract items and rely on get_items slice to still work!")));
}
let list = &self.current.lock()?.commits;
Ok(list.clone())
}
///
pub fn extract_items(&self) -> Result<Vec<CommitId>> {
self.partial_extract.store(true, Ordering::Relaxed);
let list = &mut self.current.lock()?.commits;
let result = list.clone();
list.clear();
Ok(result)
}
///
pub fn get_last_duration(&self) -> Result<Duration> {
Ok(self.current.lock()?.duration)
}
///
pub fn is_pending(&self) -> bool {
self.pending.load(Ordering::Relaxed)
}
///
pub fn set_background(&self) {
self.background.store(true, Ordering::Relaxed);
}
///
fn current_head(&self) -> Result<Option<CommitId>> {
Ok(*self.current_head.lock()?)
}
///
fn head_changed(&self) -> Result<bool> {
if let Ok(head) = repo(&self.repo)?.head() {
return Ok(
head.target() != self.current_head()?.map(Into::into)
);
}
Ok(false)
}
///
pub fn fetch(&self) -> Result<FetchStatus> {
self.background.store(false, Ordering::Relaxed);
if self.is_pending() {
return Ok(FetchStatus::Pending);
}
if !self.head_changed()? {
return Ok(FetchStatus::NoChange);
}
self.pending.store(true, Ordering::Relaxed);
self.clear()?;
let arc_current = Arc::clone(&self.current);
let sender = self.sender.clone();
let arc_pending = Arc::clone(&self.pending);
let arc_background = Arc::clone(&self.background);
let filter = self.filter.clone();
let repo_path = self.repo.clone();
if let Ok(head) = repo(&self.repo)?.head() {
*self.current_head.lock()? =
head.target().map(CommitId::new);
}
rayon_core::spawn(move || {
scope_time!("async::revlog");
Self::fetch_helper(
&repo_path,
&arc_current,
&arc_background,
&sender,
filter,
)
.expect("failed to fetch");
arc_pending.store(false, Ordering::Relaxed);
Self::notify(&sender);
});
Ok(FetchStatus::Started)
}
fn fetch_helper(
repo_path: &RepoPath,
arc_current: &Arc<Mutex<AsyncLogResult>>,
arc_background: &Arc<AtomicBool>,
sender: &Sender<AsyncGitNotification>,
filter: Option<SharedCommitFilterFn>,
) -> Result<()> {
filter.map_or_else(
|| {
Self::fetch_helper_without_filter(
repo_path,
arc_current,
arc_background,
sender,
)
},
|filter| {
Self::fetch_helper_with_filter(
repo_path,
arc_current,
arc_background,
sender,
filter,
)
},
)
}
fn fetch_helper_with_filter(
repo_path: &RepoPath,
arc_current: &Arc<Mutex<AsyncLogResult>>,
arc_background: &Arc<AtomicBool>,
sender: &Sender<AsyncGitNotification>,
filter: SharedCommitFilterFn,
) -> Result<()> {
let start_time = Instant::now();
let mut entries = vec![CommitId::default(); LIMIT_COUNT];
entries.resize(0, CommitId::default());
let r = repo(repo_path)?;
let mut walker =
LogWalker::new(&r, LIMIT_COUNT)?.filter(Some(filter));
loop {
entries.clear();
let read = walker.read(&mut entries)?;
let mut current = arc_current.lock()?;
current.commits.extend(entries.iter());
current.duration = start_time.elapsed();
if read == 0 {
break;
}
Self::notify(sender);
let sleep_duration =
if arc_background.load(Ordering::Relaxed) {
SLEEP_BACKGROUND
} else {
SLEEP_FOREGROUND
};
thread::sleep(sleep_duration);
}
log::trace!("revlog visited: {}", walker.visited());
Ok(())
}
fn fetch_helper_without_filter(
repo_path: &RepoPath,
arc_current: &Arc<Mutex<AsyncLogResult>>,
arc_background: &Arc<AtomicBool>,
sender: &Sender<AsyncGitNotification>,
) -> Result<()> {
let start_time = Instant::now();
let mut entries = vec![CommitId::default(); LIMIT_COUNT];
entries.resize(0, CommitId::default());
let mut repo: gix::Repository = gix_repo(repo_path)?;
let mut walker =
LogWalkerWithoutFilter::new(&mut repo, LIMIT_COUNT)?;
loop {
entries.clear();
let read = walker.read(&mut entries)?;
let mut current = arc_current.lock()?;
current.commits.extend(entries.iter());
current.duration = start_time.elapsed();
if read == 0 {
break;
}
Self::notify(sender);
let sleep_duration =
if arc_background.load(Ordering::Relaxed) {
SLEEP_BACKGROUND
} else {
SLEEP_FOREGROUND
};
thread::sleep(sleep_duration);
}
log::trace!("revlog visited: {}", walker.visited());
Ok(())
}
fn clear(&self) -> Result<()> {
self.current.lock()?.commits.clear();
*self.current_head.lock()? = None;
self.partial_extract.store(false, Ordering::Relaxed);
Ok(())
}
fn notify(sender: &Sender<AsyncGitNotification>) {
sender
.send(AsyncGitNotification::Log)
.expect("error sending");
}
}
#[cfg(test)]
mod tests {
use std::sync::atomic::AtomicBool;
use std::sync::{Arc, Mutex};
use std::time::Duration;
use crossbeam_channel::unbounded;
use serial_test::serial;
use tempfile::TempDir;
use crate::sync::tests::{debug_cmd_print, repo_init};
use crate::sync::RepoPath;
use crate::AsyncLog;
use super::AsyncLogResult;
#[test]
#[serial]
fn test_smoke_in_subdir() {
let (_td, repo) = repo_init().unwrap();
let root = repo.path().parent().unwrap();
let repo_path: RepoPath =
root.as_os_str().to_str().unwrap().into();
let (tx_git, _rx_git) = unbounded();
debug_cmd_print(&repo_path, "mkdir subdir");
let subdir = repo.path().parent().unwrap().join("subdir");
let subdir_path: RepoPath =
subdir.as_os_str().to_str().unwrap().into();
let arc_current = Arc::new(Mutex::new(AsyncLogResult {
commits: Vec::new(),
duration: Duration::default(),
}));
let arc_background = Arc::new(AtomicBool::new(false));
let result = AsyncLog::fetch_helper_without_filter(
&subdir_path,
&arc_current,
&arc_background,
&tx_git,
);
assert_eq!(result.unwrap(), ());
}
#[test]
#[serial]
fn test_env_variables() {
let (_td, repo) = repo_init().unwrap();
let git_dir = repo.path();
let (tx_git, _rx_git) = unbounded();
let empty_dir = TempDir::new().unwrap();
let empty_path: RepoPath =
empty_dir.path().to_str().unwrap().into();
let arc_current = Arc::new(Mutex::new(AsyncLogResult {
commits: Vec::new(),
duration: Duration::default(),
}));
let arc_background = Arc::new(AtomicBool::new(false));
std::env::set_var("GIT_DIR", git_dir);
let result = AsyncLog::fetch_helper_without_filter(
// We pass an empty path, thus testing whether `GIT_DIR`, set above, is taken into account.
&empty_path,
&arc_current,
&arc_background,
&tx_git,
);
std::env::remove_var("GIT_DIR");
assert_eq!(result.unwrap(), ());
}
}
================================================
FILE: asyncgit/src/status.rs
================================================
use crate::{
error::Result,
hash,
sync::{
self, status::StatusType, RepoPath, ShowUntrackedFilesConfig,
},
AsyncGitNotification, StatusItem,
};
use crossbeam_channel::Sender;
use std::{
hash::Hash,
sync::{
atomic::{AtomicU64, AtomicUsize, Ordering},
Arc, Mutex,
},
};
#[derive(Default, Hash, Clone)]
pub struct Status {
pub items: Vec<StatusItem>,
}
///
#[derive(Default, Hash, Copy, Clone, PartialEq, Eq)]
pub struct StatusParams {
status_type: StatusType,
config: Option<ShowUntrackedFilesConfig>,
}
impl StatusParams {
///
pub const fn new(
status_type: StatusType,
config: Option<ShowUntrackedFilesConfig>,
) -> Self {
Self {
status_type,
config,
}
}
}
struct Request<R, A>(R, Option<A>);
///
pub struct AsyncStatus {
current: Arc<Mutex<Request<u64, Status>>>,
last: Arc<Mutex<Status>>,
sender: Sender<AsyncGitNotification>,
pending: Arc<AtomicUsize>,
repo: RepoPath,
/// Counter that increments after each completed fetch.
generation: Arc<AtomicU64>,
}
impl AsyncStatus {
///
pub fn new(
repo: RepoPath,
sender: Sender<AsyncGitNotification>,
) -> Self {
Self {
repo,
current: Arc::new(Mutex::new(Request(0, None))),
last: Arc::new(Mutex::new(Status::default())),
sender,
pending: Arc::new(AtomicUsize::new(0)),
generation: Arc::new(AtomicU64::new(0)),
}
}
///
pub fn last(&self) -> Result<Status> {
let last = self.last.lock()?;
Ok(last.clone())
}
///
pub fn is_pending(&self) -> bool {
self.pending.load(Ordering::Relaxed) > 0
}
///
pub fn fetch(
&self,
params: &StatusParams,
) -> Result<Option<Status>> {
if self.is_pending() {
log::trace!("request blocked, still pending");
return Ok(None);
}
let generation = self.generation.load(Ordering::Relaxed);
let hash_request = hash(&(params, generation));
log::trace!(
"request: [hash: {}] (type: {:?}, gen: {})",
hash_request,
params.status_type,
generation,
);
{
let mut current = self.current.lock()?;
if current.0 == hash_request {
return Ok(current.1.clone());
}
current.0 = hash_request;
current.1 = None;
}
let arc_current = Arc::clone(&self.current);
let arc_last = Arc::clone(&self.last);
let arc_generation = Arc::clone(&self.generation);
let sender = self.sender.clone();
let arc_pending = Arc::clone(&self.pending);
let status_type = params.status_type;
let config = params.config;
let repo = self.repo.clone();
self.pending.fetch_add(1, Ordering::Relaxed);
rayon_core::spawn(move || {
if let Err(e) = Self::fetch_helper(
&repo,
status_type,
config,
hash_request,
&arc_current,
&arc_last,
) {
log::error!("fetch_helper: {e}");
}
// Increment generation to invalidate cache for next request
arc_generation.fetch_add(1, Ordering::Relaxed);
arc_pending.fetch_sub(1, Ordering::Relaxed);
sender
.send(AsyncGitNotification::Status)
.expect("error sending status");
});
Ok(None)
}
fn fetch_helper(
repo: &RepoPath,
status_type: StatusType,
config: Option<ShowUntrackedFilesConfig>,
hash_request: u64,
arc_current: &Arc<Mutex<Request<u64, Status>>>,
arc_last: &Arc<Mutex<Status>>,
) -> Result<()> {
let res = Self::get_status(repo, status_type, config)?;
log::trace!(
"status fetched: {hash_request} (type: {status_type:?})",
);
{
let mut current = arc_current.lock()?;
if current.0 == hash_request {
current.1 = Some(res.clone());
}
}
{
let mut last = arc_last.lock()?;
*last = res;
}
Ok(())
}
fn get_status(
repo: &RepoPath,
status_type: StatusType,
config: Option<ShowUntrackedFilesConfig>,
) -> Result<Status> {
Ok(Status {
items: sync::status::get_status(
repo,
status_type,
config,
)?,
})
}
}
================================================
FILE: asyncgit/src/sync/blame.rs
================================================
//! Sync git API for fetching a file blame
use super::{utils, CommitId, RepoPath};
use crate::{
error::{Error, Result},
sync::{get_commits_info, repository::repo},
};
use git2::BlameOptions;
use scopetime::scope_time;
use std::collections::{HashMap, HashSet};
use std::io::{BufRead, BufReader};
use std::path::Path;
/// A `BlameHunk` contains all the information that will be shown to the user.
#[derive(Clone, Hash, Debug, PartialEq, Eq)]
pub struct BlameHunk {
///
pub commit_id: CommitId,
///
pub author: String,
///
pub time: i64,
/// `git2::BlameHunk::final_start_line` returns 1-based indices, but
/// `start_line` is 0-based because the `Vec` storing the lines starts at
/// index 0.
pub start_line: usize,
///
pub end_line: usize,
}
/// A `BlameFile` represents a collection of lines. This is targeted at how the
/// data will be used by the UI.
#[derive(Clone, Debug)]
pub struct FileBlame {
///
pub commit_id: CommitId,
///
pub path: String,
///
pub lines: Vec<(Option<BlameHunk>, String)>,
}
/// fixup `\` windows path separators to git compatible `/`
fn fixup_windows_path(path: &str) -> String {
#[cfg(windows)]
{
path.replace('\\', "/")
}
#[cfg(not(windows))]
{
path.to_string()
}
}
///
pub fn blame_file(
repo_path: &RepoPath,
file_path: &str,
commit_id: Option<CommitId>,
) -> Result<FileBlame> {
scope_time!("blame_file");
let repo = repo(repo_path)?;
let commit_id = if let Some(commit_id) = commit_id {
commit_id
} else {
utils::get_head_repo(&repo)?
};
let spec =
format!("{}:{}", commit_id, fixup_windows_path(file_path));
let object = repo.revparse_single(&spec)?;
let blob = repo.find_blob(object.id())?;
if blob.is_binary() {
return Err(Error::NoBlameOnBinaryFile);
}
let mut opts = BlameOptions::new();
opts.newest_commit(commit_id.into());
let blame =
repo.blame_file(Path::new(file_path), Some(&mut opts))?;
let reader = BufReader::new(blob.content());
let unique_commit_ids: HashSet<_> = blame
.iter()
.map(|hunk| CommitId::new(hunk.final_commit_id()))
.collect();
let mut commit_ids = Vec::with_capacity(unique_commit_ids.len());
commit_ids.extend(unique_commit_ids);
let commit_infos = get_commits_info(repo_path, &commit_ids, 0)?;
let unique_commit_infos: HashMap<_, _> = commit_infos
.iter()
.map(|commit_info| (commit_info.id, commit_info))
.collect();
let lines: Vec<(Option<BlameHunk>, String)> = reader
.lines()
.enumerate()
.map(|(i, line)| {
// Line indices in a `FileBlame` are 1-based.
let corresponding_hunk = blame.get_line(i + 1);
if let Some(hunk) = corresponding_hunk {
let commit_id = CommitId::new(hunk.final_commit_id());
// Line indices in a `BlameHunk` are 1-based.
let start_line =
hunk.final_start_line().saturating_sub(1);
let end_line =
start_line.saturating_add(hunk.lines_in_hunk());
if let Some(commit_info) =
unique_commit_infos.get(&commit_id)
{
let hunk = BlameHunk {
commit_id,
author: commit_info.author.clone(),
time: commit_info.time,
start_line,
end_line,
};
return (
Some(hunk),
line.unwrap_or_else(|_| String::new()),
);
}
}
(None, line.unwrap_or_else(|_| String::new()))
})
.collect();
let file_blame = FileBlame {
commit_id,
path: file_path.into(),
lines,
};
Ok(file_blame)
}
#[cfg(test)]
mod tests {
use super::*;
use crate::{
error::Result,
sync::{commit, stage_add_file, tests::repo_init_empty},
};
use std::{
fs::{File, OpenOptions},
io::Write,
path::Path,
};
#[test]
fn test_blame() -> Result<()> {
let file_path = Path::new("foo");
let (_td, repo) = repo_init_empty()?;
let root = repo.path().parent().unwrap();
let repo_path: &RepoPath =
&root.as_os_str().to_str().unwrap().into();
assert!(blame_file(repo_path, "foo", None).is_err());
File::create(root.join(file_path))?.write_all(b"line 1\n")?;
stage_add_file(repo_path, file_path)?;
commit(repo_path, "first commit")?;
let blame = blame_file(repo_path, "foo", None)?;
assert!(matches!(
blame.lines.as_slice(),
[(
Some(BlameHunk {
author,
start_line: 0,
end_line: 1,
..
}),
line
)] if author == "name" && line == "line 1"
));
let mut file = OpenOptions::new()
.append(true)
.open(root.join(file_path))?;
file.write(b"line 2\n")?;
stage_add_file(repo_path, file_path)?;
commit(repo_path, "second commit")?;
let blame = blame_file(repo_path, "foo", None)?;
assert!(matches!(
blame.lines.as_slice(),
[
(
Some(BlameHunk {
start_line: 0,
end_line: 1,
..
}),
first_line
),
(
Some(BlameHunk {
author,
start_line: 1,
end_line: 2,
..
}),
second_line
)
] if author == "name" && first_line == "line 1" && second_line == "line 2"
));
file.write(b"line 3\n")?;
let blame = blame_file(repo_path, "foo", None)?;
assert_eq!(blame.lines.len(), 2);
stage_add_file(repo_path, file_path)?;
commit(repo_path, "third commit")?;
let blame = blame_file(repo_path, "foo", None)?;
assert_eq!(blame.lines.len(), 3);
Ok(())
}
#[test]
fn test_blame_windows_path_dividers() {
let file_path = Path::new("bar\\foo");
let (_td, repo) = repo_init_empty().unwrap();
let root = repo.path().parent().unwrap();
let repo_path: &RepoPath =
&root.as_os_str().to_str().unwrap().into();
std::fs::create_dir(root.join("bar")).unwrap();
File::create(root.join(file_path))
.unwrap()
.write_all(b"line 1\n")
.unwrap();
stage_add_file(repo_path, file_path).unwrap();
commit(repo_path, "first commit").unwrap();
assert!(blame_file(repo_path, "bar\\foo", None).is_ok());
}
}
================================================
FILE: asyncgit/src/sync/branch/merge_commit.rs
================================================
//! merging from upstream
use super::BranchType;
use crate::{
error::{Error, Result},
sync::{merge_msg, repository::repo, CommitId, RepoPath},
};
use git2::Commit;
use scopetime::scope_time;
/// merge upstream using a merge commit if we did not create conflicts.
/// if we did not create conflicts we create a merge commit and return the commit id.
/// Otherwise we return `None`
pub fn merge_upstream_commit(
repo_path: &RepoPath,
branch_name: &str,
) -> Result<Option<CommitId>> {
scope_time!("merge_upstream_commit");
let repo = repo(repo_path)?;
let branch = repo.find_branch(branch_name, BranchType::Local)?;
let upstream = branch.upstream()?;
let upstream_commit = upstream.get().peel_to_commit()?;
let annotated_upstream = repo
.reference_to_annotated_commit(&upstream.into_reference())?;
l
gitextract_1za1en4i/
├── .cargo/
│ └── config.toml
├── .clippy.toml
├── .editorconfig
├── .github/
│ ├── FUNDING.yml
│ ├── ISSUE_TEMPLATE/
│ │ ├── bug_report.md
│ │ └── feature_request.md
│ ├── PULL_REQUEST_TEMPLATE.md
│ ├── dependabot.yml
│ ├── stale.yml
│ └── workflows/
│ ├── brew.yml
│ ├── cd.yml
│ ├── ci.yml
│ └── nightly.yml
├── .gitignore
├── .vscode/
│ ├── launch.json
│ └── settings.json
├── CHANGELOG.md
├── CONTRIBUTING.md
├── Cargo.toml
├── FAQ.md
├── KEY_CONFIG.md
├── LICENSE.md
├── Makefile
├── NIGHTLIES.md
├── README.md
├── THEMES.md
├── assets/
│ ├── expandable-commands.drawio
│ ├── log-commit-info.drawio
│ ├── options.drawio
│ └── stashing.drawio
├── asyncgit/
│ ├── Cargo.toml
│ ├── README.md
│ └── src/
│ ├── asyncjob/
│ │ └── mod.rs
│ ├── blame.rs
│ ├── branches.rs
│ ├── cached/
│ │ ├── branchname.rs
│ │ └── mod.rs
│ ├── commit_files.rs
│ ├── diff.rs
│ ├── error.rs
│ ├── fetch_job.rs
│ ├── filter_commits.rs
│ ├── lib.rs
│ ├── progress.rs
│ ├── pull.rs
│ ├── push.rs
│ ├── push_tags.rs
│ ├── remote_progress.rs
│ ├── remote_tags.rs
│ ├── revlog.rs
│ ├── status.rs
│ ├── sync/
│ │ ├── blame.rs
│ │ ├── branch/
│ │ │ ├── merge_commit.rs
│ │ │ ├── merge_ff.rs
│ │ │ ├── merge_rebase.rs
│ │ │ ├── mod.rs
│ │ │ └── rename.rs
│ │ ├── commit.rs
│ │ ├── commit_details.rs
│ │ ├── commit_files.rs
│ │ ├── commit_filter.rs
│ │ ├── commit_revert.rs
│ │ ├── commits_info.rs
│ │ ├── config.rs
│ │ ├── cred.rs
│ │ ├── diff.rs
│ │ ├── hooks.rs
│ │ ├── hunks.rs
│ │ ├── ignore.rs
│ │ ├── logwalker.rs
│ │ ├── merge.rs
│ │ ├── mod.rs
│ │ ├── patches.rs
│ │ ├── rebase.rs
│ │ ├── remotes/
│ │ │ ├── callbacks.rs
│ │ │ ├── mod.rs
│ │ │ ├── push.rs
│ │ │ └── tags.rs
│ │ ├── repository.rs
│ │ ├── reset.rs
│ │ ├── reword.rs
│ │ ├── sign.rs
│ │ ├── staging/
│ │ │ ├── discard_tracked.rs
│ │ │ ├── mod.rs
│ │ │ └── stage_tracked.rs
│ │ ├── stash.rs
│ │ ├── state.rs
│ │ ├── status.rs
│ │ ├── submodules.rs
│ │ ├── tags.rs
│ │ ├── tree.rs
│ │ └── utils.rs
│ ├── tags.rs
│ └── treefiles.rs
├── build.rs
├── deny.toml
├── filetreelist/
│ ├── Cargo.toml
│ ├── README.md
│ └── src/
│ ├── error.rs
│ ├── filetree.rs
│ ├── filetreeitems.rs
│ ├── item.rs
│ ├── lib.rs
│ ├── tree_iter.rs
│ └── treeitems_iter.rs
├── git2-hooks/
│ ├── Cargo.toml
│ ├── README.md
│ └── src/
│ ├── error.rs
│ ├── hookspath.rs
│ └── lib.rs
├── git2-testing/
│ ├── Cargo.toml
│ ├── README.md
│ └── src/
│ └── lib.rs
├── invalidstring/
│ ├── Cargo.toml
│ ├── README.md
│ └── src/
│ └── lib.rs
├── rust-toolchain.toml
├── rustfmt.toml
├── scopetime/
│ ├── Cargo.toml
│ ├── README.md
│ └── src/
│ └── lib.rs
├── src/
│ ├── app.rs
│ ├── args.rs
│ ├── bug_report.rs
│ ├── clipboard.rs
│ ├── cmdbar.rs
│ ├── components/
│ │ ├── changes.rs
│ │ ├── command.rs
│ │ ├── commit_details/
│ │ │ ├── compare_details.rs
│ │ │ ├── details.rs
│ │ │ ├── mod.rs
│ │ │ └── style.rs
│ │ ├── commitlist.rs
│ │ ├── cred.rs
│ │ ├── diff.rs
│ │ ├── mod.rs
│ │ ├── revision_files.rs
│ │ ├── status_tree.rs
│ │ ├── syntax_text.rs
│ │ ├── textinput.rs
│ │ └── utils/
│ │ ├── emoji.rs
│ │ ├── filetree.rs
│ │ ├── logitems.rs
│ │ ├── mod.rs
│ │ ├── scroll_horizontal.rs
│ │ ├── scroll_vertical.rs
│ │ └── statustree.rs
│ ├── input.rs
│ ├── keys/
│ │ ├── key_config.rs
│ │ ├── key_list.rs
│ │ ├── mod.rs
│ │ └── symbols.rs
│ ├── main.rs
│ ├── notify_mutex.rs
│ ├── options.rs
│ ├── popup_stack.rs
│ ├── popups/
│ │ ├── blame_file.rs
│ │ ├── branchlist.rs
│ │ ├── checkout_option.rs
│ │ ├── commit.rs
│ │ ├── compare_commits.rs
│ │ ├── confirm.rs
│ │ ├── create_branch.rs
│ │ ├── create_remote.rs
│ │ ├── externaleditor.rs
│ │ ├── fetch.rs
│ │ ├── file_revlog.rs
│ │ ├── fuzzy_find.rs
│ │ ├── goto_line.rs
│ │ ├── help.rs
│ │ ├── inspect_commit.rs
│ │ ├── log_search.rs
│ │ ├── mod.rs
│ │ ├── msg.rs
│ │ ├── options.rs
│ │ ├── pull.rs
│ │ ├── push.rs
│ │ ├── push_tags.rs
│ │ ├── remotelist.rs
│ │ ├── rename_branch.rs
│ │ ├── rename_remote.rs
│ │ ├── reset.rs
│ │ ├── revision_files.rs
│ │ ├── stashmsg.rs
│ │ ├── submodules.rs
│ │ ├── tag_commit.rs
│ │ ├── taglist.rs
│ │ └── update_remote_url.rs
│ ├── queue.rs
│ ├── spinner.rs
│ ├── string_utils.rs
│ ├── strings.rs
│ ├── tabs/
│ │ ├── files.rs
│ │ ├── mod.rs
│ │ ├── revlog.rs
│ │ ├── stashing.rs
│ │ ├── stashlist.rs
│ │ └── status.rs
│ ├── ui/
│ │ ├── mod.rs
│ │ ├── reflow.rs
│ │ ├── scrollbar.rs
│ │ ├── scrolllist.rs
│ │ ├── stateful_paragraph.rs
│ │ ├── style.rs
│ │ └── syntax_text.rs
│ └── watcher.rs
├── typos.toml
├── vim_style_key_config.ron
└── wix/
├── License.rtf
├── Microsoft_VC142_CRT_x64.msm
└── main.wxs
SYMBOL INDEX (2506 symbols across 157 files)
FILE: asyncgit/src/asyncjob/mod.rs
type RunParams (line 10) | pub struct RunParams<
function send (line 26) | pub fn send(&self, notification: T) -> Result<()> {
function set_progress (line 32) | pub fn set_progress(&self, p: P) -> Result<bool> {
type AsyncJob (line 43) | pub trait AsyncJob: Send + Sync + Clone {
method run (line 54) | fn run(
method get_progress (line 61) | fn get_progress(&self) -> Self::Progress {
type Notification (line 197) | type Notification = TestNotification;
type Progress (line 198) | type Progress = ();
method run (line 200) | fn run(
type AsyncSingleJob (line 69) | pub struct AsyncSingleJob<J: AsyncJob> {
function new (line 79) | pub fn new(sender: Sender<J::Notification>) -> Self {
function is_pending (line 90) | pub fn is_pending(&self) -> bool {
function cancel (line 95) | pub fn cancel(&self) -> bool {
function take_last (line 107) | pub fn take_last(&self) -> Option<J> {
function spawn (line 114) | pub fn spawn(&self, task: J) -> bool {
function progress (line 120) | pub fn progress(&self) -> Option<J::Progress> {
function check_for_job (line 124) | fn check_for_job(&self) -> bool {
function run_job (line 143) | fn run_job(&self, mut task: J) -> Result<()> {
function schedule_next (line 165) | fn schedule_next(&self, task: J) {
function take_next (line 171) | fn take_next(&self) -> Option<J> {
type TestJob (line 188) | struct TestJob {
type TestNotification (line 194) | type TestNotification = ();
function test_overwrite (line 226) | fn test_overwrite() {
function wait_for_job (line 258) | fn wait_for_job(job: &AsyncSingleJob<TestJob>) {
function test_cancel (line 265) | fn test_cancel() {
FILE: asyncgit/src/blame.rs
type BlameParams (line 18) | pub struct BlameParams {
type Request (line 25) | struct Request<R, A>(R, Option<A>);
type LastResult (line 28) | struct LastResult<P, R> {
type AsyncBlame (line 34) | pub struct AsyncBlame {
method new (line 44) | pub fn new(
method last (line 58) | pub fn last(&self) -> Result<Option<(BlameParams, FileBlame)>> {
method refresh (line 67) | pub fn refresh(&self) -> Result<()> {
method is_pending (line 76) | pub fn is_pending(&self) -> bool {
method request (line 81) | pub fn request(
method get_blame_helper (line 139) | fn get_blame_helper(
method get_last_param (line 174) | fn get_last_param(&self) -> Result<Option<BlameParams>> {
method clear_current (line 182) | fn clear_current(&self) -> Result<()> {
FILE: asyncgit/src/branches.rs
type JobState (line 9) | enum JobState {
type AsyncBranchesJob (line 19) | pub struct AsyncBranchesJob {
method new (line 26) | pub fn new(repo: RepoPath, local_branches: bool) -> Self {
method result (line 36) | pub fn result(&self) -> Option<Result<Vec<BranchInfo>>> {
type Notification (line 51) | type Notification = AsyncGitNotification;
type Progress (line 52) | type Progress = ();
method run (line 54) | fn run(
FILE: asyncgit/src/cached/branchname.rs
type BranchName (line 8) | pub struct BranchName {
method new (line 15) | pub const fn new(repo: RepoPathRef) -> Self {
method lookup (line 23) | pub fn lookup(&mut self) -> Result<String> {
method last (line 38) | pub fn last(&self) -> Option<String> {
method fetch (line 42) | fn fetch(&mut self, head: Head) -> Result<String> {
FILE: asyncgit/src/commit_files.rs
type ResultType (line 12) | type ResultType = Vec<StatusItem>;
type Request (line 13) | struct Request<R, A>(R, A);
type CommitFilesParams (line 17) | pub struct CommitFilesParams {
method from (line 25) | fn from(id: CommitId) -> Self {
method from (line 31) | fn from((id, other): (CommitId, CommitId)) -> Self {
method from (line 40) | fn from(old_new: OldNew<CommitId>) -> Self {
type AsyncCommitFiles (line 49) | pub struct AsyncCommitFiles {
method new (line 59) | pub fn new(
method current (line 72) | pub fn current(
method is_pending (line 82) | pub fn is_pending(&self) -> bool {
method fetch (line 87) | pub fn fetch(&self, params: CommitFilesParams) -> Result<()> {
method fetch_helper (line 124) | fn fetch_helper(
FILE: asyncgit/src/diff.rs
type DiffType (line 21) | pub enum DiffType {
type DiffParams (line 34) | pub struct DiffParams {
type Request (line 43) | struct Request<R, A>(R, Option<A>);
type LastResult (line 46) | struct LastResult<P, R> {
type AsyncDiff (line 52) | pub struct AsyncDiff {
method new (line 62) | pub fn new(
method last (line 76) | pub fn last(&self) -> Result<Option<(DiffParams, FileDiff)>> {
method refresh (line 83) | pub fn refresh(&self) -> Result<()> {
method is_pending (line 92) | pub fn is_pending(&self) -> bool {
method request (line 97) | pub fn request(
method get_diff_helper (line 155) | fn get_diff_helper(
method get_last_param (line 211) | fn get_last_param(&self) -> Result<Option<DiffParams>> {
method clear_current (line 215) | fn clear_current(&self) -> Result<()> {
FILE: asyncgit/src/error.rs
type GixError (line 9) | pub enum GixError {
method from (line 205) | fn from(error: gix::discover::Error) -> Self {
method from (line 239) | fn from(error: gix::pathspec::init::Error) -> Self {
method from (line 281) | fn from(error: gix::status::Error) -> Self {
method from (line 293) | fn from(error: gix::status::iter::Error) -> Self {
method from (line 305) | fn from(error: gix::status::into_iter::Error) -> Self {
method from (line 317) | fn from(error: gix::status::index_worktree::Error) -> Self {
method from (line 329) | fn from(error: gix::status::tree_index::Error) -> Self {
method from (line 341) | fn from(error: gix::worktree::open_index::Error) -> Self {
type Error (line 83) | pub enum Error {
method from (line 193) | fn from(error: std::sync::PoisonError<T>) -> Self {
method from (line 199) | fn from(error: crossbeam_channel::SendError<T>) -> Self {
method from (line 211) | fn from(error: gix::discover::Error) -> Self {
method from (line 217) | fn from(error: gix::head::peel::to_commit::Error) -> Self {
method from (line 225) | fn from(
method from (line 233) | fn from(error: gix::objs::decode::Error) -> Self {
method from (line 245) | fn from(error: gix::pathspec::init::Error) -> Self {
method from (line 251) | fn from(error: gix::reference::find::existing::Error) -> Self {
method from (line 257) | fn from(error: gix::reference::head_tree_id::Error) -> Self {
method from (line 263) | fn from(error: gix::reference::iter::Error) -> Self {
method from (line 269) | fn from(error: gix::reference::iter::init::Error) -> Self {
method from (line 275) | fn from(error: gix::revision::walk::Error) -> Self {
method from (line 287) | fn from(error: gix::status::Error) -> Self {
method from (line 299) | fn from(error: gix::status::iter::Error) -> Self {
method from (line 311) | fn from(error: gix::status::into_iter::Error) -> Self {
method from (line 323) | fn from(error: gix::status::index_worktree::Error) -> Self {
method from (line 335) | fn from(error: gix::status::tree_index::Error) -> Self {
method from (line 347) | fn from(error: gix::worktree::open_index::Error) -> Self {
type Result (line 190) | pub type Result<T> = std::result::Result<T, Error>;
FILE: asyncgit/src/fetch_job.rs
type JobState (line 13) | enum JobState {
type AsyncFetchJob (line 20) | pub struct AsyncFetchJob {
method new (line 28) | pub fn new(
type Notification (line 42) | type Notification = AsyncGitNotification;
type Progress (line 43) | type Progress = ProgressPercent;
method run (line 45) | fn run(
FILE: asyncgit/src/filter_commits.rs
type CommitFilterResult (line 21) | pub struct CommitFilterResult {
type JobState (line 28) | enum JobState {
type AsyncCommitFilterJob (line 38) | pub struct AsyncCommitFilterJob {
method new (line 47) | pub fn new(
method result (line 64) | pub fn result(&self) -> Option<Result<CommitFilterResult>> {
method run_request (line 77) | fn run_request(
method filter_commits (line 93) | fn filter_commits(
method update_progress (line 161) | fn update_progress(
type Notification (line 180) | type Notification = AsyncGitNotification;
type Progress (line 181) | type Progress = ProgressPercent;
method run (line 183) | fn run(
FILE: asyncgit/src/lib.rs
type AsyncGitNotification (line 98) | pub enum AsyncGitNotification {
function hash (line 134) | pub fn hash<T: Hash + ?Sized>(v: &T) -> u64 {
function register_tracing_logging (line 142) | pub fn register_tracing_logging() -> bool {
function register_tracing_logging (line 151) | pub fn register_tracing_logging() -> bool {
FILE: asyncgit/src/progress.rs
type ProgressPercent (line 8) | pub struct ProgressPercent {
method new (line 15) | pub fn new(current: usize, total: usize) -> Self {
method empty (line 22) | pub const fn empty() -> Self {
method full (line 26) | pub const fn full() -> Self {
function test_progress_zero_total (line 36) | fn test_progress_zero_total() {
function test_progress_zero_all (line 43) | fn test_progress_zero_all() {
function test_progress_rounding (line 49) | fn test_progress_rounding() {
FILE: asyncgit/src/pull.rs
type FetchRequest (line 18) | pub struct FetchRequest {
type FetchState (line 29) | struct FetchState {}
type AsyncPull (line 32) | pub struct AsyncPull {
method new (line 42) | pub fn new(
method is_pending (line 56) | pub fn is_pending(&self) -> Result<bool> {
method last_result (line 62) | pub fn last_result(&self) -> Result<Option<(usize, String)>> {
method progress (line 68) | pub fn progress(&self) -> Result<Option<RemoteProgress>> {
method request (line 74) | pub fn request(&self, params: FetchRequest) -> Result<()> {
method set_request (line 125) | fn set_request(&self, _params: &FetchRequest) -> Result<()> {
method clear_request (line 137) | fn clear_request(
method set_result (line 147) | fn set_result(
FILE: asyncgit/src/push.rs
type PushRequest (line 19) | pub struct PushRequest {
type PushState (line 36) | struct PushState {}
type AsyncPush (line 39) | pub struct AsyncPush {
method new (line 49) | pub fn new(
method is_pending (line 63) | pub fn is_pending(&self) -> Result<bool> {
method last_result (line 69) | pub fn last_result(&self) -> Result<Option<String>> {
method progress (line 75) | pub fn progress(&self) -> Result<Option<RemoteProgress>> {
method request (line 81) | pub fn request(&self, params: PushRequest) -> Result<()> {
method set_request (line 136) | fn set_request(&self, _params: &PushRequest) -> Result<()> {
method clear_request (line 148) | fn clear_request(
method set_result (line 158) | fn set_result(
FILE: asyncgit/src/push_tags.rs
type PushTagsRequest (line 18) | pub struct PushTagsRequest {
type PushState (line 27) | struct PushState {}
type AsyncPushTags (line 30) | pub struct AsyncPushTags {
method new (line 40) | pub fn new(
method is_pending (line 54) | pub fn is_pending(&self) -> Result<bool> {
method last_result (line 60) | pub fn last_result(&self) -> Result<Option<String>> {
method progress (line 66) | pub fn progress(&self) -> Result<Option<PushTagsProgress>> {
method request (line 72) | pub fn request(&self, params: PushTagsRequest) -> Result<()> {
method set_request (line 119) | fn set_request(&self, _params: &PushTagsRequest) -> Result<()> {
method clear_request (line 131) | fn clear_request(
method set_result (line 141) | fn set_result(
FILE: asyncgit/src/remote_progress.rs
type RemoteProgressState (line 18) | pub enum RemoteProgressState {
type RemoteProgress (line 33) | pub struct RemoteProgress {
method new (line 42) | pub fn new(
method get_progress_percent (line 54) | pub const fn get_progress_percent(&self) -> u8 {
method set_progress (line 58) | pub(crate) fn set_progress<T>(
method spawn_receiver_thread (line 70) | pub(crate) fn spawn_receiver_thread<
method from (line 109) | fn from(progress: ProgressNotification) -> Self {
FILE: asyncgit/src/remote_tags.rs
type JobState (line 16) | enum JobState {
type AsyncRemoteTagsJob (line 23) | pub struct AsyncRemoteTagsJob {
method new (line 31) | pub fn new(
method result (line 44) | pub fn result(&self) -> Option<Result<Vec<String>>> {
type Notification (line 59) | type Notification = AsyncGitNotification;
type Progress (line 60) | type Progress = ();
method run (line 62) | fn run(
FILE: asyncgit/src/revlog.rs
type FetchStatus (line 22) | pub enum FetchStatus {
type AsyncLogResult (line 32) | pub struct AsyncLogResult {
type AsyncLog (line 39) | pub struct AsyncLog {
method new (line 56) | pub fn new(
method count (line 77) | pub fn count(&self) -> Result<usize> {
method get_slice (line 82) | pub fn get_slice(
method get_items (line 100) | pub fn get_items(&self) -> Result<Vec<CommitId>> {
method extract_items (line 110) | pub fn extract_items(&self) -> Result<Vec<CommitId>> {
method get_last_duration (line 119) | pub fn get_last_duration(&self) -> Result<Duration> {
method is_pending (line 124) | pub fn is_pending(&self) -> bool {
method set_background (line 129) | pub fn set_background(&self) {
method current_head (line 134) | fn current_head(&self) -> Result<Option<CommitId>> {
method head_changed (line 139) | fn head_changed(&self) -> Result<bool> {
method fetch (line 149) | pub fn fetch(&self) -> Result<FetchStatus> {
method fetch_helper (line 196) | fn fetch_helper(
method fetch_helper_with_filter (line 224) | fn fetch_helper_with_filter(
method fetch_helper_without_filter (line 268) | fn fetch_helper_without_filter(
method clear (line 311) | fn clear(&self) -> Result<()> {
method notify (line 318) | fn notify(sender: &Sender<AsyncGitNotification>) {
function test_smoke_in_subdir (line 343) | fn test_smoke_in_subdir() {
function test_env_variables (line 375) | fn test_env_variables() {
FILE: asyncgit/src/status.rs
type Status (line 19) | pub struct Status {
type StatusParams (line 25) | pub struct StatusParams {
method new (line 32) | pub const fn new(
type Request (line 43) | struct Request<R, A>(R, Option<A>);
type AsyncStatus (line 46) | pub struct AsyncStatus {
method new (line 58) | pub fn new(
method last (line 73) | pub fn last(&self) -> Result<Status> {
method is_pending (line 79) | pub fn is_pending(&self) -> bool {
method fetch (line 84) | pub fn fetch(
method fetch_helper (line 149) | fn fetch_helper(
method get_status (line 177) | fn get_status(
FILE: asyncgit/src/sync/blame.rs
type BlameHunk (line 16) | pub struct BlameHunk {
type FileBlame (line 34) | pub struct FileBlame {
function fixup_windows_path (line 44) | fn fixup_windows_path(path: &str) -> String {
function blame_file (line 57) | pub fn blame_file(
function test_blame (line 163) | fn test_blame() -> Result<()> {
function test_blame_windows_path_dividers (line 243) | fn test_blame_windows_path_dividers() {
FILE: asyncgit/src/sync/branch/merge_commit.rs
function merge_upstream_commit (line 14) | pub fn merge_upstream_commit(
function commit_merge_with_head (line 64) | pub(crate) fn commit_merge_with_head(
function test_merge_normal (line 111) | fn test_merge_normal() {
function test_merge_normal_non_ff (line 208) | fn test_merge_normal_non_ff() {
FILE: asyncgit/src/sync/branch/merge_ff.rs
function branch_merge_upstream_fastforward (line 11) | pub fn branch_merge_upstream_fastforward(
function test_merge_fastforward (line 64) | fn test_merge_fastforward() {
FILE: asyncgit/src/sync/branch/merge_rebase.rs
function merge_upstream_rebase (line 14) | pub fn merge_upstream_rebase(
function get_commit_msgs (line 50) | fn get_commit_msgs(r: &Repository) -> Vec<String> {
function test_merge_normal (line 64) | fn test_merge_normal() {
function test_merge_multiple (line 178) | fn test_merge_multiple() {
function test_merge_conflict (line 277) | fn test_merge_conflict() {
FILE: asyncgit/src/sync/branch/mod.rs
function get_branch_name (line 22) | pub(crate) fn get_branch_name(
function get_branch_name_repo (line 31) | pub(crate) fn get_branch_name_repo(
type LocalBranch (line 49) | pub struct LocalBranch {
type UpstreamBranch (line 62) | pub struct UpstreamBranch {
type RemoteBranch (line 69) | pub struct RemoteBranch {
type BranchDetails (line 76) | pub enum BranchDetails {
type BranchInfo (line 85) | pub struct BranchInfo {
method local_details (line 100) | pub const fn local_details(&self) -> Option<&LocalBranch> {
method is_local (line 109) | pub const fn is_local(&self) -> bool {
method remote_details (line 890) | const fn remote_details(&self) -> Option<&RemoteBranch> {
function validate_branch_name (line 115) | pub fn validate_branch_name(name: &str) -> Result<bool> {
function get_branches_info (line 125) | pub fn get_branches_info(
type BranchCompare (line 209) | pub struct BranchCompare {
function branch_set_upstream_after_push (line 217) | pub(crate) fn branch_set_upstream_after_push(
function get_branch_remote (line 236) | pub fn get_branch_remote(
function get_branch_upstream_merge (line 255) | pub fn get_branch_upstream_merge(
function config_is_pull_rebase (line 271) | pub fn config_is_pull_rebase(repo_path: &RepoPath) -> Result<bool> {
function branch_compare_upstream (line 285) | pub fn branch_compare_upstream(
function checkout_branch (line 314) | pub fn checkout_branch(
function checkout_commit (line 346) | pub fn checkout_commit(
function checkout_remote_branch (line 376) | pub fn checkout_remote_branch(
function delete_branch (line 419) | pub fn delete_branch(
function create_branch (line 436) | pub fn create_branch(
function test_smoke (line 461) | fn test_smoke() {
function test_empty_repo (line 474) | fn test_empty_repo() {
function test_smoke (line 493) | fn test_smoke() {
function test_smoke (line 514) | fn test_smoke() {
function test_smoke (line 541) | fn test_smoke() {
function test_multiple (line 558) | fn test_multiple() {
function clone_branch_commit_push (line 576) | fn clone_branch_commit_push(target: &str, branch_name: &str) {
function test_remotes_of_branches (line 596) | fn test_remotes_of_branches() {
function test_branch_remote_no_upstream (line 679) | fn test_branch_remote_no_upstream() {
function test_branch_remote_no_branch (line 692) | fn test_branch_remote_no_branch() {
function test_branch_no_upstream_merge_config (line 702) | fn test_branch_no_upstream_merge_config() {
function test_branch_with_upstream_merge_config (line 716) | fn test_branch_with_upstream_merge_config() {
function test_smoke (line 752) | fn test_smoke() {
function test_multiple (line 763) | fn test_multiple() {
function test_branch_with_slash_in_name (line 777) | fn test_branch_with_slash_in_name() {
function test_staged_new_file (line 788) | fn test_staged_new_file() {
function test_smoke (line 813) | fn test_smoke() {
function test_delete_branch (line 839) | fn test_delete_branch() {
function test_remote_branches (line 900) | fn test_remote_branches() {
function test_checkout_remote_branch (line 959) | fn test_checkout_remote_branch() {
function test_checkout_remote_branch_hierarchical (line 1025) | fn test_checkout_remote_branch_hierarchical() {
function test_has_tracking (line 1079) | fn test_has_tracking() {
FILE: asyncgit/src/sync/branch/rename.rs
function rename_branch (line 10) | pub fn rename_branch(
function test_rename_branch (line 32) | fn test_rename_branch() {
FILE: asyncgit/src/sync/commit.rs
function amend (line 14) | pub fn amend(
function signature_allow_undefined_name (line 60) | pub(crate) fn signature_allow_undefined_name(
function commit (line 84) | pub fn commit(repo_path: &RepoPath, msg: &str) -> Result<CommitId> {
function tag_commit (line 162) | pub fn tag_commit(
function commit_message_prettify (line 187) | pub fn commit_message_prettify(
function count_commits (line 217) | fn count_commits(repo: &Repository, max: usize) -> usize {
function test_commit (line 225) | fn test_commit() {
function test_commit_in_empty_repo (line 249) | fn test_commit_in_empty_repo() {
function test_amend (line 275) | fn test_amend() -> Result<()> {
function test_amend_with_different_user (line 313) | fn test_amend_with_different_user() {
function test_tag (line 362) | fn test_tag() -> Result<()> {
function test_tag_with_message (line 401) | fn test_tag_with_message() -> Result<()> {
function test_empty_email (line 435) | fn test_empty_email() -> Result<()> {
function test_empty_name (line 471) | fn test_empty_name() -> Result<()> {
function test_empty_comment_char (line 513) | fn test_empty_comment_char() -> Result<()> {
function test_with_comment_char (line 530) | fn test_with_comment_char() -> Result<()> {
FILE: asyncgit/src/sync/commit_details.rs
type CommitSignature (line 8) | pub struct CommitSignature {
method from (line 19) | pub fn from(s: &Signature<'_>) -> Self {
type CommitMessage (line 31) | pub struct CommitMessage {
method from (line 40) | pub fn from(s: &str) -> Self {
method combine (line 61) | pub fn combine(self) -> String {
type CommitDetails (line 72) | pub struct CommitDetails {
method short_hash (line 85) | pub fn short_hash(&self) -> &str {
function get_author_of_commit (line 91) | pub fn get_author_of_commit<'a>(
function get_committer_of_commit (line 109) | pub fn get_committer_of_commit<'a>(
function get_commit_details (line 127) | pub fn get_commit_details(
function test_msg_invalid_utf8 (line 176) | fn test_msg_invalid_utf8() -> Result<()> {
function test_msg_linefeeds (line 202) | fn test_msg_linefeeds() -> Result<()> {
function test_commit_message_combine (line 212) | fn test_commit_message_combine() -> Result<()> {
FILE: asyncgit/src/sync/commit_files.rs
type OldNew (line 15) | pub struct OldNew<T> {
function sort_commits (line 23) | pub fn sort_commits(
function get_commit_files (line 44) | pub fn get_commit_files(
function get_compare_commits_diff (line 91) | pub fn get_compare_commits_diff(
function get_commit_diff (line 128) | pub(crate) fn get_commit_diff<'a>(
function test_smoke (line 197) | fn test_smoke() -> Result<()> {
function test_stashed_untracked (line 220) | fn test_stashed_untracked() -> Result<()> {
function test_stashed_untracked_and_modified (line 241) | fn test_stashed_untracked_and_modified() -> Result<()> {
FILE: asyncgit/src/sync/commit_filter.rs
type SharedCommitFilterFn (line 12) | pub type SharedCommitFilterFn = Arc<
function diff_contains_file (line 17) | pub fn diff_contains_file(file_path: String) -> SharedCommitFilterFn {
method default (line 59) | fn default() -> Self {
method default (line 76) | fn default() -> Self {
type LogFilterSearchOptions (line 83) | pub struct LogFilterSearchOptions {
type LogFilterSearch (line 94) | pub struct LogFilterSearch {
method new (line 103) | pub fn new(options: LogFilterSearchOptions) -> Self {
method match_diff (line 115) | fn match_diff(&self, diff: &Diff<'_>) -> bool {
method match_text (line 135) | pub fn match_text(&self, text: &str) -> bool {
function filter_commit_by_search (line 158) | pub fn filter_commit_by_search(
FILE: asyncgit/src/sync/commit_revert.rs
constant GIT_REVERT_HEAD_FILE (line 8) | const GIT_REVERT_HEAD_FILE: &str = "REVERT_HEAD";
function revert_commit (line 11) | pub fn revert_commit(
function revert_head (line 27) | pub fn revert_head(repo_path: &RepoPath) -> Result<CommitId> {
function commit_revert (line 40) | pub fn commit_revert(
FILE: asyncgit/src/sync/commits_info.rs
type CommitId (line 19) | pub struct CommitId(Oid);
method new (line 29) | pub const fn new(id: Oid) -> Self {
method get_oid (line 34) | pub(crate) const fn get_oid(self) -> Oid {
method get_short_string (line 39) | pub fn get_short_string(&self) -> String {
method from_revision (line 44) | pub fn from_revision(
method from_str_unchecked (line 58) | pub fn from_str_unchecked(commit_id_str: &str) -> Result<Self> {
method from (line 85) | fn from(id: Oid) -> Self {
method from (line 91) | fn from(object_id: gix::ObjectId) -> Self {
method from (line 100) | fn from(commit: gix::Commit<'_>) -> Self {
method default (line 22) | fn default() -> Self {
method fmt (line 70) | fn fmt(
method from (line 79) | fn from(id: CommitId) -> Self {
function from (line 109) | fn from(id: CommitId) -> Self {
type CommitInfo (line 116) | pub struct CommitInfo {
function get_commits_info (line 128) | pub fn get_commits_info(
function get_commit_info (line 166) | pub fn get_commit_info(
function get_message (line 197) | pub fn get_message(
function gix_get_message (line 215) | pub fn gix_get_message(
function test_log (line 244) | fn test_log() -> Result<()> {
function test_log_first_msg_line (line 275) | fn test_log_first_msg_line() -> Result<()> {
function test_invalid_utf8 (line 295) | fn test_invalid_utf8() -> Result<()> {
function test_get_commit_from_revision (line 323) | fn test_get_commit_from_revision() -> Result<()> {
FILE: asyncgit/src/sync/config.rs
type ShowUntrackedFilesConfig (line 13) | pub enum ShowUntrackedFilesConfig {
method include_none (line 25) | pub const fn include_none(self) -> bool {
method include_untracked (line 30) | pub const fn include_untracked(self) -> bool {
method recurse_untracked_dirs (line 35) | pub const fn recurse_untracked_dirs(self) -> bool {
function untracked_files_config_repo (line 40) | pub fn untracked_files_config_repo(
type PushDefaultStrategyConfig (line 67) | pub enum PushDefaultStrategyConfig {
type Error (line 77) | type Error = crate::Error;
method try_from (line 78) | fn try_from(
function push_default_strategy_config_repo (line 94) | pub fn push_default_strategy_config_repo(
function untracked_files_config (line 106) | pub fn untracked_files_config(
function get_config_string (line 114) | pub fn get_config_string(
function get_config_string_repo (line 122) | pub fn get_config_string_repo(
function test_get_config (line 151) | fn test_get_config() {
FILE: asyncgit/src/sync/cred.rs
type BasicAuthCredential (line 17) | pub struct BasicAuthCredential {
method is_complete (line 26) | pub const fn is_complete(&self) -> bool {
method new (line 30) | pub const fn new(
function need_username_password (line 39) | pub fn need_username_password(repo_path: &RepoPath) -> Result<bool> {
function need_username_password_for_fetch (line 55) | pub fn need_username_password_for_fetch(
function need_username_password_for_push (line 73) | pub fn need_username_password_for_push(
function extract_username_password (line 89) | pub fn extract_username_password(
function extract_username_password_for_fetch (line 118) | pub fn extract_username_password_for_fetch(
function extract_username_password_for_push (line 147) | pub fn extract_username_password_for_push(
function extract_cred_from_url (line 175) | pub fn extract_cred_from_url(url: &str) -> BasicAuthCredential {
function test_credential_complete (line 205) | fn test_credential_complete() {
function test_credential_not_complete (line 214) | fn test_credential_not_complete() {
function test_extract_username_from_url (line 229) | fn test_extract_username_from_url() {
function test_extract_username_password_from_url (line 237) | fn test_extract_username_password_from_url() {
function test_extract_nothing_from_url (line 248) | fn test_extract_nothing_from_url() {
function test_need_username_password_if_https (line 257) | fn test_need_username_password_if_https() {
function test_dont_need_username_password_if_ssh (line 271) | fn test_dont_need_username_password_if_ssh() {
function test_dont_need_username_password_if_pushurl_ssh (line 285) | fn test_dont_need_username_password_if_pushurl_ssh() {
function test_error_if_no_remote_when_trying_to_retrieve_if_need_username_password (line 305) | fn test_error_if_no_remote_when_trying_to_retrieve_if_need_username_pass...
function test_extract_username_password_from_repo (line 317) | fn test_extract_username_password_from_repo() {
function test_extract_username_from_repo (line 340) | fn test_extract_username_from_repo() {
function test_error_if_no_remote_when_trying_to_extract_username_password (line 358) | fn test_error_if_no_remote_when_trying_to_extract_username_password(
FILE: asyncgit/src/sync/diff.rs
type DiffLineType (line 26) | pub enum DiffLineType {
method from (line 39) | fn from(line_type: git2::DiffLineType) -> Self {
type DiffLine (line 53) | pub struct DiffLine {
type DiffLinePosition (line 64) | pub struct DiffLinePosition {
method eq (line 72) | fn eq(&self, other: &&git2::DiffLine) -> bool {
method from (line 79) | fn from(line: &git2::DiffLine<'_>) -> Self {
type HunkHeader (line 88) | pub(crate) struct HunkHeader {
method from (line 96) | fn from(h: DiffHunk) -> Self {
type Hunk (line 108) | pub struct Hunk {
type FileDiff (line 117) | pub struct FileDiff {
type DiffOptions (line 134) | pub struct DiffOptions {
method default (line 144) | fn default() -> Self {
function get_diff_raw (line 153) | pub(crate) fn get_diff_raw<'a>(
function get_diff (line 199) | pub fn get_diff(
function get_diff_commit (line 216) | pub fn get_diff_commit(
function get_diff_commits (line 238) | pub fn get_diff_commits(
function raw_diff_to_file_diff (line 257) | fn raw_diff_to_file_diff(
function is_newline (line 394) | const fn is_newline(c: char) -> bool {
function new_file_content (line 398) | fn new_file_content(path: &Path) -> Option<Vec<u8>> {
function test_untracked_subfolder (line 435) | fn test_untracked_subfolder() {
function test_empty_repo (line 459) | fn test_empty_repo() {
function test_hunks (line 517) | fn test_hunks() {
function test_diff_newfile_in_sub_dir_current_dir (line 559) | fn test_diff_newfile_in_sub_dir_current_dir() {
function test_diff_delta_size (line 584) | fn test_diff_delta_size() -> Result<()> {
function test_binary_diff_delta_size_untracked (line 615) | fn test_binary_diff_delta_size_untracked() -> Result<()> {
function test_diff_delta_size_commit (line 640) | fn test_diff_delta_size_commit() -> Result<()> {
FILE: asyncgit/src/sync/hooks.rs
type HookResult (line 20) | pub enum HookResult {
method from (line 28) | fn from(v: git2_hooks::HookResult) -> Self {
function advertised_remote_refs (line 52) | fn advertised_remote_refs(
function get_remote_ref_for_push (line 85) | fn get_remote_ref_for_push(
function hooks_commit_msg (line 116) | pub fn hooks_commit_msg(
function hooks_pre_commit (line 128) | pub fn hooks_pre_commit(repo_path: &RepoPath) -> Result<HookResult> {
function hooks_post_commit (line 137) | pub fn hooks_post_commit(repo_path: &RepoPath) -> Result<HookResult> {
function hooks_prepare_commit_msg (line 146) | pub fn hooks_prepare_commit_msg(
function hooks_pre_push (line 162) | pub fn hooks_pre_push(
function pre_push_branch_update (line 224) | fn pre_push_branch_update(
function pre_push_tag_updates (line 250) | fn pre_push_tag_updates(
type PrePushTarget (line 279) | pub enum PrePushTarget<'a> {
function repo_init (line 301) | fn repo_init() -> Result<(TempDir, Repository)> {
function create_hook_in_path (line 322) | fn create_hook_in_path(path: &Path, hook_script: &[u8]) {
function test_post_commit_hook_reject_in_subfolder (line 340) | fn test_post_commit_hook_reject_in_subfolder() {
function test_pre_commit_workdir (line 371) | fn test_pre_commit_workdir() {
function test_hooks_commit_msg_reject_in_subfolder (line 398) | fn test_hooks_commit_msg_reject_in_subfolder() {
function test_hooks_commit_msg_reject_in_hooks_folder_githooks_moved_absolute (line 430) | fn test_hooks_commit_msg_reject_in_hooks_folder_githooks_moved_absolute(
function test_pre_push_hook_rejects_based_on_stdin (line 460) | fn test_pre_push_hook_rejects_based_on_stdin() {
FILE: asyncgit/src/sync/hunks.rs
function stage_hunk (line 14) | pub fn stage_hunk(
function reset_hunk (line 40) | pub fn reset_hunk(
function find_hunk_index (line 72) | fn find_hunk_index(diff: &Diff, hunk_hash: u64) -> Option<usize> {
function unstage_hunk (line 99) | pub fn unstage_hunk(
function reset_untracked_file_which_will_not_find_hunk (line 165) | fn reset_untracked_file_which_will_not_find_hunk() -> Result<()> {
FILE: asyncgit/src/sync/ignore.rs
function add_to_ignore (line 16) | pub fn add_to_ignore(
function file_ends_with_newline (line 52) | fn file_ends_with_newline(file: &Path) -> Result<bool> {
function test_empty (line 72) | fn test_empty() -> Result<()> {
function read_lines (line 89) | fn read_lines<P>(
function test_append (line 100) | fn test_append() -> Result<()> {
function test_append_no_newline_at_end (line 122) | fn test_append_no_newline_at_end() -> Result<()> {
function test_ignore_ignore (line 144) | fn test_ignore_ignore() {
FILE: asyncgit/src/sync/logwalker.rs
type TimeOrderedCommit (line 10) | struct TimeOrderedCommit<'a>(Commit<'a>);
method eq (line 15) | fn eq(&self, other: &Self) -> bool {
method partial_cmp (line 21) | fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
method cmp (line 27) | fn cmp(&self, other: &Self) -> Ordering {
type LogWalker (line 33) | pub struct LogWalker<'a> {
function new (line 43) | pub fn new(repo: &'a Repository, limit: usize) -> Result<Self> {
function visited (line 59) | pub fn visited(&self) -> usize {
function filter (line 65) | pub fn filter(
function read (line 73) | pub fn read(&mut self, out: &mut Vec<CommitId>) -> Result<usize> {
function visit (line 103) | fn visit(&mut self, c: Commit<'a>) {
type LogWalkerWithoutFilter (line 120) | pub struct LogWalkerWithoutFilter<'a> {
function new (line 128) | pub fn new(
function visited (line 155) | pub const fn visited(&self) -> usize {
function read (line 160) | pub fn read(&mut self, out: &mut Vec<CommitId>) -> Result<usize> {
function test_limit (line 198) | fn test_limit() -> Result<()> {
function test_logwalker (line 223) | fn test_logwalker() -> Result<()> {
function test_logwalker_without_filter (line 256) | fn test_logwalker_without_filter() -> Result<()> {
function test_logwalker_with_filter (line 290) | fn test_logwalker_with_filter() -> Result<()> {
function test_logwalker_with_filter_search (line 341) | fn test_logwalker_with_filter_search() {
FILE: asyncgit/src/sync/merge.rs
function mergehead_ids (line 21) | pub fn mergehead_ids(repo_path: &RepoPath) -> Result<Vec<CommitId>> {
function abort_pending_state (line 39) | pub fn abort_pending_state(repo_path: &RepoPath) -> Result<()> {
function merge_branch (line 53) | pub fn merge_branch(
function rebase_progress (line 68) | pub fn rebase_progress(
function continue_pending_rebase (line 79) | pub fn continue_pending_rebase(
function abort_pending_rebase (line 90) | pub fn abort_pending_rebase(repo_path: &RepoPath) -> Result<()> {
function merge_branch_repo (line 99) | pub fn merge_branch_repo(
function merge_msg (line 124) | pub fn merge_msg(repo_path: &RepoPath) -> Result<String> {
function merge_commit (line 134) | pub fn merge_commit(
function test_smoke (line 165) | fn test_smoke() {
FILE: asyncgit/src/sync/mod.rs
function repo_init_empty (line 131) | pub fn repo_init_empty() -> Result<(TempDir, Repository)> {
function repo_init (line 147) | pub fn repo_init() -> Result<(TempDir, Repository)> {
function repo_init_with_prefix (line 153) | pub fn repo_init_with_prefix(
function repo_clone (line 185) | pub fn repo_clone(p: &str) -> Result<(TempDir, Repository)> {
function write_commit_file (line 202) | pub fn write_commit_file(
function write_commit_file_at (line 224) | pub fn write_commit_file_at(
function get_statuses (line 242) | pub fn get_statuses(repo_path: &RepoPath) -> (usize, usize) {
function debug_cmd_print (line 254) | pub fn debug_cmd_print(path: &RepoPath, cmd: &str) {
function get_commit_ids (line 260) | pub fn get_commit_ids(
function repo_init_bare (line 274) | pub fn repo_init_bare() -> Result<(TempDir, Repository)> {
function sandbox_config_files (line 286) | fn sandbox_config_files() {
function commit_at (line 304) | fn commit_at(
function init_log (line 341) | fn init_log() {
function debug_cmd (line 348) | fn debug_cmd(path: &RepoPath, cmd: &str) -> String {
FILE: asyncgit/src/sync/patches.rs
type HunkLines (line 5) | pub struct HunkLines<'a> {
function get_file_diff_patch (line 10) | pub fn get_file_diff_patch<'a>(
function patch_get_hunklines (line 39) | pub fn patch_get_hunklines<'a>(
function get_patches (line 66) | fn get_patches<'a>(diff: &Diff<'a>) -> Result<Vec<Patch<'a>>> {
FILE: asyncgit/src/sync/rebase.rs
function rebase_branch (line 12) | pub fn rebase_branch(
function rebase_branch_repo (line 24) | fn rebase_branch_repo(
function conflict_free_rebase (line 38) | pub fn conflict_free_rebase(
type RebaseState (line 73) | pub enum RebaseState {
function rebase (line 81) | pub fn rebase(
function continue_rebase (line 110) | pub fn continue_rebase(
type RebaseProgress (line 148) | pub struct RebaseProgress {
function get_rebase_progress (line 158) | pub fn get_rebase_progress(
function abort_rebase (line 178) | pub fn abort_rebase(repo: &git2::Repository) -> Result<()> {
function parent_ids (line 200) | fn parent_ids(repo: &Repository, c: CommitId) -> Vec<CommitId> {
function test_rebase_branch_repo (line 212) | fn test_rebase_branch_repo(
function test_smoke (line 229) | fn test_smoke() {
function test_conflict (line 258) | fn test_conflict() {
function test_conflicted_abort (line 300) | fn test_conflicted_abort() {
FILE: asyncgit/src/sync/remotes/callbacks.rs
type CallbackStats (line 12) | pub struct CallbackStats {
type Callbacks (line 18) | pub struct Callbacks {
method new (line 27) | pub fn new(
method get_stats (line 44) | pub fn get_stats(&self) -> Result<CallbackStats> {
method callbacks (line 50) | pub fn callbacks<'a>(&self) -> RemoteCallbacks<'a> {
method push_update_reference (line 105) | fn push_update_reference(
method pack_progress (line 118) | fn pack_progress(
method transfer_progress (line 134) | fn transfer_progress(&self, p: &git2::Progress) {
method update_tips (line 148) | fn update_tips(&self, name: &str, a: git2::Oid, b: git2::Oid) {
method push_transfer_progress (line 159) | fn push_transfer_progress(
method credentials (line 179) | fn credentials(
FILE: asyncgit/src/sync/remotes/mod.rs
constant DEFAULT_REMOTE_NAME (line 28) | pub const DEFAULT_REMOTE_NAME: &str = "origin";
function proxy_auto (line 31) | pub fn proxy_auto<'a>() -> ProxyOptions<'a> {
function add_remote (line 38) | pub fn add_remote(
function rename_remote (line 49) | pub fn rename_remote(
function update_remote_url (line 60) | pub fn update_remote_url(
function delete_remote (line 71) | pub fn delete_remote(
function validate_remote_name (line 81) | pub fn validate_remote_name(name: &str) -> bool {
function get_remotes (line 86) | pub fn get_remotes(repo_path: &RepoPath) -> Result<Vec<String>> {
function get_remote_url (line 98) | pub fn get_remote_url(
function get_default_remote (line 113) | pub fn get_default_remote(repo_path: &RepoPath) -> Result<String> {
function get_current_branch (line 121) | fn get_current_branch(
function get_default_remote_for_fetch (line 144) | pub fn get_default_remote_for_fetch(
function get_default_remote_for_fetch_in_repo (line 152) | pub(crate) fn get_default_remote_for_fetch_in_repo(
function get_default_remote_for_push (line 194) | pub fn get_default_remote_for_push(
function get_default_remote_for_push_in_repo (line 202) | pub(crate) fn get_default_remote_for_push_in_repo(
function get_default_remote_in_repo (line 236) | pub(crate) fn get_default_remote_in_repo(
function fetch_from_remote (line 270) | fn fetch_from_remote(
function fetch_all (line 298) | pub fn fetch_all(
function fetch (line 332) | pub(crate) fn fetch(
function test_smoke (line 368) | fn test_smoke() {
function test_default_remote (line 383) | fn test_default_remote() {
function test_default_remote_out_of_order (line 409) | fn test_default_remote_out_of_order() {
function test_default_remote_inconclusive (line 441) | fn test_default_remote_inconclusive() {
function test_default_remote_for_fetch (line 477) | fn test_default_remote_for_fetch() {
function test_default_remote_for_push (line 509) | fn test_default_remote_for_push() {
FILE: asyncgit/src/sync/remotes/push.rs
type AsyncProgress (line 23) | pub trait AsyncProgress: Clone + Send + Sync {
method is_done (line 25) | fn is_done(&self) -> bool;
method progress (line 27) | fn progress(&self) -> ProgressPercent;
method is_done (line 72) | fn is_done(&self) -> bool {
method progress (line 75) | fn progress(&self) -> ProgressPercent {
type ProgressNotification (line 32) | pub enum ProgressNotification {
type PushType (line 102) | pub enum PushType {
function push_branch (line 111) | pub fn push_branch(
function push_raw (line 134) | pub fn push_raw(
function test_force_push (line 217) | fn test_force_push() {
function test_force_push_rewrites_history (line 301) | fn test_force_push_rewrites_history() {
function test_delete_remote_branch (line 444) | fn test_delete_remote_branch() {
FILE: asyncgit/src/sync/remotes/tags.rs
type PushTagsProgress (line 21) | pub enum PushTagsProgress {
method progress (line 36) | fn progress(&self) -> ProgressPercent {
method is_done (line 45) | fn is_done(&self) -> bool {
function remote_tag_refs (line 51) | fn remote_tag_refs(
function tags_missing_remote (line 80) | pub fn tags_missing_remote(
function push_tags (line 105) | pub fn push_tags(
function test_push_pull_tags (line 174) | fn test_push_pull_tags() {
function test_get_remote_tags (line 222) | fn test_get_remote_tags() {
function test_tags_missing_remote (line 261) | fn test_tags_missing_remote() {
function test_tags_fetch (line 296) | fn test_tags_fetch() {
function test_tags_fetch_all (line 336) | fn test_tags_fetch_all() {
function test_tags_delete_remote (line 376) | fn test_tags_delete_remote() {
FILE: asyncgit/src/sync/repository.rs
type RepoPathRef (line 11) | pub type RepoPathRef = RefCell<RepoPath>;
type RepoPath (line 15) | pub enum RepoPath {
method gitpath (line 29) | pub fn gitpath(&self) -> &Path {
method workdir (line 37) | pub fn workdir(&self) -> Option<&Path> {
method from (line 46) | fn from(value: PathBuf) -> Self {
method from (line 52) | fn from(p: &str) -> Self {
function repo (line 57) | pub fn repo(repo_path: &RepoPath) -> Result<Repository> {
function gix_repo (line 71) | pub fn gix_repo(repo_path: &RepoPath) -> Result<gix::Repository> {
FILE: asyncgit/src/sync/reset.rs
function reset_stage (line 7) | pub fn reset_stage(repo_path: &RepoPath, path: &str) -> Result<()> {
function reset_workdir (line 25) | pub fn reset_workdir(repo_path: &RepoPath, path: &str) -> Result<()> {
function reset_repo (line 42) | pub fn reset_repo(
function test_reset_only_unstaged (line 104) | fn test_reset_only_unstaged() {
function test_reset_untracked_in_subdir (line 149) | fn test_reset_untracked_in_subdir() {
function test_reset_folder (line 175) | fn test_reset_folder() -> Result<()> {
function test_reset_untracked_in_subdir_and_index (line 221) | fn test_reset_untracked_in_subdir_and_index() {
function unstage_in_empty_repo (line 261) | fn unstage_in_empty_repo() {
function test_reset_untracked_in_subdir_with_cwd_in_subdir (line 285) | fn test_reset_untracked_in_subdir_with_cwd_in_subdir() {
function test_reset_untracked_subdir (line 315) | fn test_reset_untracked_subdir() {
FILE: asyncgit/src/sync/reword.rs
function reword (line 12) | pub fn reword(
function get_current_branch (line 63) | fn get_current_branch(
function reword_internal (line 83) | fn reword_internal(
function test_reword (line 160) | fn test_reword() {
FILE: asyncgit/src/sync/sign.rs
type SignBuilderError (line 8) | pub enum SignBuilderError {
type SignError (line 33) | pub enum SignError {
type Sign (line 56) | pub trait Sign {
method sign (line 68) | fn sign(
method program (line 75) | fn program(&self) -> &String;
method signing_key (line 79) | fn signing_key(&self) -> &String;
method sign (line 207) | fn sign(
method program (line 264) | fn program(&self) -> &String {
method signing_key (line 269) | fn signing_key(&self) -> &String {
method sign (line 316) | fn sign(
method program (line 330) | fn program(&self) -> &String {
method signing_key (line 335) | fn signing_key(&self) -> &String {
type SignBuilder (line 83) | pub struct SignBuilder;
method from_gitconfig (line 105) | pub fn from_gitconfig(
type GPGSign (line 191) | pub struct GPGSign {
method new (line 198) | pub fn new(program: &str, signing_key: &str) -> Self {
type SSHSign (line 275) | pub struct SSHSign {
method new (line 285) | pub fn new(mut key: PathBuf) -> Result<Self, SignBuilderError> {
function test_invalid_signing_format (line 347) | fn test_invalid_signing_format() -> Result<()> {
function test_program_and_signing_key_defaults (line 364) | fn test_program_and_signing_key_defaults() -> Result<()> {
function test_gpg_program_configs (line 376) | fn test_gpg_program_configs() -> Result<()> {
function test_user_signingkey (line 409) | fn test_user_signingkey() -> Result<()> {
function test_ssh_program_configs (line 425) | fn test_ssh_program_configs() -> Result<()> {
FILE: asyncgit/src/sync/staging/discard_tracked.rs
function discard_lines (line 13) | pub fn discard_lines(
function test_discard (line 51) | fn test_discard() {
function test_discard2 (line 102) | fn test_discard2() {
function test_discard3 (line 141) | fn test_discard3() {
function test_discard4 (line 186) | fn test_discard4() {
function test_discard_if_first_selected_line_is_not_in_any_hunk (line 233) | fn test_discard_if_first_selected_line_is_not_in_any_hunk() {
function test_discard_deletions_filestart_breaking_with_zero_context (line 277) | fn test_discard_deletions_filestart_breaking_with_zero_context() {
function test_discard5 (line 315) | fn test_discard5() {
FILE: asyncgit/src/sync/staging/mod.rs
constant NEWLINE (line 14) | const NEWLINE: char = '\n';
type NewFromOldContent (line 17) | struct NewFromOldContent {
method add_from_hunk (line 23) | fn add_from_hunk(&mut self, line: &DiffLine) -> Result<()> {
method skip_old_line (line 37) | const fn skip_old_line(&mut self) {
method add_old_line (line 41) | fn add_old_line(&mut self, old_lines: &[&str]) {
method catchup_to_hunkstart (line 46) | fn catchup_to_hunkstart(
method finish (line 56) | fn finish(mut self, old_lines: &[&str]) -> String {
function apply_selection (line 73) | pub fn apply_selection(
function load_file (line 174) | pub fn load_file(
FILE: asyncgit/src/sync/staging/stage_tracked.rs
function stage_lines (line 14) | pub fn stage_lines(
function test_stage (line 72) | fn test_stage() {
function test_panic_stage_no_newline (line 107) | fn test_panic_stage_no_newline() {
function test_unstage (line 146) | fn test_unstage() {
FILE: asyncgit/src/sync/stash.rs
function get_stashes (line 13) | pub fn get_stashes(repo_path: &RepoPath) -> Result<Vec<CommitId>> {
function stash_drop (line 27) | pub fn stash_drop(
function stash_pop (line 43) | pub fn stash_pop(
function stash_apply (line 59) | pub fn stash_apply(
function get_stash_index (line 80) | fn get_stash_index(
function stash_save (line 101) | pub fn stash_save(
function test_smoke (line 141) | fn test_smoke() {
function test_stashing (line 153) | fn test_stashing() -> Result<()> {
function test_stashes (line 172) | fn test_stashes() -> Result<()> {
function test_stash_nothing_untracked (line 196) | fn test_stash_nothing_untracked() -> Result<()> {
function test_stash_without_second_parent (line 213) | fn test_stash_without_second_parent() -> Result<()> {
function test_stash_apply_conflict (line 242) | fn test_stash_apply_conflict() {
function test_stash_apply_conflict2 (line 261) | fn test_stash_apply_conflict2() {
function test_stash_apply_creating_conflict (line 282) | fn test_stash_apply_creating_conflict() {
function test_stash_pop_no_conflict (line 307) | fn test_stash_pop_no_conflict() {
function test_stash_pop_conflict (line 330) | fn test_stash_pop_conflict() {
function test_stash_pop_conflict_after_commit (line 353) | fn test_stash_pop_conflict_after_commit() {
FILE: asyncgit/src/sync/state.rs
type RepoState (line 8) | pub enum RepoState {
method from (line 22) | fn from(state: RepositoryState) -> Self {
function repo_state (line 37) | pub fn repo_state(repo_path: &RepoPath) -> Result<RepoState> {
FILE: asyncgit/src/sync/status.rs
type StatusItemType (line 18) | pub enum StatusItemType {
method from (line 36) | fn from(
method from (line 55) | fn from(change_ref: gix::diff::index::ChangeRef) -> Self {
method from (line 68) | fn from(s: Status) -> Self {
method from (line 86) | fn from(d: Delta) -> Self {
type StatusItem (line 99) | pub struct StatusItem {
type StatusType (line 108) | pub enum StatusType {
method from (line 119) | fn from(s: StatusType) -> Self {
function is_workdir_clean (line 129) | pub fn is_workdir_clean(
function from (line 161) | fn from(value: ShowUntrackedFilesConfig) -> Self {
function get_status (line 171) | pub fn get_status(
function discard_status (line 289) | pub fn discard_status(repo_path: &RepoPath) -> Result<bool> {
function test_discard_status (line 314) | fn test_discard_status() {
function test_get_status_with_workdir (line 343) | fn test_get_status_with_workdir() {
FILE: asyncgit/src/sync/submodules.rs
type SubmoduleInfo (line 16) | pub struct SubmoduleInfo {
method get_repo_path (line 44) | pub fn get_repo_path(
type SubmoduleParentInfo (line 33) | pub struct SubmoduleParentInfo {
function submodule_to_info (line 55) | fn submodule_to_info(s: &Submodule, r: &Repository) -> SubmoduleInfo {
function get_submodules (line 74) | pub fn get_submodules(
function update_submodule (line 91) | pub fn update_submodule(
function submodule_parent_info (line 110) | pub fn submodule_parent_info(
function test_smoke (line 167) | fn test_smoke() {
FILE: asyncgit/src/sync/tags.rs
type Tag (line 11) | pub struct Tag {
method new (line 20) | pub fn new(name: &str) -> Self {
type CommitTags (line 29) | pub type CommitTags = Vec<Tag>;
type Tags (line 31) | pub type Tags = BTreeMap<CommitId, CommitTags>;
type TagWithMetadata (line 34) | pub struct TagWithMetadata {
function get_tags (line 52) | pub fn get_tags(repo_path: &RepoPath) -> Result<Tags> {
function get_tags_with_metadata (line 90) | pub fn get_tags_with_metadata(
function delete_tag (line 150) | pub fn delete_tag(
function test_smoke (line 169) | fn test_smoke() {
function test_multitags (line 179) | fn test_multitags() {
FILE: asyncgit/src/sync/tree.rs
type TreeFile (line 15) | pub struct TreeFile {
function tree_files (line 25) | pub fn tree_files(
function sort_file_list (line 45) | fn sort_file_list(files: &mut [TreeFile]) {
function path_cmp (line 50) | fn path_cmp(a: &Path, b: &Path) -> Ordering {
function tree_file_content (line 75) | pub fn tree_file_content(
function tree_recurse (line 95) | fn tree_recurse(
function test_smoke (line 130) | fn test_smoke() {
function test_sorting (line 158) | fn test_sorting() {
function test_sorting_folders (line 183) | fn test_sorting_folders() {
function test_sorting_folders2 (line 207) | fn test_sorting_folders2() {
function test_path_cmp (line 231) | fn test_path_cmp() {
function test_path_file_cmp (line 242) | fn test_path_file_cmp() {
FILE: asyncgit/src/sync/utils.rs
type Head (line 20) | pub struct Head {
function repo_open_error (line 28) | pub fn repo_open_error(repo_path: &RepoPath) -> Option<String> {
function work_dir (line 44) | pub(crate) fn work_dir(repo: &Repository) -> Result<&Path> {
function repo_dir (line 49) | pub fn repo_dir(repo_path: &RepoPath) -> Result<PathBuf> {
function repo_work_dir (line 55) | pub fn repo_work_dir(repo_path: &RepoPath) -> Result<String> {
function get_head (line 64) | pub fn get_head(repo_path: &RepoPath) -> Result<CommitId> {
function get_head_tuple (line 70) | pub fn get_head_tuple(repo_path: &RepoPath) -> Result<Head> {
function get_head_refname (line 79) | pub fn get_head_refname(repo: &Repository) -> Result<String> {
function get_head_repo (line 87) | pub fn get_head_repo(repo: &Repository) -> Result<CommitId> {
function stage_add_file (line 96) | pub fn stage_add_file(
function stage_add_all (line 113) | pub fn stage_add_all(
function undo_last_commit (line 146) | pub fn undo_last_commit(repo_path: &RepoPath) -> Result<()> {
function stage_addremoved (line 161) | pub fn stage_addremoved(
function bytes2string (line 177) | pub(crate) fn bytes2string(bytes: &[u8]) -> Result<String> {
function repo_write_file (line 182) | pub(crate) fn repo_write_file(
function read_file (line 197) | pub fn read_file(path: &Path) -> Result<String> {
function repo_read_file (line 208) | pub(crate) fn repo_read_file(
function test_stage_add_smoke (line 246) | fn test_stage_add_smoke() {
function test_staging_one_file (line 256) | fn test_staging_one_file() {
function test_staging_one_file_from_different_sub_directory (line 281) | fn test_staging_one_file_from_different_sub_directory() {
function test_staging_folder (line 319) | fn test_staging_folder() -> Result<()> {
function test_undo_commit_empty_repo (line 350) | fn test_undo_commit_empty_repo() {
function test_undo_commit (line 361) | fn test_undo_commit() {
function test_not_staging_untracked_folder (line 387) | fn test_not_staging_untracked_folder() -> Result<()> {
function test_staging_deleted_file (line 417) | fn test_staging_deleted_file() {
function test_staging_sub_git_folder (line 453) | fn test_staging_sub_git_folder() -> Result<()> {
function test_head_empty (line 486) | fn test_head_empty() -> Result<()> {
function test_head (line 498) | fn test_head() -> Result<()> {
FILE: asyncgit/src/tags.rs
type TagsResult (line 17) | pub struct TagsResult {
type AsyncTags (line 23) | pub struct AsyncTags {
method new (line 32) | pub fn new(
method last (line 45) | pub fn last(&self) -> Result<Option<Tags>> {
method is_pending (line 50) | pub fn is_pending(&self) -> bool {
method is_outdated (line 55) | fn is_outdated(&self, dur: Duration) -> bool {
method request (line 62) | pub fn request(
type JobState (line 103) | enum JobState {
type AsyncTagsJob (line 110) | pub struct AsyncTagsJob {
method new (line 117) | pub fn new(last_hash: u64, repo: RepoPath) -> Self {
method result (line 126) | pub fn result(&self) -> Option<Result<(Instant, TagsResult)>> {
type Notification (line 141) | type Notification = AsyncGitNotification;
type Progress (line 142) | type Progress = ();
method run (line 144) | fn run(
FILE: asyncgit/src/treefiles.rs
type FileTreeResult (line 10) | pub struct FileTreeResult {
type JobState (line 17) | enum JobState {
type AsyncTreeFilesJob (line 24) | pub struct AsyncTreeFilesJob {
method new (line 31) | pub fn new(repo: RepoPath, commit: CommitId) -> Self {
method result (line 41) | pub fn result(&self) -> Option<FileTreeResult> {
type Notification (line 56) | type Notification = AsyncGitNotification;
type Progress (line 57) | type Progress = ();
method run (line 59) | fn run(
FILE: build.rs
function get_git_hash (line 3) | fn get_git_hash() -> String {
function main (line 27) | fn main() {
FILE: filetreelist/src/error.rs
type Error (line 5) | pub enum Error {
type Result (line 13) | pub type Result<T> = std::result::Result<T, Error>;
FILE: filetreelist/src/filetree.rs
type MoveSelection (line 9) | pub enum MoveSelection {
type Direction (line 21) | enum Direction {
type VisualSelection (line 27) | pub struct VisualSelection {
type FileTree (line 34) | pub struct FileTree {
method new (line 44) | pub fn new(
method is_empty (line 60) | pub const fn is_empty(&self) -> bool {
method selection (line 65) | pub const fn selection(&self) -> Option<usize> {
method collapse_but_root (line 70) | pub fn collapse_but_root(&mut self) {
method iterate (line 78) | pub fn iterate(
method visual_selection (line 93) | pub const fn visual_selection(&self) -> Option<&VisualSelection> {
method selected_file (line 98) | pub fn selected_file(&self) -> Option<&TreeItemInfo> {
method collapse_recursive (line 110) | pub fn collapse_recursive(&mut self) {
method expand_recursive (line 117) | pub fn expand_recursive(&mut self) {
method selection_page_updown (line 123) | fn selection_page_updown(
method move_selection (line 144) | pub fn move_selection(&mut self, dir: MoveSelection) -> bool {
method select_file (line 180) | pub fn select_file(&mut self, path: &Path) -> bool {
method visual_index_to_absolute (line 199) | fn visual_index_to_absolute(
method calc_visual_selection (line 215) | fn calc_visual_selection(&self) -> Option<VisualSelection> {
method selection_end (line 236) | fn selection_end(&self) -> Option<usize> {
method get_new_selection (line 242) | fn get_new_selection(
method selection_updown (line 253) | fn selection_updown(
method select_parent (line 271) | fn select_parent(&self, current_index: usize) -> Option<usize> {
method selection_left (line 285) | fn selection_left(
method selection_right (line 299) | fn selection_right(
method is_visible_index (line 319) | fn is_visible_index(&self, index: usize) -> bool {
function test_selection (line 334) | fn test_selection() {
function test_selection_skips_collapsed (line 352) | fn test_selection_skips_collapsed() {
function test_selection_left_collapse (line 375) | fn test_selection_left_collapse() {
function test_selection_left_parent (line 401) | fn test_selection_left_parent() {
function test_selection_right_expand (line 428) | fn test_selection_right_expand() {
function test_selection_top (line 460) | fn test_selection_top() {
function test_visible_selection (line 481) | fn test_visible_selection() {
function test_selection_page_updown (line 507) | fn test_selection_page_updown() {
FILE: filetreelist/src/filetreeitems.rs
type FileTreeItems (line 14) | pub struct FileTreeItems {
method new (line 21) | pub fn new(
method create_items (line 35) | fn create_items<'a>(
method len (line 62) | pub const fn len(&self) -> usize {
method file_count (line 67) | pub const fn file_count(&self) -> usize {
method iterate (line 72) | pub const fn iterate(
method push_dirs (line 80) | fn push_dirs<'a>(
method path_to_string (line 120) | fn path_to_string(p: &Path) -> Result<String> {
method collapse (line 129) | pub fn collapse(&mut self, index: usize, recursive: bool) {
method expand (line 156) | pub fn expand(&mut self, index: usize, recursive: bool) {
method show_element (line 192) | pub fn show_element(&mut self, index: usize) -> Option<usize> {
method show_element_upward (line 199) | fn show_element_upward(&mut self, index: usize) -> Option<usize> {
method show_element_downward (line 241) | fn show_element_downward(
method update_visibility (line 274) | fn update_visibility(
method fold_paths (line 326) | fn fold_paths(
method unindent (line 368) | fn unindent(
function test_simple (line 389) | fn test_simple() {
function test_push_path (line 416) | fn test_push_path() {
function test_push_path2 (line 442) | fn test_push_path2() {
function test_folder (line 470) | fn test_folder() {
function test_indent (line 489) | fn test_indent() {
function test_indent_folder_file_name (line 506) | fn test_indent_folder_file_name() {
function test_folder_dup (line 525) | fn test_folder_dup() {
function test_collapse (line 554) | fn test_collapse() {
function test_iterate_collapsed (line 571) | fn test_iterate_collapsed() {
function get_visible (line 590) | pub fn get_visible(tree: &FileTreeItems) -> Vec<bool> {
function test_expand (line 598) | fn test_expand() {
function test_expand_bug (line 642) | fn test_expand_bug() {
function test_collapse_too_much (line 686) | fn test_collapse_too_much() {
function test_expand_with_collapsed_sub_parts (line 716) | fn test_expand_with_collapsed_sub_parts() {
function test_show_element (line 774) | fn test_show_element() {
function test_show_element_later_elements (line 812) | fn test_show_element_later_elements() {
function test_show_element_downward_parent (line 850) | fn test_show_element_downward_parent() {
function test_show_element_expand_visible_parent (line 884) | fn test_show_element_expand_visible_parent() {
function test_merge_simple (line 926) | fn test_merge_simple() {
function test_merge_simple2 (line 940) | fn test_merge_simple2() {
function test_merge_indent (line 960) | fn test_merge_indent() {
function test_merge_single_paths (line 992) | fn test_merge_single_paths() {
function test_merge_nothing (line 1016) | fn test_merge_nothing() {
FILE: filetreelist/src/item.rs
type TreeItemInfo (line 6) | pub struct TreeItemInfo {
method new (line 20) | pub const fn new(indent: u8, full_path: PathBuf) -> Self {
method is_visible (line 30) | pub const fn is_visible(&self) -> bool {
method full_path_str (line 36) | pub fn full_path_str(&self) -> &str {
method full_path (line 41) | pub fn full_path(&self) -> &Path {
method path_str (line 46) | pub fn path_str(&self) -> &str {
method path (line 52) | pub fn path(&self) -> &Path {
method indent (line 68) | pub const fn indent(&self) -> u8 {
method unindent (line 73) | pub const fn unindent(&mut self) {
method set_visible (line 77) | pub const fn set_visible(&mut self, visible: bool) {
type PathCollapsed (line 84) | pub struct PathCollapsed(pub bool);
type FileTreeItemKind (line 88) | pub enum FileTreeItemKind {
method is_path (line 94) | pub const fn is_path(&self) -> bool {
method is_path_collapsed (line 98) | pub const fn is_path_collapsed(&self) -> bool {
type FileTreeItem (line 108) | pub struct FileTreeItem {
method new_file (line 114) | pub fn new_file(path: &Path) -> Result<Self> {
method new_path (line 127) | pub fn new_path(path: &Path, collapsed: bool) -> Result<Self> {
method fold (line 138) | pub fn fold(&mut self, next: Self) {
method info (line 150) | pub const fn info(&self) -> &TreeItemInfo {
method info_mut (line 155) | pub const fn info_mut(&mut self) -> &mut TreeItemInfo {
method kind (line 160) | pub const fn kind(&self) -> &FileTreeItemKind {
method collapse_path (line 166) | pub fn collapse_path(&mut self) {
method expand_path (line 173) | pub fn expand_path(&mut self) {
method hide (line 179) | pub const fn hide(&mut self) {
method show (line 184) | pub const fn show(&mut self) {
method eq (line 192) | fn eq(&self, other: &Self) -> bool {
method partial_cmp (line 198) | fn partial_cmp(
method cmp (line 207) | fn cmp(&self, other: &Self) -> std::cmp::Ordering {
function test_smoke (line 218) | fn test_smoke() {
FILE: filetreelist/src/tree_iter.rs
type TreeIterator (line 3) | pub struct TreeIterator<'a> {
function new (line 9) | pub const fn new(
type Item (line 21) | type Item = (&'a FileTreeItem, bool);
method next (line 23) | fn next(&mut self) -> Option<Self::Item> {
FILE: filetreelist/src/treeitems_iter.rs
type TreeItemsIterator (line 3) | pub struct TreeItemsIterator<'a> {
function new (line 11) | pub const fn new(
type Item (line 26) | type Item = (usize, &'a FileTreeItem);
method next (line 28) | fn next(&mut self) -> Option<Self::Item> {
FILE: git2-hooks/src/error.rs
type HooksError (line 5) | pub enum HooksError {
type Result (line 23) | pub type Result<T> = std::result::Result<T, HooksError>;
FILE: git2-hooks/src/hookspath.rs
type HookPaths (line 12) | pub struct HookPaths {
method new (line 30) | pub fn new(
method expand_path (line 64) | fn expand_path(path: &Path, pwd: &Path) -> Result<PathBuf> {
method config_hook_path (line 97) | fn config_hook_path(repo: &Repository) -> Result<Option<String>> {
method find_hook (line 103) | fn find_hook(
method found (line 131) | pub fn found(&self) -> bool {
method run_hook (line 137) | pub fn run_hook(&self, args: &[&str]) -> Result<HookResult> {
method run_hook_os_str (line 143) | pub fn run_hook_os_str<I, S>(&self, args: I) -> Result<HookResult>
method run_hook_os_str_with_stdin (line 153) | pub fn run_hook_os_str_with_stdin<I, S>(
constant CONFIG_HOOKS_PATH (line 18) | const CONFIG_HOOKS_PATH: &str = "core.hooksPath";
constant DEFAULT_HOOKS_PATH (line 19) | const DEFAULT_HOOKS_PATH: &str = "hooks";
constant ENOEXEC (line 20) | const ENOEXEC: i32 = 8;
function sh_command (line 276) | fn sh_command() -> Command {
function is_executable (line 296) | fn is_executable(path: &Path) -> bool {
function is_executable (line 315) | const fn is_executable(_: &Path) -> bool {
type CommandExt (line 319) | trait CommandExt {
constant CREATE_NO_WINDOW (line 329) | const CREATE_NO_WINDOW: u32 = 0x0800_0000;
method with_no_window (line 331) | fn with_no_window(&mut self) -> &mut Self;
method with_no_window (line 340) | fn with_no_window(&mut self) -> &mut Self {
function test_hookspath_relative (line 357) | fn test_hookspath_relative() {
function test_hookspath_absolute (line 369) | fn test_hookspath_absolute() {
FILE: git2-hooks/src/lib.rs
constant HOOK_POST_COMMIT (line 43) | pub const HOOK_POST_COMMIT: &str = "post-commit";
constant HOOK_PRE_COMMIT (line 44) | pub const HOOK_PRE_COMMIT: &str = "pre-commit";
constant HOOK_COMMIT_MSG (line 45) | pub const HOOK_COMMIT_MSG: &str = "commit-msg";
constant HOOK_PREPARE_COMMIT_MSG (line 46) | pub const HOOK_PREPARE_COMMIT_MSG: &str = "prepare-commit-msg";
constant HOOK_PRE_PUSH (line 47) | pub const HOOK_PRE_PUSH: &str = "pre-push";
constant HOOK_COMMIT_MSG_TEMP_FILE (line 49) | const HOOK_COMMIT_MSG_TEMP_FILE: &str = "COMMIT_EDITMSG";
function hook_available (line 52) | pub fn hook_available(
type PrePushRef (line 62) | pub struct PrePushRef {
method new (line 70) | pub fn new(
method format_oid (line 84) | fn format_oid(oid: Option<Oid>) -> String {
method to_line (line 90) | pub fn to_line(&self) -> String {
method to_stdin (line 101) | pub fn to_stdin(updates: &[Self]) -> String {
type HookRunResponse (line 113) | pub struct HookRunResponse {
method is_successful (line 141) | pub const fn is_successful(&self) -> bool {
type HookResult (line 125) | pub enum HookResult {
method is_successful (line 134) | pub const fn is_successful(&self) -> bool {
function create_hook (line 150) | pub fn create_hook(
function create_hook_in_path (line 164) | fn create_hook_in_path(path: &Path, hook_script: &[u8]) {
function hooks_commit_msg (line 184) | pub fn hooks_commit_msg(
function hooks_pre_commit (line 208) | pub fn hooks_pre_commit(
function hooks_post_commit (line 222) | pub fn hooks_post_commit(
function hooks_pre_push (line 247) | pub fn hooks_pre_push(
type PrepareCommitMsgSource (line 274) | pub enum PrepareCommitMsgSource {
function hooks_prepare_commit_msg (line 284) | pub fn hooks_prepare_commit_msg(
function branch_update (line 340) | fn branch_update(
function head_branch (line 371) | fn head_branch(repo: &Repository) -> String {
function test_pre_push_ref_format (line 376) | fn test_pre_push_ref_format() {
function test_smoke (line 445) | fn test_smoke() {
function test_hooks_commit_msg_ok (line 465) | fn test_hooks_commit_msg_ok() {
function test_hooks_commit_msg_with_shell_command_ok (line 483) | fn test_hooks_commit_msg_with_shell_command_ok() {
function test_pre_commit_sh (line 503) | fn test_pre_commit_sh() {
function test_hook_with_missing_shebang (line 516) | fn test_hook_with_missing_shebang() {
function test_no_hook_found (line 545) | fn test_no_hook_found() {
function test_other_path (line 553) | fn test_other_path() {
function test_other_path_precedence (line 575) | fn test_other_path_precedence() {
function test_pre_commit_fail_sh (line 608) | fn test_pre_commit_fail_sh() {
function test_env_containing_path (line 622) | fn test_env_containing_path() {
function test_pre_commit_fail_hookspath (line 650) | fn test_pre_commit_fail_hookspath() {
function test_pre_commit_fail_bare (line 680) | fn test_pre_commit_fail_bare() {
function test_pre_commit_py (line 694) | fn test_pre_commit_py() {
function test_pre_commit_fail_py (line 715) | fn test_pre_commit_fail_py() {
function test_hooks_commit_msg_reject (line 736) | fn test_hooks_commit_msg_reject() {
function test_commit_msg_no_block_but_alter (line 761) | fn test_commit_msg_no_block_but_alter() {
function test_hook_pwd_in_bare_without_workdir (line 779) | fn test_hook_pwd_in_bare_without_workdir() {
function test_hook_pwd (line 790) | fn test_hook_pwd() {
function test_hooks_prep_commit_msg_success (line 801) | fn test_hooks_prep_commit_msg_success() {
function test_hooks_prep_commit_msg_reject (line 825) | fn test_hooks_prep_commit_msg_reject() {
function test_pre_push_sh (line 861) | fn test_pre_push_sh() {
function test_pre_push_fail_sh (line 892) | fn test_pre_push_fail_sh() {
function test_pre_push_no_remote_name (line 926) | fn test_pre_push_no_remote_name() {
function test_pre_push_with_arguments (line 963) | fn test_pre_push_with_arguments() {
function test_pre_push_multiple_updates (line 1004) | fn test_pre_push_multiple_updates() {
function test_pre_push_delete_ref_uses_zero_oid (line 1066) | fn test_pre_push_delete_ref_uses_zero_oid() {
function test_pre_push_stdin (line 1104) | fn test_pre_push_stdin() {
function test_pre_push_uses_push_target_remote_not_upstream (line 1142) | fn test_pre_push_uses_push_target_remote_not_upstream() {
FILE: git2-testing/src/lib.rs
function repo_init_empty (line 7) | pub fn repo_init_empty() -> (TempDir, Repository) {
function repo_init (line 24) | pub fn repo_init() -> (TempDir, Repository) {
function init_log (line 49) | fn init_log() {
function repo_init_bare (line 57) | pub fn repo_init_bare() -> (TempDir, Repository) {
function sandbox_config_files (line 71) | fn sandbox_config_files() {
FILE: invalidstring/src/lib.rs
function invalid_utf8 (line 5) | pub fn invalid_utf8(prefix: &str) -> String {
FILE: scopetime/src/lib.rs
type ScopeTimeLog (line 10) | pub struct ScopeTimeLog<'a> {
function new (line 19) | pub fn new(
method drop (line 36) | fn drop(&mut self) {
FILE: src/app.rs
type QuitState (line 63) | pub enum QuitState {
type App (line 70) | pub struct App {
method new (line 155) | pub fn new(
method draw (line 258) | pub fn draw(&self, f: &mut Frame) -> Result<()> {
method event (line 306) | pub fn event(&mut self, ev: InputEvent) -> Result<()> {
method update (line 403) | pub fn update(&mut self) -> Result<()> {
method update_async (line 420) | pub fn update_async(
method is_quit (line 453) | pub fn is_quit(&self) -> bool {
method quit_state (line 459) | pub fn quit_state(&self) -> QuitState {
method any_work_pending (line 464) | pub fn any_work_pending(&self) -> bool {
method requires_redraw (line 483) | pub fn requires_redraw(&self) -> bool {
method check_quit (line 574) | fn check_quit(&mut self, ev: &Event) -> bool {
method check_hard_exit (line 587) | fn check_hard_exit(&mut self, ev: &Event) -> bool {
method get_tabs (line 597) | fn get_tabs(&mut self) -> Vec<&mut dyn Component> {
method toggle_tabs (line 607) | fn toggle_tabs(&mut self, reverse: bool) -> Result<()> {
method switch_tab (line 618) | fn switch_tab(&mut self, k: &KeyEvent) -> Result<()> {
method set_tab (line 634) | fn set_tab(&mut self, tab: usize) -> Result<()> {
method switch_to_tab (line 650) | fn switch_to_tab(&mut self, tab: &AppTabs) -> Result<()> {
method update_commands (line 661) | fn update_commands(&mut self) {
method process_queue (line 668) | fn process_queue(&mut self, flags: NeedsUpdate) -> Result<()> {
method open_popup (line 697) | fn open_popup(
method process_internal_events (line 722) | fn process_internal_events(&mut self) -> Result<NeedsUpdate> {
method process_internal_event (line 739) | fn process_internal_event(
method process_confirmed_action (line 947) | fn process_confirmed_action(
method delete_tag (line 1041) | fn delete_tag(&mut self, tag_name: String) -> Result<()> {
method delete_remote_branch (line 1060) | fn delete_remote_branch(
method delete_remote (line 1088) | fn delete_remote(&self, remote_name: &str) {
method commands (line 1106) | fn commands(&self, force_all: bool) -> Vec<CommandInfo> {
method draw_top_bar (line 1160) | fn draw_top_bar(&self, f: &mut Frame, r: Rect) {
type Environment (line 124) | pub struct Environment {
method test_env (line 137) | pub fn test_env() -> Self {
FILE: src/args.rs
constant BUG_REPORT_FLAG_ID (line 15) | const BUG_REPORT_FLAG_ID: &str = "bugreport";
constant LOG_FILE_FLAG_ID (line 16) | const LOG_FILE_FLAG_ID: &str = "logfile";
constant LOGGING_FLAG_ID (line 17) | const LOGGING_FLAG_ID: &str = "logging";
constant THEME_FLAG_ID (line 18) | const THEME_FLAG_ID: &str = "theme";
constant WORKDIR_FLAG_ID (line 19) | const WORKDIR_FLAG_ID: &str = "workdir";
constant FILE_FLAG_ID (line 20) | const FILE_FLAG_ID: &str = "file";
constant GIT_DIR_FLAG_ID (line 21) | const GIT_DIR_FLAG_ID: &str = "directory";
constant WATCHER_FLAG_ID (line 22) | const WATCHER_FLAG_ID: &str = "watcher";
constant KEY_BINDINGS_FLAG_ID (line 23) | const KEY_BINDINGS_FLAG_ID: &str = "key_bindings";
constant KEY_SYMBOLS_FLAG_ID (line 24) | const KEY_SYMBOLS_FLAG_ID: &str = "key_symbols";
constant DEFAULT_THEME (line 25) | const DEFAULT_THEME: &str = "theme.ron";
constant DEFAULT_GIT_DIR (line 26) | const DEFAULT_GIT_DIR: &str = ".";
type CliArgs (line 29) | pub struct CliArgs {
function process_cmdline (line 38) | pub fn process_cmdline() -> Result<CliArgs> {
function app (line 105) | fn app() -> ClapApp {
function setup_logging (line 195) | fn setup_logging(path_override: Option<PathBuf>) -> Result<()> {
function get_app_cache_path (line 215) | fn get_app_cache_path() -> Result<PathBuf> {
function get_app_config_path (line 229) | pub fn get_app_config_path() -> Result<PathBuf> {
function verify_app (line 242) | fn verify_app() {
FILE: src/bug_report.rs
function generate_bugreport (line 10) | pub fn generate_bugreport() {
FILE: src/clipboard.rs
function exec_copy_with_args (line 7) | fn exec_copy_with_args(
function is_wsl (line 56) | fn is_wsl() -> bool {
function copy_string_osc52 (line 74) | fn copy_string_osc52(text: &str, out: &mut impl Write) -> Result<()> {
function copy_string_wayland (line 87) | fn copy_string_wayland(text: &str) -> Result<()> {
function copy_string_x (line 96) | fn copy_string_x(text: &str) -> Result<()> {
function copy_string (line 118) | pub fn copy_string(text: &str) -> Result<()> {
function exec_copy (line 131) | fn exec_copy(command: &str, text: &str) -> Result<()> {
function copy_string (line 136) | pub fn copy_string(text: &str) -> Result<()> {
function copy_string (line 141) | pub fn copy_string(text: &str) -> Result<()> {
function test_copy_string_osc52 (line 148) | fn test_copy_string_osc52() {
FILE: src/cmdbar.rs
type DrawListEntry (line 14) | enum DrawListEntry {
type Command (line 20) | struct Command {
type CommandBar (line 26) | pub struct CommandBar {
method new (line 40) | pub const fn new(
method refresh_width (line 56) | pub fn refresh_width(&mut self, width: u16) {
method is_multiline (line 63) | fn is_multiline(&self, width: u16) -> bool {
method refresh_list (line 79) | fn refresh_list(&mut self, width: u16) {
method set_cmds (line 116) | pub fn set_cmds(&mut self, cmds: Vec<CommandInfo>) {
method height (line 125) | pub const fn height(&self) -> u16 {
method toggle_more (line 133) | pub const fn toggle_more(&mut self) {
method draw (line 139) | pub fn draw(&self, f: &mut Frame, r: Rect) {
constant MORE_WIDTH (line 37) | const MORE_WIDTH: u16 = 9;
FILE: src/components/changes.rs
type ChangesComponent (line 24) | pub struct ChangesComponent {
method new (line 35) | pub fn new(
method set_items (line 52) | pub fn set_items(&mut self, list: &[StatusItem]) -> Result<()> {
method selection (line 59) | pub fn selection(&self) -> Option<FileTreeItem> {
method focus_select (line 64) | pub fn focus_select(&mut self, focus: bool) {
method is_empty (line 70) | pub const fn is_empty(&self) -> bool {
method is_file_selected (line 75) | pub fn is_file_selected(&self) -> bool {
method index_add_remove (line 79) | fn index_add_remove(&self) -> Result<bool> {
method index_add_all (line 131) | fn index_add_all(&self) -> Result<()> {
method stage_remove_all (line 141) | fn stage_remove_all(&self) -> Result<()> {
method dispatch_reset_workdir (line 149) | fn dispatch_reset_workdir(&self) -> bool {
method add_to_ignore (line 162) | fn add_to_ignore(&self) -> bool {
method draw (line 187) | fn draw(&self, f: &mut Frame, r: Rect) -> Result<()> {
method commands (line 195) | fn commands(
method event (line 241) | fn event(&mut self, ev: &Event) -> Result<EventState> {
method focused (line 301) | fn focused(&self) -> bool {
method focus (line 305) | fn focus(&mut self, focus: bool) {
method is_visible (line 309) | fn is_visible(&self) -> bool {
method hide (line 313) | fn hide(&mut self) {
method show (line 317) | fn show(&mut self) -> Result<()> {
FILE: src/components/command.rs
type CommandText (line 5) | pub struct CommandText {
method new (line 18) | pub const fn new(
method hide_help (line 31) | pub const fn hide_help(self) -> Self {
type CommandInfo (line 39) | pub struct CommandInfo {
method new (line 55) | pub const fn new(
method order (line 70) | pub const fn order(self, order: i8) -> Self {
method hidden (line 77) | pub const fn hidden(self) -> Self {
method show_in_quickbar (line 84) | pub const fn show_in_quickbar(&self) -> bool {
FILE: src/components/commit_details/compare_details.rs
type CompareDetailsComponent (line 26) | pub struct CompareDetailsComponent {
method new (line 35) | pub fn new(env: &Environment, focused: bool) -> Self {
method set_commits (line 44) | pub fn set_commits(&mut self, ids: Option<OldNew<CommitId>>) {
method get_commit_text (line 61) | fn get_commit_text(&self, data: &CommitDetails) -> Vec<Line<'_>> {
method draw (line 103) | fn draw(&self, f: &mut Frame, rect: Rect) -> Result<()> {
method commands (line 145) | fn commands(
method event (line 153) | fn event(&mut self, _event: &Event) -> Result<EventState> {
method focused (line 157) | fn focused(&self) -> bool {
method focus (line 161) | fn focus(&mut self, focus: bool) {
FILE: src/components/commit_details/details.rs
type DetailsComponent (line 30) | pub struct DetailsComponent {
method new (line 47) | pub fn new(env: &Environment, focused: bool) -> Self {
method set_commit (line 61) | pub fn set_commit(
method wrap_commit_details (line 79) | fn wrap_commit_details(
method get_wrapped_lines (line 104) | fn get_wrapped_lines(
method get_number_of_lines (line 117) | fn get_number_of_lines(
method get_theme_for_line (line 127) | fn get_theme_for_line(&self, bold: bool) -> Style {
method get_wrapped_text_message (line 135) | fn get_wrapped_text_message(
method get_text_info (line 159) | fn get_text_info(&self) -> Vec<Line<'_>> {
method move_scroll_top (line 247) | fn move_scroll_top(&self, move_type: ScrollType) -> bool {
type WrappedCommitMessage (line 42) | type WrappedCommitMessage<'a> =
method draw (line 257) | fn draw(&self, f: &mut Frame, rect: Rect) -> Result<()> {
method commands (line 338) | fn commands(
method event (line 361) | fn event(&mut self, event: &Event) -> Result<EventState> {
method focused (line 404) | fn focused(&self) -> bool {
method focus (line 408) | fn focus(&mut self, focus: bool) {
function get_wrapped_lines (line 423) | fn get_wrapped_lines(
function test_textwrap (line 434) | fn test_textwrap() {
function test_smoke (line 496) | fn test_smoke() {
FILE: src/components/commit_details/mod.rs
type CommitDetailsComponent (line 28) | pub struct CommitDetailsComponent {
method new (line 42) | pub fn new(env: &Environment) -> Self {
method get_files_title (line 57) | fn get_files_title(&self) -> String {
method set_commits (line 68) | pub fn set_commits(
method any_work_pending (line 114) | pub fn any_work_pending(&self) -> bool {
method files (line 119) | pub const fn files(&self) -> &StatusTreeComponent {
method details_focused (line 123) | fn details_focused(&self) -> bool {
method set_details_focus (line 128) | fn set_details_focus(&mut self, focus: bool) {
method is_compare (line 136) | fn is_compare(&self) -> bool {
method draw (line 142) | fn draw(&self, f: &mut Frame, rect: Rect) -> Result<()> {
method commands (line 182) | fn commands(
method event (line 198) | fn event(&mut self, ev: &Event) -> Result<EventState> {
method is_visible (line 233) | fn is_visible(&self) -> bool {
method hide (line 236) | fn hide(&mut self) {
method show (line 239) | fn show(&mut self) -> Result<()> {
method focused (line 245) | fn focused(&self) -> bool {
method focus (line 249) | fn focus(&mut self, focus: bool) {
FILE: src/components/commit_details/style.rs
type Detail (line 5) | pub enum Detail {
function style_detail (line 13) | pub fn style_detail<'a>(
FILE: src/components/commitlist.rs
constant ELEMENTS_PER_LINE (line 36) | const ELEMENTS_PER_LINE: usize = 9;
constant SLICE_SIZE (line 37) | const SLICE_SIZE: usize = 1200;
type CommitList (line 40) | pub struct CommitList {
method new (line 65) | pub fn new(env: &Environment, title: &str) -> Self {
method tags (line 88) | pub const fn tags(&self) -> Option<&Tags> {
method clear (line 93) | pub fn clear(&mut self) {
method copy_items (line 99) | pub fn copy_items(&self) -> Vec<CommitId> {
method set_tags (line 104) | pub fn set_tags(&mut self, tags: Tags) {
method selected_entry (line 109) | pub fn selected_entry(&self) -> Option<&LogEntry> {
method marked_count (line 116) | pub const fn marked_count(&self) -> usize {
method clear_marked (line 121) | pub fn clear_marked(&mut self) {
method marked_commits (line 126) | pub fn marked_commits(&self) -> Vec<CommitId> {
method concat_selected_commit_ids (line 134) | fn concat_selected_commit_ids(&self) -> Option<String> {
method copy_commit_hash (line 155) | pub fn copy_commit_hash(&self) -> Result<()> {
method checkout (line 166) | pub fn checkout(&self) {
method set_local_branches (line 179) | pub fn set_local_branches(
method set_remote_branches (line 194) | pub fn set_remote_branches(
method set_commits (line 209) | pub fn set_commits(&mut self, commits: IndexSet<CommitId>) {
method refresh_extend_data (line 218) | pub fn refresh_extend_data(&mut self, commits: Vec<CommitId>) {
method set_highlighting (line 231) | pub fn set_highlighting(
method select_commit (line 251) | pub fn select_commit(&mut self, id: CommitId) -> Result<()> {
method highlighted_selection_info (line 264) | pub fn highlighted_selection_info(&self) -> (usize, usize) {
method set_highlighted_selection_index (line 273) | fn set_highlighted_selection_index(&mut self) {
method selection (line 282) | const fn selection(&self) -> usize {
method current_size (line 287) | const fn current_size(&self) -> Option<(u16, u16)> {
method selection_max (line 291) | fn selection_max(&self) -> usize {
method selected_entry_marked (line 295) | fn selected_entry_marked(&self) -> bool {
method move_selection (line 301) | fn move_selection(&mut self, scroll: ScrollType) -> Result<bool> {
method move_selection_highlighting (line 311) | fn move_selection_highlighting(
method move_selection_normal (line 350) | fn move_selection_normal(
method mark (line 390) | fn mark(&mut self) {
method update_scroll_speed (line 408) | fn update_scroll_speed(&mut self) {
method is_marked (line 432) | fn is_marked(&self, id: &CommitId) -> Option<bool> {
method get_entry_to_add (line 443) | fn get_entry_to_add<'a>(
method get_text (line 568) | fn get_text(&self, height: usize, width: usize) -> Vec<Line<'_>> {
method remote_branches_string (line 625) | fn remote_branches_string(&self, e: &LogEntry) -> Option<String> {
method relative_selection (line 668) | fn relative_selection(&self) -> usize {
method select_next_highlight (line 672) | fn select_next_highlight(&mut self) {
method selection_highlighted (line 710) | fn selection_highlighted(&self) -> bool {
method needs_data (line 718) | fn needs_data(&self, idx: usize, idx_max: usize) -> bool {
method is_list_in_sync (line 723) | fn is_list_in_sync(&self) -> bool {
method fetch_commits (line 735) | fn fetch_commits(&mut self, force: bool) {
method draw (line 774) | fn draw(&self, f: &mut Frame, area: Rect) -> Result<()> {
method event (line 831) | fn event(&mut self, ev: &Event) -> Result<EventState> {
method commands (line 875) | fn commands(
method default (line 904) | fn default() -> Self {
function test_string_width_align (line 930) | fn test_string_width_align() {
function test_string_width_align_unicode (line 939) | fn test_string_width_align_unicode() {
function build_commit_list_with_some_commits (line 952) | fn build_commit_list_with_some_commits() -> CommitList {
function build_marked_from_indices (line 986) | fn build_marked_from_indices(
function test_copy_commit_list_empty (line 1000) | fn test_copy_commit_list_empty() {
function test_copy_commit_none_marked (line 1008) | fn test_copy_commit_none_marked() {
function test_copy_commit_one_marked (line 1025) | fn test_copy_commit_one_marked() {
function test_copy_commit_range_marked (line 1040) | fn test_copy_commit_range_marked() {
function test_copy_commit_random_marked (line 1058) | fn test_copy_commit_random_marked() {
FILE: src/components/cred.rs
type CredComponent (line 20) | pub struct CredComponent {
method new (line 30) | pub fn new(env: &Environment) -> Self {
method set_cred (line 53) | pub fn set_cred(&mut self, cred: BasicAuthCredential) {
method get_cred (line 57) | pub const fn get_cred(&self) -> &BasicAuthCredential {
method draw (line 63) | fn draw(&self, f: &mut Frame, rect: Rect) -> Result<()> {
method commands (line 73) | fn commands(
method event (line 98) | fn event(&mut self, ev: &Event) -> Result<EventState> {
method is_visible (line 143) | fn is_visible(&self) -> bool {
method hide (line 147) | fn hide(&mut self) {
method show (line 152) | fn show(&mut self) -> Result<()> {
FILE: src/components/diff.rs
type Current (line 35) | struct Current {
type Selection (line 43) | enum Selection {
method get_start (line 49) | const fn get_start(&self) -> usize {
method get_end (line 55) | const fn get_end(&self) -> usize {
method get_top (line 61) | fn get_top(&self) -> usize {
method get_bottom (line 68) | fn get_bottom(&self) -> usize {
method modify (line 75) | fn modify(&mut self, direction: Direction, max: usize) {
method contains (line 90) | fn contains(&self, index: usize) -> bool {
type DiffComponent (line 105) | pub struct DiffComponent {
method new (line 126) | pub fn new(env: &Environment, is_immutable: bool) -> Self {
method can_scroll (line 147) | fn can_scroll(&self) -> bool {
method current (line 151) | pub fn current(&self) -> (String, bool) {
method clear (line 155) | pub fn clear(&mut self, pending: bool) {
method update (line 166) | pub fn update(
method move_selection (line 220) | fn move_selection(&mut self, move_type: ScrollType) {
method update_selection (line 251) | fn update_selection(&mut self, new_start: usize) {
method lines_count (line 261) | fn lines_count(&self) -> usize {
method max_scroll_right (line 265) | fn max_scroll_right(&self) -> usize {
method modify_selection (line 270) | fn modify_selection(&mut self, direction: Direction) {
method copy_selection (line 276) | fn copy_selection(&self) {
method find_selected_hunk (line 304) | fn find_selected_hunk(
method get_text (line 327) | fn get_text(&self, width: u16, height: u16) -> Vec<Line<'_>> {
method get_text_binary (line 390) | fn get_text_binary(&self, diff: &FileDiff) -> Vec<Line<'_>> {
method get_line_to_add (line 422) | fn get_line_to_add<'a>(
method hunk_visible (line 476) | const fn hunk_visible(
method unstage_hunk (line 497) | fn unstage_hunk(&self) -> Result<()> {
method stage_hunk (line 514) | fn stage_hunk(&self) -> Result<()> {
method queue_update (line 539) | fn queue_update(&self) {
method reset_hunk (line 543) | fn reset_hunk(&self) {
method reset_lines (line 558) | fn reset_lines(&self) {
method stage_lines (line 567) | fn stage_lines(&self) {
method selected_lines (line 589) | fn selected_lines(&self) -> Vec<DiffLinePosition> {
method reset_untracked (line 614) | fn reset_untracked(&self) {
method stage_unstage_hunk (line 622) | fn stage_unstage_hunk(&self) -> Result<()> {
method calc_hunk_move_target (line 632) | fn calc_hunk_move_target(
method diff_hunk_move_up_down (line 652) | fn diff_hunk_move_up_down(&mut self, direction: isize) {
method is_stage (line 676) | const fn is_stage(&self) -> bool {
method draw (line 682) | fn draw(&self, f: &mut Frame, r: Rect) -> Result<()> {
method commands (line 743) | fn commands(
method event (line 822) | fn event(&mut self, ev: &Event) -> Result<EventState> {
method focused (line 937) | fn focused(&self) -> bool {
method focus (line 940) | fn focus(&mut self, focus: bool) {
function test_line_break (line 954) | fn test_line_break() {
FILE: src/components/mod.rs
function event_pump (line 142) | pub fn event_pump(
function command_pump (line 158) | pub fn command_pump(
type ScrollType (line 173) | pub enum ScrollType {
type HorizontalScrollType (line 183) | pub enum HorizontalScrollType {
type Direction (line 189) | pub enum Direction {
type CommandBlocking (line 196) | pub enum CommandBlocking {
function visibility_blocking (line 202) | pub fn visibility_blocking<T: Component>(
type DrawableComponent (line 213) | pub trait DrawableComponent {
method draw (line 215) | fn draw(&self, f: &mut Frame, rect: Rect) -> Result<()>;
type EventState (line 220) | pub enum EventState {
method is_consumed (line 232) | pub fn is_consumed(&self) -> bool {
method from (line 238) | fn from(consumed: bool) -> Self {
type FuzzyFinderTarget (line 226) | pub enum FuzzyFinderTarget {
type Component (line 248) | pub trait Component {
method commands (line 250) | fn commands(
method event (line 257) | fn event(&mut self, ev: &Event) -> Result<EventState>;
method focused (line 260) | fn focused(&self) -> bool {
method focus (line 264) | fn focus(&mut self, _focus: bool) {}
method is_visible (line 266) | fn is_visible(&self) -> bool {
method hide (line 270) | fn hide(&mut self) {}
method show (line 272) | fn show(&mut self) -> Result<()> {
method toggle_visible (line 277) | fn toggle_visible(&mut self) -> Result<()> {
function dialog_paragraph (line 287) | fn dialog_paragraph<'a>(
FILE: src/components/revision_files.rs
type Focus (line 40) | enum Focus {
type RevisionFilesComponent (line 45) | pub struct RevisionFilesComponent {
method new (line 64) | pub fn new(
method set_commit (line 88) | pub fn set_commit(&mut self, commit: CommitId) -> Result<()> {
method revision (line 107) | pub const fn revision(&self) -> Option<&CommitInfo> {
method update (line 112) | pub fn update(&mut self, ev: AsyncNotification) -> Result<()> {
method refresh_files (line 125) | fn refresh_files(&mut self) -> Result<(), anyhow::Error> {
method any_work_pending (line 162) | pub fn any_work_pending(&self) -> bool {
method tree_item_to_span (line 167) | fn tree_item_to_span<'a>(
method blame (line 203) | fn blame(&self) -> bool {
method file_history (line 217) | fn file_history(&self) -> bool {
method open_finder (line 229) | fn open_finder(&self) {
method find_file (line 246) | pub fn find_file(&mut self, file: &Path) {
method selected_file_path_with_prefix (line 253) | fn selected_file_path_with_prefix(&self) -> Option<String> {
method selected_file_path (line 259) | fn selected_file_path(&self) -> Option<String> {
method selection_changed (line 268) | fn selection_changed(&mut self) {
method draw_tree (line 288) | fn draw_tree(&self, f: &mut Frame, area: Rect) -> Result<()> {
method title_within (line 354) | fn title_within(&self, tree_width: usize) -> Result<String> {
method request_files (line 389) | fn request_files(&self, commit: CommitId) {
method draw (line 398) | fn draw(&self, f: &mut Frame, area: Rect) -> Result<()> {
method commands (line 420) | fn commands(
method event (line 471) | fn event(
method hide (line 548) | fn hide(&mut self) {
method is_visible (line 552) | fn is_visible(&self) -> bool {
method show (line 556) | fn show(&mut self) -> Result<()> {
function tree_nav_cmds (line 564) | fn tree_nav_cmds(
function tree_nav (line 580) | fn tree_nav(
FILE: src/components/status_tree.rs
type StatusTreeComponent (line 27) | pub struct StatusTreeComponent {
method new (line 44) | pub fn new(env: &Environment, title: &str, focus: bool) -> Self {
method set_commit (line 61) | pub const fn set_commit(&mut self, revision: Option<CommitId>) {
method update (line 66) | pub fn update(&mut self, list: &[StatusItem]) -> Result<()> {
method selection (line 79) | pub fn selection(&self) -> Option<FileTreeItem> {
method selection_file (line 84) | pub fn selection_file(&self) -> Option<StatusItem> {
method show_selection (line 95) | pub const fn show_selection(&mut self, show: bool) {
method is_empty (line 100) | pub const fn is_empty(&self) -> bool {
method file_count (line 105) | pub const fn file_count(&self) -> usize {
method set_title (line 110) | pub fn set_title(&mut self, title: String) {
method clear (line 115) | pub fn clear(&mut self) -> Result<()> {
method is_file_selected (line 122) | pub fn is_file_selected(&self) -> bool {
method move_selection (line 131) | fn move_selection(&mut self, dir: MoveSelection) -> bool {
method item_status_char (line 141) | const fn item_status_char(item_type: StatusItemType) -> char {
method item_to_text (line 152) | fn item_to_text<'b>(
method build_vec_text_draw_info_for_drawing (line 224) | fn build_vec_text_draw_info_for_drawing(
method copy_file_path (line 300) | fn copy_file_path(&self) {
method open_history (line 312) | fn open_history(&mut self) {
type TextDrawInfo (line 333) | struct TextDrawInfo<'a> {
method draw (line 341) | fn draw(&self, f: &mut Frame, r: Rect) -> Result<()> {
method commands (line 412) | fn commands(
method event (line 474) | fn event(&mut self, ev: &Event) -> Result<EventState> {
method focused (line 568) | fn focused(&self) -> bool {
method focus (line 571) | fn focus(&mut self, focus: bool) {
method is_visible (line 576) | fn is_visible(&self) -> bool {
method hide (line 580) | fn hide(&mut self) {
method show (line 584) | fn show(&mut self) -> Result<()> {
function string_vec_to_status (line 594) | fn string_vec_to_status(items: &[&str]) -> Vec<StatusItem> {
function test_correct_scroll_position (line 605) | fn test_correct_scroll_position() {
function test_correct_foldup_and_not_visible_scroll_position (line 646) | fn test_correct_foldup_and_not_visible_scroll_position() {
FILE: src/components/syntax_text.rs
type SyntaxTextComponent (line 33) | pub struct SyntaxTextComponent {
method new (line 46) | pub fn new(env: &Environment) -> Self {
method update (line 62) | pub fn update(&mut self, ev: AsyncNotification) {
method any_work_pending (line 93) | pub fn any_work_pending(&self) -> bool {
method clear (line 98) | pub fn clear(&mut self) {
method load_file (line 103) | pub fn load_file(&mut self, path: String, item: &TreeFile) {
method scroll (line 139) | fn scroll(&self, nav: MoveSelection) -> bool {
method set_scroll (line 163) | fn set_scroll(&self, pos: u16) -> bool {
method draw (line 187) | fn draw(&self, f: &mut Frame, area: Rect) -> Result<()> {
method commands (line 242) | fn commands(
method event (line 260) | fn event(
method focused (line 278) | fn focused(&self) -> bool {
method focus (line 283) | fn focus(&mut self, focus: bool) {
FILE: src/components/textinput.rs
type InputType (line 27) | pub enum InputType {
type SelectionState (line 34) | enum SelectionState {
type TextAreaComponent (line 40) | type TextAreaComponent = TextArea<'static>;
type TextInputComponent (line 43) | pub struct TextInputComponent {
method new (line 60) | pub fn new(
method with_input_type (line 83) | pub const fn with_input_type(
method set_input_type (line 92) | pub fn set_input_type(&mut self, input_type: InputType) {
method clear (line 98) | pub fn clear(&mut self) {
method get_text (line 106) | pub fn get_text(&self) -> &str {
method get_area (line 125) | pub const fn get_area(&self) -> Rect {
method embed (line 130) | pub const fn embed(&mut self) {
method enabled (line 135) | pub const fn enabled(&mut self, enable: bool) {
method show_inner_textarea (line 139) | fn show_inner_textarea(&mut self) {
method set_text (line 185) | pub fn set_text(&mut self, msg: String) {
method set_title (line 193) | pub fn set_title(&mut self, t: String) {
method set_default_msg (line 198) | pub fn set_default_msg(&mut self, v: String) {
method draw_char_count (line 205) | fn draw_char_count(&self, f: &mut Frame, r: Rect) {
method should_select (line 227) | fn should_select(&mut self, input: &Input) {
method process_inputs (line 263) | fn process_inputs(ta: &mut TextArea<'_>, input: &Input) -> bool {
method draw (line 619) | fn draw(&self, f: &mut Frame, rect: Rect) -> Result<()> {
method commands (line 657) | fn commands(
method event (line 676) | fn event(&mut self, ev: &Event) -> Result<EventState> {
method is_visible (line 719) | fn is_visible(&self) -> bool {
method hide (line 723) | fn hide(&mut self) {
method show (line 727) | fn show(&mut self) -> Result<()> {
function test_smoke (line 738) | fn test_smoke() {
function text_cursor_initial_position (line 756) | fn text_cursor_initial_position() {
function test_multiline (line 770) | fn test_multiline() {
function test_next_word_position (line 785) | fn test_next_word_position() {
function test_previous_word_position (line 814) | fn test_previous_word_position() {
function test_next_word_multibyte (line 845) | fn test_next_word_multibyte() {
FILE: src/components/utils/emoji.rs
function emojifi_string (line 10) | pub fn emojifi_string(s: String) -> String {
FILE: src/components/utils/filetree.rs
type TreeItemInfo (line 14) | pub struct TreeItemInfo {
method new (line 26) | const fn new(
type PathCollapsed (line 42) | pub struct PathCollapsed(pub bool);
type FileTreeItemKind (line 46) | pub enum FileTreeItemKind {
type FileTreeItem (line 53) | pub struct FileTreeItem {
method new_file (line 59) | fn new_file(item: &StatusItem) -> Result<Self> {
method new_path (line 83) | fn new_path(
method eq (line 112) | fn eq(&self, other: &Self) -> bool {
method partial_cmp (line 118) | fn partial_cmp(
method cmp (line 127) | fn cmp(&self, other: &Self) -> std::cmp::Ordering {
type FileTreeItems (line 134) | pub struct FileTreeItems {
method new (line 141) | pub(crate) fn new(
method items (line 170) | pub(crate) const fn items(&self) -> &Vec<FileTreeItem> {
method len (line 175) | pub(crate) const fn len(&self) -> usize {
method file_count (line 180) | pub const fn file_count(&self) -> usize {
method find_parent_index (line 185) | pub(crate) fn find_parent_index(&self, index: usize) -> usize {
method push_dirs (line 198) | fn push_dirs<'a>(
method multiple_items_at_path (line 226) | pub fn multiple_items_at_path(&self, index: usize) -> bool {
method index_mut (line 247) | fn index_mut(&mut self, idx: usize) -> &mut Self::Output {
type Output (line 253) | type Output = FileTreeItem;
method index (line 255) | fn index(&self, idx: usize) -> &Self::Output {
function string_vec_to_status (line 265) | fn string_vec_to_status(items: &[&str]) -> Vec<StatusItem> {
function test_simple (line 276) | fn test_simple() {
function test_folder (line 310) | fn test_folder() {
function test_indent (line 329) | fn test_indent() {
function test_indent_folder_file_name (line 347) | fn test_indent_folder_file_name() {
function test_folder_dup (line 366) | fn test_folder_dup() {
function test_multiple_items_at_path (line 390) | fn test_multiple_items_at_path() {
function test_find_parent (line 413) | fn test_find_parent() {
FILE: src/components/utils/logitems.rs
type BoxStr (line 11) | type BoxStr = Box<str>;
type LogEntry (line 13) | pub struct LogEntry {
method from (line 26) | fn from(c: CommitInfo) -> Self {
method time_to_string (line 62) | pub fn time_to_string(&self, now: DateTime<Local>) -> String {
type ItemBatch (line 83) | pub struct ItemBatch {
method last_idx (line 90) | fn last_idx(&self) -> usize {
method index_offset (line 95) | pub fn index_offset(&self) -> usize {
method index_offset_raw (line 100) | pub const fn index_offset_raw(&self) -> Option<usize> {
method highlighting (line 105) | pub const fn highlighting(&self) -> bool {
method iter (line 110) | pub fn iter(&self) -> Iter<'_, LogEntry> {
method clear (line 115) | pub fn clear(&mut self) {
method set_items (line 121) | pub fn set_items(
method needs_data (line 147) | pub fn needs_data(&self, idx: usize, idx_max: usize) -> bool {
type IntoIter (line 161) | type IntoIter = std::slice::Iter<
type Item (line 165) | type Item = &'a crate::components::utils::logitems::LogEntry;
method into_iter (line 166) | fn into_iter(self) -> Self::IntoIter {
function test_conversion (line 176) | fn test_conversion(s: &str) -> String {
function test_emojifi_string_conversion_cases (line 181) | fn test_emojifi_string_conversion_cases() {
function test_emojifi_string_no_conversion_cases (line 198) | fn test_emojifi_string_no_conversion_cases() {
FILE: src/components/utils/mod.rs
function time_to_string (line 30) | pub fn time_to_string(secs: i64, short: bool) -> String {
function string_width_align (line 49) | pub fn string_width_align(s: &str, width: usize) -> String {
function find_truncate_point (line 67) | fn find_truncate_point(s: &str, chars: usize) -> usize {
FILE: src/components/utils/scroll_horizontal.rs
type HorizontalScroll (line 8) | pub struct HorizontalScroll {
method new (line 14) | pub const fn new() -> Self {
method get_right (line 21) | pub const fn get_right(&self) -> usize {
method reset (line 25) | pub fn reset(&self) {
method move_right (line 29) | pub fn move_right(
method update (line 52) | pub fn update(
method update_no_selection (line 77) | pub fn update_no_selection(
method draw (line 85) | pub fn draw(&self, f: &mut Frame, r: Rect, theme: &SharedTheme) {
function calc_scroll_right (line 97) | const fn calc_scroll_right(
function test_scroll_no_scroll_to_right (line 125) | fn test_scroll_no_scroll_to_right() {
function test_scroll_zero_width (line 130) | fn test_scroll_zero_width() {
FILE: src/components/utils/scroll_vertical.rs
type VerticalScroll (line 8) | pub struct VerticalScroll {
method new (line 15) | pub const fn new() -> Self {
method get_top (line 23) | pub const fn get_top(&self) -> usize {
method reset (line 27) | pub fn reset(&self) {
method move_top (line 31) | pub fn move_top(&self, move_type: ScrollType) -> bool {
method move_area_to_visible (line 59) | pub fn move_area_to_visible(
method update (line 85) | pub fn update(
method update_no_selection (line 111) | pub fn update_no_selection(
method draw (line 119) | pub fn draw(&self, f: &mut Frame, r: Rect, theme: &SharedTheme) {
function calc_scroll_top (line 131) | const fn calc_scroll_top(
function test_scroll_no_scroll_to_top (line 159) | fn test_scroll_no_scroll_to_top() {
function test_scroll_zero_height (line 164) | fn test_scroll_zero_height() {
function test_scroll_bottom_into_view (line 169) | fn test_scroll_bottom_into_view() {
function test_scroll_top_into_view (line 189) | fn test_scroll_top_into_view() {
function test_scroll_with_pageup_pagedown (line 206) | fn test_scroll_with_pageup_pagedown() {
FILE: src/components/utils/statustree.rs
type StatusTree (line 12) | pub struct StatusTree {
method update (line 49) | pub fn update(&mut self, list: &[StatusItem]) -> Result<()> {
method setup_available_selections (line 83) | fn setup_available_selections(&self) -> Vec<usize> {
method find_visible_idx (line 122) | fn find_visible_idx(&self, mut idx: usize) -> usize {
method move_selection (line 135) | pub fn move_selection(&mut self, dir: MoveSelection) -> bool {
method selected_item (line 171) | pub fn selected_item(&self) -> Option<FileTreeItem> {
method is_empty (line 176) | pub const fn is_empty(&self) -> bool {
method all_collapsed (line 180) | fn all_collapsed(&self) -> BTreeSet<&String> {
method find_last_selection (line 196) | fn find_last_selection(
method selection_updown (line 214) | fn selection_updown(
method selection_end (line 278) | fn selection_end(&self) -> SelectionChange {
method selection_page_updown (line 299) | fn selection_page_updown(
method is_visible_index (line 318) | fn is_visible_index(&self, idx: usize) -> bool {
method selection_right (line 322) | fn selection_right(
method selection_left (line 349) | fn selection_left(
method collapse (line 379) | fn collapse(&mut self, path: &str, index: usize) {
method expand (line 400) | fn expand(&mut self, path: &str, current_index: usize) {
method update_visibility (line 417) | fn update_visibility(
type MoveSelection (line 25) | pub enum MoveSelection {
type SelectionChange (line 37) | struct SelectionChange {
method new (line 42) | const fn new(new_index: usize, changes: bool) -> Self {
function string_vec_to_status (line 469) | fn string_vec_to_status(items: &[&str]) -> Vec<StatusItem> {
function get_visible (line 479) | fn get_visible(tree: &StatusTree) -> Vec<bool> {
function test_selection (line 488) | fn test_selection() {
function test_keep_selected_item (line 506) | fn test_keep_selected_item() {
function test_keep_selected_index (line 518) | fn test_keep_selected_index() {
function test_keep_selected_index_if_not_collapsed (line 528) | fn test_keep_selected_index_if_not_collapsed() {
function test_keep_collapsed_states (line 549) | fn test_keep_collapsed_states() {
function test_expand (line 597) | fn test_expand() {
function test_expand_bug (line 641) | fn test_expand_bug() {
function test_collapse_too_much (line 685) | fn test_collapse_too_much() {
function test_expand_with_collapsed_sub_parts (line 715) | fn test_expand_with_collapsed_sub_parts() {
function test_selection_skips_collapsed (line 773) | fn test_selection_skips_collapsed() {
function test_folders_fold_up_if_alone_in_directory (line 795) | fn test_folders_fold_up_if_alone_in_directory() {
function test_folders_fold_up_if_alone_in_directory_2 (line 845) | fn test_folders_fold_up_if_alone_in_directory_2() {
function test_folders_fold_up_down_with_selection_left_right (line 869) | fn test_folders_fold_up_down_with_selection_left_right() {
FILE: src/input.rs
type InputState (line 19) | pub enum InputState {
type InputEvent (line 26) | pub enum InputEvent {
type Input (line 33) | pub struct Input {
method new (line 42) | pub fn new() -> Self {
method receiver (line 71) | pub fn receiver(&self) -> Receiver<InputEvent> {
method set_polling (line 76) | pub fn set_polling(&self, enabled: bool) {
method shall_poll (line 80) | fn shall_poll(&self) -> bool {
method is_state_changing (line 85) | pub fn is_state_changing(&self) -> bool {
method is_aborted (line 90) | pub fn is_aborted(&self) -> bool {
method poll (line 94) | fn poll(dur: Duration) -> anyhow::Result<Option<Event>> {
method input_loop (line 102) | fn input_loop(
FILE: src/keys/key_config.rs
type SharedKeyConfig (line 12) | pub type SharedKeyConfig = Rc<KeyConfig>;
constant KEY_LIST_FILENAME (line 13) | const KEY_LIST_FILENAME: &str = "key_bindings.ron";
constant KEY_SYMBOLS_FILENAME (line 14) | const KEY_SYMBOLS_FILENAME: &str = "key_symbols.ron";
type KeyConfig (line 17) | pub struct KeyConfig {
method get_config_file (line 23) | fn get_config_file() -> Result<PathBuf> {
method get_symbols_file (line 30) | fn get_symbols_file() -> Result<PathBuf> {
method init (line 37) | pub fn init(
method get_key_symbol (line 55) | fn get_key_symbol(&self, k: KeyCode) -> &str {
method get_hint (line 76) | pub fn get_hint(&self, ev: GituiKeyEvent) -> String {
method get_modifier_hint (line 121) | fn get_modifier_hint(&self, modifier: KeyModifiers) -> &str {
function test_get_hint (line 139) | fn test_get_hint() {
function test_symbolic_links (line 149) | fn test_symbolic_links() {
function create_symlink (line 271) | fn create_symlink<
function create_symlink (line 282) | fn create_symlink<
FILE: src/keys/key_list.rs
type GituiKeyEvent (line 8) | pub struct GituiKeyEvent {
method new (line 14) | pub const fn new(code: KeyCode, modifiers: KeyModifiers) -> Self {
function key_match (line 19) | pub fn key_match(ev: &KeyEvent, binding: GituiKeyEvent) -> bool {
method eq (line 24) | fn eq(&self, other: &Self) -> bool {
method from (line 32) | fn from(other: &GituiKeyEvent) -> Self {
type KeysList (line 39) | pub struct KeysList {
method init (line 235) | pub fn init(file: PathBuf) -> Self {
method default (line 136) | fn default() -> Self {
function test_apply_vim_style_example (line 257) | fn test_apply_vim_style_example() {
function test_smoke (line 267) | fn test_smoke() {
FILE: src/keys/symbols.rs
type KeySymbols (line 7) | pub struct KeySymbols {
method init (line 55) | pub fn init(file: PathBuf) -> Self {
method default (line 30) | fn default() -> Self {
type KeySymbolsFile (line 68) | pub struct KeySymbolsFile {
method read_file (line 90) | fn read_file(config_file: PathBuf) -> Result<Self> {
method get_symbols (line 97) | pub fn get_symbols(self) -> KeySymbols {
FILE: src/main.rs
type Terminal (line 116) | type Terminal = ratatui::Terminal<CrosstermBackend<io::Stdout>>;
type QueueEvent (line 123) | pub enum QueueEvent {
type SyntaxHighlightProgress (line 132) | pub enum SyntaxHighlightProgress {
type AsyncAppNotification (line 138) | pub enum AsyncAppNotification {
type AsyncNotification (line 144) | pub enum AsyncNotification {
type Updater (line 152) | enum Updater {
function main (line 165) | fn main() -> Result<()> {
function run_app (line 229) | fn run_app(
function setup_terminal (line 335) | fn setup_terminal() -> Result<()> {
function shutdown_terminal (line 341) | fn shutdown_terminal() {
function draw (line 356) | fn draw(terminal: &mut Terminal, app: &App) -> io::Result<()> {
function ensure_valid_path (line 370) | fn ensure_valid_path(repo_path: &RepoPath) -> Result<()> {
function select_event (line 380) | fn select_event(
function start_terminal (line 417) | fn start_terminal(
function set_panic_handler (line 445) | fn set_panic_handler() -> Result<()> {
FILE: src/notify_mutex.rs
type NotifiableMutex (line 5) | pub struct NotifiableMutex<T>
function new (line 17) | pub fn new(start_value: T) -> Self {
function wait (line 24) | pub fn wait(&self, condition: T)
function set_and_notify (line 36) | pub fn set_and_notify(&self, value: T) {
function get (line 42) | pub fn get(&self) -> T
FILE: src/options.rs
type OptionsData (line 20) | struct OptionsData {
constant COMMIT_MSG_HISTORY_LENGTH (line 27) | const COMMIT_MSG_HISTORY_LENGTH: usize = 20;
type Options (line 30) | pub struct Options {
method test_env (line 37) | pub fn test_env() -> Self {
method new (line 49) | pub fn new(repo: RepoPathRef) -> SharedOptions {
method set_current_tab (line 56) | pub fn set_current_tab(&mut self, tab: usize) {
method current_tab (line 61) | pub const fn current_tab(&self) -> usize {
method diff_options (line 65) | pub const fn diff_options(&self) -> DiffOptions {
method status_show_untracked (line 69) | pub const fn status_show_untracked(
method set_status_show_untracked (line 75) | pub fn set_status_show_untracked(
method diff_context_change (line 83) | pub fn diff_context_change(&mut self, increase: bool) {
method diff_hunk_lines_change (line 93) | pub fn diff_hunk_lines_change(&mut self, increase: bool) {
method diff_toggle_whitespace (line 103) | pub fn diff_toggle_whitespace(&mut self) {
method add_commit_msg (line 110) | pub fn add_commit_msg(&mut self, msg: &str) {
method has_commit_msg_history (line 119) | pub const fn has_commit_msg_history(&self) -> bool {
method commit_msg (line 123) | pub fn commit_msg(&self, idx: usize) -> Option<String> {
method save (line 140) | fn save(&self) {
method read (line 146) | fn read(repo: &RepoPathRef) -> Result<OptionsData> {
method save_failable (line 155) | fn save_failable(&self) -> Result<()> {
method options_file (line 166) | fn options_file(repo: &RepoPathRef) -> Result<PathBuf> {
type SharedOptions (line 46) | pub type SharedOptions = Rc<RefCell<Options>>;
FILE: src/popup_stack.rs
type PopupStack (line 4) | pub struct PopupStack {
method push (line 9) | pub fn push(&mut self, popup: StackablePopupOpen) {
method pop (line 13) | pub fn pop(&mut self) -> Option<StackablePopupOpen> {
FILE: src/popups/blame_file.rs
type SyntaxFileBlame (line 38) | struct SyntaxFileBlame {
method path (line 44) | fn path(&self) -> &str {
method commit_id (line 48) | const fn commit_id(&self) -> &CommitId {
method lines (line 52) | const fn lines(&self) -> &Vec<(Option<BlameHunk>, String)> {
type BlameProcess (line 57) | enum BlameProcess {
method result (line 67) | const fn result(&self) -> Option<&SyntaxFileBlame> {
type BlameFileOpen (line 80) | pub struct BlameFileOpen {
type BlameFilePopup (line 86) | pub struct BlameFilePopup {
method new (line 358) | pub fn new(env: &Environment, title: &str) -> Self {
method hide_stacked (line 376) | fn hide_stacked(&mut self, stack: bool) {
method open (line 394) | pub fn open(&mut self, open: BlameFileOpen) -> Result<()> {
method any_work_pending (line 413) | pub const fn any_work_pending(&self) -> bool {
method update_async (line 418) | pub fn update_async(
method update_git (line 430) | fn update_git(
method update (line 441) | fn update(&mut self) -> Result<()> {
method update_syntax (line 482) | fn update_syntax(&mut self, ev: AsyncNotification) {
method get_title (line 523) | fn get_title(&self) -> String {
method get_rows (line 554) | fn get_rows(&self, width: usize) -> Vec<Row<'_>> {
method highlight_blame_lines (line 581) | fn highlight_blame_lines(&mut self) {
method get_line_blame (line 609) | fn get_line_blame<'a>(
method get_metadata_for_line_blame (line 664) | fn get_metadata_for_line_blame(
method get_max_line_number (line 702) | fn get_max_line_number(&self) -> usize {
method get_line_number_width (line 709) | fn get_line_number_width(&self) -> usize {
method move_selection (line 715) | fn move_selection(&self, scroll_type: ScrollType) -> bool {
method set_open_selection (line 746) | fn set_open_selection(&self) {
method get_selection (line 756) | fn get_selection(&self) -> Option<usize> {
method goto_line (line 771) | pub fn goto_line(&mut self, line: usize) {
method selected_commit (line 779) | fn selected_commit(&self) -> Option<CommitId> {
method draw (line 103) | fn draw(&self, f: &mut Frame, area: Rect) -> Result<()> {
method commands (line 191) | fn commands(
method event (line 252) | fn event(
method is_visible (line 345) | fn is_visible(&self) -> bool {
method show (line 349) | fn show(&mut self) -> Result<()> {
function get_author_width (line 801) | fn get_author_width(width: usize) -> usize {
function number_of_digits (line 806) | const fn number_of_digits(number: usize) -> usize {
FILE: src/popups/branchlist.rs
type BranchListPopup (line 45) | pub struct BranchListPopup {
method new (line 242) | pub fn new(env: &Environment) -> Self {
method move_event (line 258) | fn move_event(&mut self, e: &KeyEvent) -> Result<EventState> {
method open (line 294) | pub fn open(&mut self) -> Result<()> {
method branch_finder_update (line 301) | pub fn branch_finder_update(&mut self, idx: usize) -> Result<()> {
method check_remotes (line 306) | fn check_remotes(&mut self) {
method update_branches (line 315) | pub fn update_branches(&mut self) -> Result<()> {
method update_git (line 333) | pub fn update_git(
method valid_selection (line 344) | const fn valid_selection(&self) -> bool {
method merge_branch (line 348) | fn merge_branch(&mut self) -> Result<()> {
method rebase_branch (line 364) | fn rebase_branch(&mut self) -> Result<()> {
method inspect_head_of_branch (line 380) | fn inspect_head_of_branch(&mut self) {
method get_branch_type (line 391) | const fn get_branch_type(&self) -> BranchType {
method hide_and_switch_tab (line 399) | fn hide_and_switch_tab(&mut self) -> Result<()> {
method selection_is_cur_branch (line 411) | fn selection_is_cur_branch(&self) -> bool {
method get_selected_commit (line 425) | fn get_selected_commit(&self) -> Option<CommitId> {
method move_selection (line 432) | fn move_selection(&mut self, scroll: ScrollType) -> Result<bool> {
method set_selection (line 455) | fn set_selection(&mut self, selection: u16) -> Result<()> {
method get_text (line 471) | fn get_text(
method switch_to_selected_branch (line 580) | fn switch_to_selected_branch(&mut self) -> Result<()> {
method draw_tabs (line 618) | fn draw_tabs(&self, f: &mut Frame, r: Rect) {
method draw_list (line 641) | fn draw_list(&self, f: &mut Frame, r: Rect) -> Result<()> {
method rename_branch (line 671) | fn rename_branch(&self) {
method delete_branch (line 679) | fn delete_branch(&self) {
method add_commands_internal (line 692) | fn add_commands_internal(&self, out: &mut Vec<CommandInfo>) {
method draw (line 60) | fn draw(&self, f: &mut Frame, rect: Rect) -> Result<()> {
method commands (line 106) | fn commands(
method event (line 123) | fn event(&mut self, ev: &Event) -> Result<EventState> {
method is_visible (line 226) | fn is_visible(&self) -> bool {
method hide (line 230) | fn hide(&mut self) {
method show (line 234) | fn show(&mut self) -> Result<()> {
FILE: src/popups/checkout_option.rs
type CheckoutOptionPopup (line 27) | pub struct CheckoutOptionPopup {
method new (line 39) | pub fn new(env: &Environment) -> Self {
method get_text (line 51) | fn get_text(&self, _width: u16) -> Vec<Line<'_>> {
method open (line 80) | pub fn open(&mut self, branch: BranchInfo) -> Result<()> {
method checkout (line 88) | fn checkout(&self) -> Result<()> {
method handle_event (line 100) | fn handle_event(&mut self) -> Result<()> {
method change_kind (line 118) | const fn change_kind(&mut self, incr: bool) {
method draw (line 128) | fn draw(&self, f: &mut Frame, area: Rect) -> Result<()> {
method commands (line 158) | fn commands(
method event (line 195) | fn event(
method is_visible (line 226) | fn is_visible(&self) -> bool {
method hide (line 230) | fn hide(&mut self) {
method show (line 234) | fn show(&mut self) -> Result<()> {
FILE: src/popups/commit.rs
type CommitResult (line 41) | enum CommitResult {
type Mode (line 46) | enum Mode {
type CommitPopup (line 54) | pub struct CommitPopup {
method new (line 72) | pub fn new(env: &Environment) -> Self {
method update (line 96) | pub fn update(&mut self) {
method draw_branch_name (line 100) | fn draw_branch_name(&self, f: &mut Frame) {
method draw_warnings (line 116) | fn draw_warnings(&self, f: &mut Frame) {
method item_status_char (line 147) | const fn item_status_char(
method show_editor (line 160) | pub fn show_editor(
method commit (line 209) | fn commit(&mut self) -> Result<()> {
method commit_with_msg (line 230) | fn commit_with_msg(
method do_commit (line 280) | fn do_commit(&self, msg: &str) -> Result<()> {
method can_commit (line 303) | fn can_commit(&self) -> bool {
method can_amend (line 307) | fn can_amend(&self) -> bool {
method is_empty (line 313) | fn is_empty(&self) -> bool {
method is_changed (line 317) | fn is_changed(&self) -> bool {
method amend (line 322) | fn amend(&mut self) -> Result<()> {
method signoff_commit (line 340) | fn signoff_commit(&mut self) {
method toggle_verify (line 348) | const fn toggle_verify(&mut self) {
method open (line 352) | pub fn open(&mut self, reword: Option<CommitId>) -> Result<()> {
method add_sign_off (line 461) | fn add_sign_off(&self, msg: &str) -> Result<String> {
constant FIRST_LINE_LIMIT (line 68) | const FIRST_LINE_LIMIT: usize = 50;
method draw (line 485) | fn draw(&self, f: &mut Frame, rect: Rect) -> Result<()> {
method commands (line 497) | fn commands(
method event (line 558) | fn event(&mut self, ev: &Event) -> Result<EventState> {
method is_visible (line 629) | fn is_visible(&self) -> bool {
method hide (line 633) | fn hide(&mut self) {
method show (line 637) | fn show(&mut self) -> Result<()> {
FILE: src/popups/compare_commits.rs
type CompareCommitsPopup (line 28) | pub struct CompareCommitsPopup {
method new (line 166) | pub fn new(env: &Environment) -> Self {
method open (line 184) | pub fn open(&mut self, open: InspectCommitOpen) -> Result<()> {
method any_work_pending (line 201) | pub fn any_work_pending(&self) -> bool {
method update_git (line 206) | pub fn update_git(
method get_ids (line 221) | fn get_ids(&self) -> Option<OldNew<CommitId>> {
method update_diff (line 237) | pub fn update_diff(&mut self) -> Result<()> {
method update (line 269) | fn update(&mut self) -> Result<()> {
method can_focus_diff (line 279) | fn can_focus_diff(&self) -> bool {
method hide_stacked (line 283) | fn hide_stacked(&mut self, stack: bool) {
method draw (line 41) | fn draw(&self, f: &mut Frame, rect: Rect) -> Result<()> {
method commands (line 71) | fn commands(
method event (line 108) | fn event(&mut self, ev: &Event) -> Result<EventState> {
method is_visible (line 146) | fn is_visible(&self) -> bool {
method hide (line 149) | fn hide(&mut self) {
method show (line 152) | fn show(&mut self) -> Result<()> {
FILE: src/popups/confirm.rs
type ConfirmPopup (line 20) | pub struct ConfirmPopup {
method new (line 103) | pub fn new(env: &Environment) -> Self {
method open (line 113) | pub fn open(&mut self, a: Action) -> Result<()> {
method confirm (line 120) | pub fn confirm(&mut self) {
method get_text (line 128) | fn get_text(&self) -> (String, String) {
method draw (line 29) | fn draw(&self, f: &mut Frame, _rect: Rect) -> Result<()> {
method commands (line 51) | fn commands(
method event (line 70) | fn event(&mut self, ev: &Event) -> Result<EventState> {
method is_visible (line 86) | fn is_visible(&self) -> bool {
method hide (line 90) | fn hide(&mut self) {
method show (line 94) | fn show(&mut self) -> Result<()> {
FILE: src/popups/create_branch.rs
type CreateBranchPopup (line 18) | pub struct CreateBranchPopup {
method new (line 92) | pub fn new(env: &Environment) -> Self {
method open (line 109) | pub fn open(&mut self) -> Result<()> {
method create_branch (line 116) | pub fn create_branch(&mut self) {
method draw_warnings (line 140) | fn draw_warnings(&self, f: &mut Frame) {
method draw (line 27) | fn draw(&self, f: &mut Frame, rect: Rect) -> Result<()> {
method commands (line 38) | fn commands(
method event (line 58) | fn event(&mut self, ev: &Event) -> Result<EventState> {
method is_visible (line 75) | fn is_visible(&self) -> bool {
method hide (line 79) | fn hide(&mut self) {
method show (line 83) | fn show(&mut self) -> Result<()> {
FILE: src/popups/create_remote.rs
type State (line 20) | enum State {
type CreateRemotePopup (line 30) | pub struct CreateRemotePopup {
method new (line 117) | pub fn new(env: &Environment) -> Self {
method open (line 129) | pub fn open(&mut self) -> Result<()> {
method draw_warnings (line 137) | fn draw_warnings(&self, f: &mut Frame) {
method handle_submit (line 170) | fn handle_submit(&mut self) {
method draw (line 40) | fn draw(
method commands (line 54) | fn commands(
method event (line 73) | fn event(
method is_visible (line 93) | fn is_visible(&self) -> bool {
method hide (line 97) | fn hide(&mut self) {
method show (line 101) | fn show(&mut self) -> Result<()> {
FILE: src/popups/externaleditor.rs
type ExternalEditorPopup (line 31) | pub struct ExternalEditorPopup {
method new (line 39) | pub fn new(env: &Environment) -> Self {
method open_file_in_editor (line 48) | pub fn open_file_in_editor(
method draw (line 123) | fn draw(&self, f: &mut Frame, _rect: Rect) -> Result<()> {
method commands (line 154) | fn commands(
method event (line 166) | fn event(&mut self, _ev: &Event) -> Result<EventState> {
method is_visible (line 174) | fn is_visible(&self) -> bool {
method hide (line 178) | fn hide(&mut self) {
method show (line 182) | fn show(&mut self) -> Result<()> {
FILE: src/popups/fetch.rs
type FetchPopup (line 33) | pub struct FetchPopup {
method new (line 47) | pub fn new(env: &Environment) -> Self {
method fetch (line 62) | pub fn fetch(&mut self) -> Result<()> {
method fetch_all (line 82) | fn fetch_all(&mut self, cred: Option<BasicAuthCredential>) {
method any_work_pending (line 93) | pub const fn any_work_pending(&self) -> bool {
method update_git (line 98) | pub fn update_git(&mut self, ev: AsyncGitNotification) {
method update (line 105) | fn update(&mut self) {
method draw (line 118) | fn draw(&self, f: &mut Frame, rect: Rect) -> Result<()> {
method commands (line 149) | fn commands(
method event (line 172) | fn event(&mut self, ev: &Event) -> Result<EventState> {
method is_visible (line 193) | fn is_visible(&self) -> bool {
method hide (line 197) | fn hide(&mut self) {
method show (line 201) | fn show(&mut self) -> Result<()> {
FILE: src/popups/file_revlog.rs
constant SLICE_SIZE (line 33) | const SLICE_SIZE: usize = 1200;
type FileRevOpen (line 36) | pub struct FileRevOpen {
method new (line 42) | pub const fn new(file_path: String) -> Self {
type FileRevlogPopup (line 51) | pub struct FileRevlogPopup {
method new (line 72) | pub fn new(env: &Environment) -> Self {
method components_mut (line 96) | fn components_mut(&mut self) -> Vec<&mut dyn Component> {
method open (line 101) | pub fn open(&mut self, open_request: FileRevOpen) -> Result<()> {
method any_work_pending (line 125) | pub fn any_work_pending(&self) -> bool {
method update (line 131) | pub fn update(&mut self) -> Result<()> {
method update_git (line 143) | pub fn update_git(
method update_diff (line 159) | pub fn update_diff(&mut self) -> Result<()> {
method fetch_commits (line 196) | fn fetch_commits(
method selected_commit (line 222) | fn selected_commit(&self) -> Option<CommitId> {
method can_focus_diff (line 241) | fn can_focus_diff(&self) -> bool {
method get_title (line 245) | fn get_title(&self) -> String {
method get_rows (line 266) | fn get_rows(&self, now: DateTime<Local>) -> Vec<Row<'_>> {
method get_max_selection (line 297) | fn get_max_selection(&self) -> usize {
method move_selection (line 303) | fn move_selection(
method set_selection (line 338) | fn set_selection(&mut self, selection: usize) {
method fetch_commits_if_needed (line 352) | fn fetch_commits_if_needed(&mut self) -> Result<()> {
method get_selection (line 368) | fn get_selection(&self) -> Option<usize> {
method draw_revlog (line 376) | fn draw_revlog(&self, f: &mut Frame, area: Rect) {
method hide_stacked (line 443) | fn hide_stacked(&mut self, stack: bool) {
method draw (line 462) | fn draw(&self, f: &mut Frame, area: Rect) -> Result<()> {
method event (line 492) | fn event(&mut self, event: &Event) -> Result<EventState> {
method commands (line 581) | fn commands(
method is_visible (line 629) | fn is_visible(&self) -> bool {
method hide (line 633) | fn hide(&mut self) {
method show (line 637) | fn show(&mut self) -> Result<()> {
FILE: src/popups/fuzzy_find.rs
type FuzzyFindPopup (line 26) | pub struct FuzzyFindPopup {
method new (line 42) | pub fn new(env: &Environment) -> Self {
method update_query (line 63) | fn update_query(&mut self) {
method set_query (line 77) | fn set_query(&mut self, query: Option<String>) {
method refresh_selection (line 110) | fn refresh_selection(&mut self) {
method open (line 131) | pub fn open(
method move_selection (line 149) | fn move_selection(&mut self, move_type: ScrollType) -> bool {
method draw_matches_list (line 169) | fn draw_matches_list(&self, f: &mut Frame, mut area: Rect) {
method draw (line 250) | fn draw(&self, f: &mut Frame, area: Rect) -> Result<()> {
method commands (line 312) | fn commands(
method event (line 336) | fn event(
method is_visible (line 369) | fn is_visible(&self) -> bool {
method hide (line 373) | fn hide(&mut self) {
method show (line 377) | fn show(&mut self) -> Result<()> {
FILE: src/popups/goto_line.rs
type GotoLinePopup (line 24) | pub struct GotoLinePopup {
method new (line 36) | pub fn new(env: &Environment) -> Self {
method open (line 49) | pub const fn open(&mut self, max_line: usize) {
method commands (line 57) | fn commands(
method is_visible (line 84) | fn is_visible(&self) -> bool {
method event (line 89) | fn event(&mut self, event: &Event) -> Result<EventState> {
method draw (line 149) | fn draw(&self, f: &mut Frame, area: Rect) -> Result<()> {
FILE: src/popups/help.rs
type HelpPopup (line 25) | pub struct HelpPopup {
method new (line 177) | pub fn new(env: &Environment) -> Self {
method set_cmds (line 187) | pub fn set_cmds(&mut self, cmds: Vec<CommandInfo>) {
method move_selection (line 197) | fn move_selection(&mut self, inc: bool) {
method get_text (line 214) | fn get_text(&self) -> Vec<Line<'_>> {
method draw (line 34) | fn draw(&self, f: &mut Frame, _rect: Rect) -> Result<()> {
method commands (line 97) | fn commands(
method event (line 135) | fn event(&mut self, ev: &Event) -> Result<EventState> {
method is_visible (line 161) | fn is_visible(&self) -> bool {
method hide (line 165) | fn hide(&mut self) {
method show (line 169) | fn show(&mut self) -> Result<()> {
FILE: src/popups/inspect_commit.rs
type InspectCommitOpen (line 29) | pub struct InspectCommitOpen {
method new (line 37) | pub const fn new(commit_id: CommitId) -> Self {
method new_with_tags (line 45) | pub const fn new_with_tags(
type InspectCommitPopup (line 57) | pub struct InspectCommitPopup {
method new (line 221) | pub fn new(env: &Environment) -> Self {
method open (line 238) | pub fn open(&mut self, open: InspectCommitOpen) -> Result<()> {
method any_work_pending (line 246) | pub fn any_work_pending(&self) -> bool {
method update_git (line 251) | pub fn update_git(
method update_diff (line 267) | pub fn update_diff(&mut self) -> Result<()> {
method update (line 301) | fn update(&mut self) -> Result<()> {
method can_focus_diff (line 313) | fn can_focus_diff(&self) -> bool {
method hide_stacked (line 317) | fn hide_stacked(&mut self, stack: bool) {
method draw (line 69) | fn draw(&self, f: &mut Frame, rect: Rect) -> Result<()> {
method commands (line 99) | fn commands(
method event (line 144) | fn event(&mut self, ev: &Event) -> Result<EventState> {
method is_visible (line 201) | fn is_visible(&self) -> bool {
method hide (line 204) | fn hide(&mut self) {
method show (line 207) | fn show(&mut self) -> Result<()> {
FILE: src/popups/log_search.rs
type Selection (line 28) | enum Selection {
type PopupMode (line 38) | enum PopupMode {
type LogSearchPopupPopup (line 43) | pub struct LogSearchPopupPopup {
method new (line 58) | pub fn new(env: &Environment) -> Self {
method open (line 82) | pub fn open(&mut self) -> Result<()> {
method set_mode (line 94) | fn set_mode(&mut self, mode: &PopupMode) {
method execute_confirm (line 116) | fn execute_confirm(&mut self) {
method is_valid (line 146) | fn is_valid(&self) -> bool {
method validate_commit_sha (line 155) | fn validate_commit_sha(&mut self) {
method get_text_options (line 167) | fn get_text_options(&self) -> Vec<Line<'_>> {
method option_selected (line 270) | const fn option_selected(&self) -> bool {
method toggle_option (line 274) | fn toggle_option(&mut self) {
method move_selection (line 318) | const fn move_selection(&mut self, arg: bool) {
method draw_search_mode (line 354) | fn draw_search_mode(
method draw_commit_sha_mode (line 401) | fn draw_commit_sha_mode(
method draw_invalid_sha (line 447) | fn draw_invalid_sha(&self, f: &mut Frame) {
method event_search_mode (line 467) | fn event_search_mode(
method event_commit_sha_mode (line 503) | fn event_commit_sha_mode(
method draw (line 527) | fn draw(&self, f: &mut Frame, area: Rect) -> Result<()> {
method commands (line 544) | fn commands(
method event (line 602) | fn event(
method is_visible (line 618) | fn is_visible(&self) -> bool {
method hide (line 622) | fn hide(&mut self) {
method show (line 626) | fn show(&mut self) -> Result<()> {
FILE: src/popups/mod.rs
function popup_paragraph (line 72) | fn popup_paragraph<'a, T>(
FILE: src/popups/msg.rs
type MsgPopup (line 22) | pub struct MsgPopup {
method new (line 172) | pub fn new(env: &Environment) -> Self {
method set_new_msg (line 183) | fn set_new_msg(
method show_error (line 195) | pub fn show_error(&mut self, msg: &str) -> Result<()> {
method show_info (line 203) | pub fn show_info(&mut self, msg: &str) -> Result<()> {
constant POPUP_HEIGHT (line 31) | const POPUP_HEIGHT: u16 = 25;
constant BORDER_WIDTH (line 32) | const BORDER_WIDTH: u16 = 2;
constant MINIMUM_WIDTH (line 33) | const MINIMUM_WIDTH: u16 = 60;
method draw (line 36) | fn draw(&self, f: &mut Frame, _rect: Rect) -> Result<()> {
method commands (line 111) | fn commands(
method event (line 135) | fn event(&mut self, ev: &Event) -> Result<EventState> {
method is_visible (line 156) | fn is_visible(&self) -> bool {
method hide (line 160) | fn hide(&mut self) {
method show (line 164) | fn show(&mut self) -> Result<()> {
FILE: src/popups/options.rs
type AppOption (line 25) | pub enum AppOption {
type OptionsPopup (line 32) | pub struct OptionsPopup {
method new (line 43) | pub fn new(env: &Environment) -> Self {
method get_text (line 54) | fn get_text(&self, width: u16) -> Vec<Line<'_>> {
method add_status (line 62) | fn add_status(&self, txt: &mut Vec<Line>, width: u16) {
method is_select (line 104) | fn is_select(&self, kind: AppOption) -> bool {
method add_header (line 108) | fn add_header(txt: &mut Vec<Line>, header: &'static str) {
method add_entry (line 116) | fn add_entry(
method move_selection (line 137) | const fn move_selection(&mut self, up: bool) {
method switch_option (line 171) | fn switch_option(&self, right: bool) {
method draw (line 258) | fn draw(&self, f: &mut Frame, area: Rect) -> Result<()> {
method commands (line 288) | fn commands(
method event (line 317) | fn event(
method is_visible (line 352) | fn is_visible(&self) -> bool {
method hide (line 356) | fn hide(&mut self) {
method show (line 360) | fn show(&mut self) -> Result<()> {
FILE: src/popups/pull.rs
type PullPopup (line 36) | pub struct PullPopup {
method new (line 51) | pub fn new(env: &Environment) -> Self {
method fetch (line 70) | pub fn fetch(&mut self, branch: String) -> Result<()> {
method fetch_from_remote (line 89) | fn fetch_from_remote(
method any_work_pending (line 107) | pub const fn any_work_pending(&self) -> bool {
method update_git (line 112) | pub fn update_git(&mut self, ev: AsyncGitNotification) {
method update (line 125) | fn update(&mut self) -> Result<()> {
method try_ff_merge (line 145) | fn try_ff_merge(&mut self) -> Result<()> {
method try_conflict_free_merge (line 166) | pub fn try_conflict_free_merge(&self, rebase: bool) {
method confirm_merge (line 188) | fn confirm_merge(&mut self, incoming: usize) {
method draw (line 203) | fn draw(&self, f: &mut Frame, rect: Rect) -> Result<()> {
method commands (line 236) | fn commands(
method event (line 259) | fn event(&mut self, ev: &Event) -> Result<EventState> {
method is_visible (line 280) | fn is_visible(&self) -> bool {
method hide (line 284) | fn hide(&mut self) {
method show (line 288) | fn show(&mut self) -> Result<()> {
FILE: src/popups/push.rs
type PushComponentModifier (line 36) | enum PushComponentModifier {
method force (line 44) | pub(crate) fn force(&self) -> bool {
method delete (line 47) | pub(crate) fn delete(&self) -> bool {
type PushPopup (line 53) | pub struct PushPopup {
method new (line 70) | pub fn new(env: &Environment) -> Self {
method push (line 91) | pub fn push(
method push_to_remote (line 125) | fn push_to_remote(
method update_git (line 181) | pub fn update_git(
method update (line 193) | fn update(&mut self) -> Result<()> {
method any_work_pending (line 210) | pub const fn any_work_pending(&self) -> bool {
method get_progress (line 215) | pub fn get_progress(
method progress_state_name (line 229) | fn progress_state_name(state: &RemoteProgressState) -> String {
method draw (line 252) | fn draw(&self, f: &mut Frame, rect: Rect) -> Result<()> {
method commands (line 289) | fn commands(
method event (line 312) | fn event(&mut self, ev: &Event) -> Result<EventState> {
method is_visible (line 340) | fn is_visible(&self) -> bool {
method hide (line 344) | fn hide(&mut self) {
method show (line 348) | fn show(&mut self) -> Result<()> {
FILE: src/popups/push_tags.rs
type PushTagsPopup (line 33) | pub struct PushTagsPopup {
method new (line 47) | pub fn new(env: &Environment) -> Self {
method push_tags (line 65) | pub fn push_tags(&mut self) -> Result<()> {
method push_to_remote (line 83) | fn push_to_remote(
method update_git (line 115) | pub fn update_git(
method update (line 127) | fn update(&mut self) -> Result<()> {
method any_work_pending (line 144) | pub const fn any_work_pending(&self) -> bool {
method get_progress (line 149) | pub fn get_progress(
method progress_state_name (line 163) | fn progress_state_name(progress: &PushTagsProgress) -> String {
method draw (line 178) | fn draw(&self, f: &mut Frame, rect: Rect) -> Result<()> {
method commands (line 211) | fn commands(
method event (line 234) | fn event(&mut self, ev: &Event) -> Result<EventState> {
method is_visible (line 261) | fn is_visible(&self) -> bool {
method hide (line 265) | fn hide(&mut self) {
method show (line 269) | fn show(&mut self) -> Result<()> {
FILE: src/popups/remotelist.rs
type RemoteListPopup (line 28) | pub struct RemoteListPopup {
method new (line 178) | pub fn new(env: &Environment) -> Self {
method move_event (line 192) | fn move_event(&mut self, e: &KeyEvent) -> Result<EventState> {
method open (line 224) | pub fn open(&mut self) -> Result<()> {
method get_text (line 230) | fn get_text(
method draw_remotes_list (line 277) | fn draw_remotes_list(
method draw_separator (line 311) | fn draw_separator(&self, f: &mut Frame, r: Rect) {
method draw_selected_remote_details (line 323) | fn draw_selected_remote_details(&self, f: &mut Frame, r: Rect) {
method move_selection (line 393) | fn move_selection(&mut self, scroll: ScrollType) -> Result<bool> {
method valid_selection (line 416) | const fn valid_selection(&self) -> bool {
method set_selection (line 421) | fn set_selection(&mut self, selection: u16) -> Result<()> {
method update_remotes (line 436) | pub fn update_remotes(&mut self) -> Result<()> {
method delete_remote (line 444) | fn delete_remote(&self) {
method rename_remote (line 453) | fn rename_remote(&self) {
method update_remote_url (line 460) | fn update_remote_url(&self) {
method draw (line 41) | fn draw(&self, f: &mut Frame, rect: Rect) -> Result<()> {
method commands (line 81) | fn commands(
method event (line 132) | fn event(&mut self, ev: &Event) -> Result<EventState> {
method is_visible (line 163) | fn is_visible(&self) -> bool {
method hide (line 167) | fn hide(&mut self) {
method show (line 171) | fn show(&mut self) -> Result<()> {
FILE: src/popups/rename_branch.rs
type RenameBranchPopup (line 18) | pub struct RenameBranchPopup {
method new (line 92) | pub fn new(env: &Environment) -> Self {
method open (line 110) | pub fn open(
method rename_branch (line 124) | pub fn rename_branch(&mut self) {
method draw_warnings (line 158) | fn draw_warnings(&self, f: &mut Frame) {
method draw (line 28) | fn draw(&self, f: &mut Frame, rect: Rect) -> Result<()> {
method commands (line 38) | fn commands(
method event (line 58) | fn event(&mut self, ev: &Event) -> Result<EventState> {
method is_visible (line 75) | fn is_visible(&self) -> bool {
method hide (line 79) | fn hide(&mut self) {
method show (line 83) | fn show(&mut self) -> Result<()> {
FILE: src/popups/rename_remote.rs
type RenameRemotePopup (line 19) | pub struct RenameRemotePopup {
method new (line 92) | pub fn new(env: &Environment) -> Self {
method open (line 110) | pub fn open(&mut self, cur_name: String) -> Result<()> {
method draw_warnings (line 118) | fn draw_warnings(&self, f: &mut Frame) {
method rename_remote (line 149) | pub fn rename_remote(&mut self) {
method draw (line 29) | fn draw(&self, f: &mut Frame, rect: Rect) -> Result<()> {
method commands (line 39) | fn commands(
method event (line 58) | fn event(&mut self, ev: &Event) -> Result<EventState> {
method is_visible (line 75) | fn is_visible(&self) -> bool {
method hide (line 79) | fn hide(&mut self) {
method show (line 83) | fn show(&mut self) -> Result<()> {
FILE: src/popups/reset.rs
function type_to_string (line 25) | const fn type_to_string(
type ResetPopup (line 42) | pub struct ResetPopup {
method new (line 55) | pub fn new(env: &Environment) -> Self {
method get_text (line 70) | fn get_text(&self, _width: u16) -> Vec<Line<'_>> {
method open (line 112) | pub fn open(&mut self, id: CommitId) -> Result<()> {
method update (line 122) | pub fn update(&mut self) -> Result<()> {
method reset (line 128) | fn reset(&mut self) {
method change_kind (line 140) | const fn change_kind(&mut self, incr: bool) {
method draw (line 158) | fn draw(&self, f: &mut Frame, area: Rect) -> Result<()> {
method commands (line 188) | fn commands(
method event (line 225) | fn event(
method is_visible (line 252) | fn is_visible(&self) -> bool {
method hide (line 256) | fn hide(&mut self) {
method show (line 260) | fn show(&mut self) -> Result<()> {
FILE: src/popups/revision_files.rs
type FileTreeOpen (line 19) | pub struct FileTreeOpen {
method new (line 24) | pub const fn new(commit_id: CommitId) -> Self {
type RevisionFilesPopup (line 29) | pub struct RevisionFilesPopup {
method new (line 39) | pub fn new(env: &Environment) -> Self {
method open (line 50) | pub fn open(&mut self, request: FileTreeOpen) -> Result<()> {
method update (line 59) | pub fn update(&mut self, ev: AsyncNotification) -> Result<()> {
method any_work_pending (line 64) | pub fn any_work_pending(&self) -> bool {
method file_finder_update (line 68) | pub fn file_finder_update(&mut self, file: &Path) {
method hide_stacked (line 72) | fn hide_stacked(&mut self, stack: bool) {
method draw (line 90) | fn draw(&self, f: &mut Frame, area: Rect) -> Result<()> {
method commands (line 102) | fn commands(
method event (line 123) | fn event(
method is_visible (line 147) | fn is_visible(&self) -> bool {
method hide (line 151) | fn hide(&mut self) {
method show (line 155) | fn show(&mut self) -> Result<()> {
FILE: src/popups/stashmsg.rs
type StashMsgPopup (line 17) | pub struct StashMsgPopup {
method new (line 124) | pub fn new(env: &Environment) -> Self {
method options (line 141) | pub const fn options(&mut self, options: StashingOptions) {
method draw (line 26) | fn draw(&self, f: &mut Frame, rect: Rect) -> Result<()> {
method commands (line 34) | fn commands(
method event (line 54) | fn event(&mut self, ev: &Event) -> Result<EventState> {
method is_visible (line 107) | fn is_visible(&self) -> bool {
method hide (line 111) | fn hide(&mut self) {
method show (line 115) | fn show(&mut self) -> Result<()> {
FILE: src/popups/submodules.rs
type SubmodulesListPopup (line 32) | pub struct SubmodulesListPopup {
method new (line 243) | pub fn new(env: &Environment) -> Self {
method open (line 260) | pub fn open(&mut self) -> Result<()> {
method update_submodules (line 268) | pub fn update_submodules(&mut self) -> Result<()> {
method selected_entry (line 284) | fn selected_entry(&self) -> Option<&SubmoduleInfo> {
method is_valid_selection (line 288) | fn is_valid_selection(&self) -> bool {
method can_open_submodule (line 292) | fn can_open_submodule(&self) -> bool {
method move_selection (line 297) | fn move_selection(&mut self, scroll: ScrollType) -> Result<bool> {
method set_selection (line 319) | fn set_selection(&mut self, selection: u16) -> Result<()> {
method get_text (line 334) | fn get_text(
method get_info_text (line 396) | fn get_info_text(&self, theme: &SharedTheme) -> Text<'_> {
method get_local_info_text (line 445) | fn get_local_info_text(&self, theme: &SharedTheme) -> Text<'_> {
method draw_list (line 471) | fn draw_list(&self, f: &mut Frame, r: Rect) -> Result<()> {
method draw_info (line 501) | fn draw_info(&self, f: &mut Frame, r: Rect) {
method draw_local_info (line 509) | fn draw_local_info(&self, f: &mut Frame, r: Rect) {
method draw (line 47) | fn draw(&self, f: &mut Frame, rect: Rect) -> Result<()> {
method commands (line 101) | fn commands(
method event (line 146) | fn event(&mut self, ev: &Event) -> Result<EventState> {
method is_visible (line 227) | fn is_visible(&self) -> bool {
method hide (line 231) | fn hide(&mut self) {
method show (line 235) | fn show(&mut self) -> Result<()> {
FILE: src/popups/tag_commit.rs
type Mode (line 18) | enum Mode {
type TagCommitPopup (line 23) | pub struct TagCommitPopup {
method new (line 126) | pub fn new(env: &Environment) -> Self {
method open (line 144) | pub fn open(&mut self, id: CommitId) -> Result<()> {
method is_valid_tag (line 151) | fn is_valid_tag(&self) -> bool {
method tag_info (line 155) | fn tag_info(&self) -> (String, Option<String>) {
method tag (line 164) | pub fn tag(&mut self) -> Result<()> {
method start_annotate_mode (line 208) | fn start_annotate_mode(&mut self) {
method draw (line 33) | fn draw(&self, f: &mut Frame, rect: Rect) -> Result<()> {
method commands (line 41) | fn commands(
method event (line 71) | fn event(&mut self, ev: &Event) -> Result<EventState> {
method is_visible (line 105) | fn is_visible(&self) -> bool {
method hide (line 109) | fn hide(&mut self) {
method show (line 113) | fn show(&mut self) -> Result<()> {
FILE: src/popups/taglist.rs
type TagListPopup (line 41) | pub struct TagListPopup {
method new (line 289) | pub fn new(env: &Environment) -> Self {
method open (line 309) | pub fn open(&mut self) -> Result<()> {
method update (line 343) | pub fn update(&mut self, ev: AsyncNotification) {
method any_work_pending (line 363) | pub fn any_work_pending(&self) -> bool {
method update_tags (line 368) | pub fn update_tags(&mut self) -> Result<()> {
method update_missing_remote_tags (line 376) | pub fn update_missing_remote_tags(&self) {
method move_selection (line 386) | fn move_selection(&self, scroll_type: ScrollType) -> bool {
method show_annotation (line 418) | fn show_annotation(&self) {
method can_show_annotation (line 428) | fn can_show_annotation(&self) -> bool {
method get_rows (line 435) | fn get_rows(&self) -> Vec<Row<'_>> {
method get_row (line 442) | fn get_row(&self, tag: &TagWithMetadata) -> Row<'_> {
method valid_selection (line 486) | fn valid_selection(&self) -> bool {
method selected_tag (line 490) | fn selected_tag(&self) -> Option<&TagWithMetadata> {
method draw (line 57) | fn draw(&self, f: &mut Frame, rect: Rect) -> Result<()> {
method commands (line 137) | fn commands(
method event (line 185) | fn event(&mut self, event: &Event) -> Result<EventState> {
method is_visible (line 273) | fn is_visible(&self) -> bool {
method hide (line 277) | fn hide(&mut self) {
method show (line 281) | fn show(&mut self) -> Result<()> {
FILE: src/popups/update_remote_url.rs
type UpdateRemoteUrlPopup (line 16) | pub struct UpdateRemoteUrlPopup {
method new (line 91) | pub fn new(env: &Environment) -> Self {
method open (line 113) | pub fn open(
method update_remote_url (line 127) | pub fn update_remote_url(&mut self) {
method draw (line 26) | fn draw(
method commands (line 39) | fn commands(
method event (line 58) | fn event(&mut self, ev: &Event) -> Result<EventState> {
method is_visible (line 75) | fn is_visible(&self) -> bool {
method hide (line 79) | fn hide(&mut self) {
method show (line 83) | fn show(&mut self) -> Result<()> {
FILE: src/queue.rs
type ResetItem (line 38) | pub struct ResetItem {
type Action (line 44) | pub enum Action {
type StackablePopupOpen (line 64) | pub enum StackablePopupOpen {
type AppTabs (line 77) | pub enum AppTabs {
type InternalEvent (line 86) | pub enum InternalEvent {
type Queue (line 171) | pub struct Queue {
method new (line 176) | pub fn new() -> Self {
method push (line 182) | pub fn push(&self, ev: InternalEvent) {
method pop (line 186) | pub fn pop(&self) -> Option<InternalEvent> {
method clear (line 190) | pub fn clear(&self) {
FILE: src/spinner.rs
type Spinner (line 13) | pub struct Spinner {
method update (line 31) | pub fn update(&mut self) {
method set_state (line 37) | pub const fn set_state(&mut self, active: bool) {
method draw (line 42) | pub fn draw(
method default (line 20) | fn default() -> Self {
FILE: src/string_utils.rs
function trim_length_left (line 5) | pub fn trim_length_left(s: &str, width: usize) -> &str {
function tabs_to_spaces (line 19) | pub fn tabs_to_spaces(input: String) -> String {
function trim_offset (line 29) | pub fn trim_offset(src: &str, mut offset: usize) -> &str {
function test_trim (line 50) | fn test_trim() {
FILE: src/strings.rs
constant RARE_ACTION (line 10) | pub const RARE_ACTION: i8 = 30;
constant NAV (line 11) | pub const NAV: i8 = 20;
constant AVERAGE (line 12) | pub const AVERAGE: i8 = 10;
constant PRIORITY (line 13) | pub const PRIORITY: i8 = 1;
constant CHECKMARK (line 43) | pub const CHECKMARK: &str = "\u{2713}";
constant SPACE (line 44) | pub const SPACE: &str = "\u{02FD}";
constant EMPTY_SPACE (line 45) | pub const EMPTY_SPACE: &str = " ";
constant FOLDER_ICON_COLLAPSED (line 46) | pub const FOLDER_ICON_COLLAPSED: &str = "\u{25b8}";
constant FOLDER_ICON_EXPANDED (line 47) | pub const FOLDER_ICON_EXPANDED: &str = "\u{25be}";
constant EMPTY_STR (line 48) | pub const EMPTY_STR: &str = "";
constant ELLIPSIS (line 49) | pub const ELLIPSIS: char = '\u{2026}';
function title_branches (line 52) | pub fn title_branches() -> String {
function title_tags (line 55) | pub fn title_tags() -> String {
function title_status (line 58) | pub fn title_status(_key_config: &SharedKeyConfig) -> String {
function title_diff (line 61) | pub fn title_diff(_key_config: &SharedKeyConfig) -> String {
function title_index (line 64) | pub fn title_index(_key_config: &SharedKeyConfig) -> String {
function tab_status (line 67) | pub fn tab_status(key_config: &SharedKeyConfig) -> String {
function tab_log (line 73) | pub fn tab_log(key_config: &SharedKeyConfig) -> String {
function tab_files (line 76) | pub fn tab_files(key_config: &SharedKeyConfig) -> String {
function tab_stashing (line 82) | pub fn tab_stashing(key_config: &SharedKeyConfig) -> String {
function tab_stashes (line 88) | pub fn tab_stashes(key_config: &SharedKeyConfig) -> String {
function tab_divider (line 94) | pub fn tab_divider(_key_config: &SharedKeyConfig) -> String {
function cmd_splitter (line 97) | pub fn cmd_splitter(_key_config: &SharedKeyConfig) -> String {
function msg_opening_editor (line 100) | pub fn msg_opening_editor(_key_config: &SharedKeyConfig) -> String {
function msg_title_error (line 103) | pub fn msg_title_error(_key_config: &SharedKeyConfig) -> String {
function msg_title_info (line 106) | pub fn msg_title_info(_key_config: &SharedKeyConfig) -> String {
function commit_title (line 109) | pub fn commit_title() -> String {
function commit_reword_title (line 112) | pub fn commit_reword_title() -> String {
function commit_title_merge (line 116) | pub fn commit_title_merge() -> String {
function commit_title_revert (line 119) | pub fn commit_title_revert() -> String {
function commit_title_amend (line 122) | pub fn commit_title_amend() -> String {
function commit_msg (line 125) | pub fn commit_msg(_key_config: &SharedKeyConfig) -> String {
function commit_first_line_warning (line 128) | pub fn commit_first_line_warning(count: usize) -> String {
function branch_name_invalid (line 131) | pub const fn branch_name_invalid() -> &'static str {
function commit_editor_msg (line 134) | pub fn commit_editor_msg(_key_config: &SharedKeyConfig) -> String {
function stash_popup_title (line 140) | pub fn stash_popup_title(_key_config: &SharedKeyConfig) -> String {
function stash_popup_msg (line 143) | pub fn stash_popup_msg(_key_config: &SharedKeyConfig) -> String {
function confirm_title_reset (line 146) | pub fn confirm_title_reset() -> String {
function confirm_title_undo_commit (line 149) | pub fn confirm_title_undo_commit() -> String {
function confirm_title_stashdrop (line 152) | pub fn confirm_title_stashdrop(
function confirm_title_stashpop (line 158) | pub fn confirm_title_stashpop(
function confirm_title_merge (line 163) | pub fn confirm_title_merge(
function confirm_msg_merge (line 173) | pub fn confirm_msg_merge(
function confirm_title_abortmerge (line 185) | pub fn confirm_title_abortmerge() -> String {
function confirm_title_abortrevert (line 188) | pub fn confirm_title_abortrevert() -> String {
function confirm_msg_revertchanges (line 191) | pub fn confirm_msg_revertchanges() -> String {
function confirm_title_abortrebase (line 195) | pub fn confirm_title_abortrebase() -> String {
function confirm_msg_abortrebase (line 198) | pub fn confirm_msg_abortrebase() -> String {
function confirm_msg_reset (line 202) | pub fn confirm_msg_reset() -> String {
function confirm_msg_reset_lines (line 205) | pub fn confirm_msg_reset_lines(lines: usize) -> String {
function confirm_msg_undo_commit (line 210) | pub fn confirm_msg_undo_commit() -> String {
function confirm_msg_stashdrop (line 213) | pub fn confirm_msg_stashdrop(
function confirm_msg_stashpop (line 231) | pub fn confirm_msg_stashpop(_key_config: &SharedKeyConfig) -> String {
function confirm_msg_resethunk (line 235) | pub fn confirm_msg_resethunk(
function confirm_title_delete_branch (line 240) | pub fn confirm_title_delete_branch(
function confirm_msg_delete_branch (line 245) | pub fn confirm_msg_delete_branch(
function confirm_title_delete_remote_branch (line 251) | pub fn confirm_title_delete_remote_branch(
function confirm_title_delete_remote (line 256) | pub fn confirm_title_delete_remote(
function confirm_msg_delete_remote (line 261) | pub fn confirm_msg_delete_remote(
function confirm_msg_delete_remote_branch (line 267) | pub fn confirm_msg_delete_remote_branch(
function confirm_title_delete_tag (line 273) | pub fn confirm_title_delete_tag(
function confirm_msg_delete_tag (line 278) | pub fn confirm_msg_delete_tag(
function confirm_title_delete_tag_remote (line 284) | pub fn confirm_title_delete_tag_remote() -> String {
function confirm_msg_delete_tag_remote (line 287) | pub fn confirm_msg_delete_tag_remote(remote_name: &str) -> String {
function confirm_title_force_push (line 290) | pub fn confirm_title_force_push(
function confirm_msg_force_push (line 295) | pub fn confirm_msg_force_push(
function log_title (line 303) | pub fn log_title(_key_config: &SharedKeyConfig) -> String {
function file_log_title (line 306) | pub fn file_log_title(
function blame_title (line 313) | pub fn blame_title(_key_config: &SharedKeyConfig) -> String {
function tag_popup_name_title (line 316) | pub fn tag_popup_name_title() -> String {
function tag_popup_name_msg (line 319) | pub fn tag_popup_name_msg() -> String {
function tag_popup_annotation_title (line 322) | pub fn tag_popup_annotation_title(name: &str) -> String {
function tag_popup_annotation_msg (line 325) | pub fn tag_popup_annotation_msg() -> String {
function stashlist_title (line 328) | pub fn stashlist_title(_key_config: &SharedKeyConfig) -> String {
function help_title (line 331) | pub fn help_title(_key_config: &SharedKeyConfig) -> String {
function stashing_files_title (line 334) | pub fn stashing_files_title(_key_config: &SharedKeyConfig) -> String {
function stashing_options_title (line 337) | pub fn stashing_options_title(
function loading_text (line 342) | pub fn loading_text(_key_config: &SharedKeyConfig) -> String {
function create_branch_popup_title (line 345) | pub fn create_branch_popup_title(
function create_branch_popup_msg (line 350) | pub fn create_branch_popup_msg(
function rename_remote_popup_title (line 355) | pub fn rename_remote_popup_title(
function rename_remote_popup_msg (line 360) | pub fn rename_remote_popup_msg(
function update_remote_url_popup_title (line 365) | pub fn update_remote_url_popup_title(
function update_remote_url_popup_msg (line 370) | pub fn update_remote_url_popup_msg(
function create_remote_popup_title_name (line 375) | pub fn create_remote_popup_title_name(
function create_remote_popup_title_url (line 380) | pub fn create_remote_popup_title_url(
function create_remote_popup_msg_name (line 385) | pub fn create_remote_popup_msg_name(
function create_remote_popup_msg_url (line 390) | pub fn create_remote_popup_msg_url(
function remote_name_invalid (line 395) | pub const fn remote_name_invalid() -> &'static str {
function username_popup_title (line 398) | pub fn username_popup_title(_key_config: &SharedKeyConfig) -> String {
function username_popup_msg (line 401) | pub fn username_popup_msg(_key_config: &SharedKeyConfig) -> String {
function password_popup_title (line 404) | pub fn password_popup_title(_key_config: &SharedKeyConfig) -> String {
function password_popup_msg (line 407) | pub fn password_popup_msg(_key_config: &SharedKeyConfig) -> String {
function rename_branch_popup_title (line 411) | pub fn rename_branch_popup_title(
function rename_branch_popup_msg (line 416) | pub fn rename_branch_popup_msg(
function copy_success (line 422) | pub fn copy_success(s: &str) -> String {
function ellipsis_trim_start (line 426) | pub fn ellipsis_trim_start(s: &str, width: usize) -> Cow<'_, str> {
type CheckoutOptions (line 442) | pub enum CheckoutOptions {
method previous (line 448) | pub const fn previous(self) -> Self {
method next (line 455) | pub const fn next(self) -> Self {
method to_string_pair (line 462) | pub const fn to_string_pair(
function details_author (line 484) | pub fn details_author() -> String {
function details_committer (line 487) | pub fn details_committer() -> String {
function details_sha (line 490) | pub fn details_sha() -> String {
function details_date (line 493) | pub fn details_date() -> String {
function details_tags (line 496) | pub fn details_tags() -> String {
function details_message (line 499) | pub fn details_message() -> String {
function details_info_title (line 502) | pub fn details_info_title(
function compare_details_info_title (line 507) | pub fn compare_details_info_title(
function details_message_title (line 513) | pub fn details_message_title(
function details_files_title (line 518) | pub fn details_files_title(
function toggle_tabs (line 538) | pub fn toggle_tabs(key_config: &SharedKeyConfig) -> CommandText {
function find_file (line 548) | pub fn find_file(key_config: &SharedKeyConfig) -> CommandText {
function find_branch (line 558) | pub fn find_branch(key_config: &SharedKeyConfig) -> CommandText {
function toggle_tabs_direct (line 568) | pub fn toggle_tabs_direct(
function options_popup (line 584) | pub fn options_popup(
function help_open (line 596) | pub fn help_open(key_config: &SharedKeyConfig) -> CommandText {
function navigate_commit_message (line 606) | pub fn navigate_commit_message(
function navigate_tree (line 619) | pub fn navigate_tree(
function scroll (line 634) | pub fn scroll(key_config: &SharedKeyConfig) -> CommandText {
function commit_list_mark (line 645) | pub fn commit_list_mark(
function copy (line 659) | pub fn copy(key_config: &SharedKeyConfig) -> CommandText {
function copy_hash (line 669) | pub fn copy_hash(key_config: &SharedKeyConfig) -> CommandText {
function copy_path (line 679) | pub fn copy_path(key_config: &SharedKeyConfig) -> CommandText {
function push_tags (line 689) | pub fn push_tags(key_config: &SharedKeyConfig) -> CommandText {
function toggle_option (line 699) | pub fn toggle_option(
function show_tag_annotation (line 711) | pub fn show_tag_annotation(
function diff_hunk_next (line 723) | pub fn diff_hunk_next(
function diff_hunk_prev (line 735) | pub fn diff_hunk_prev(
function diff_home_end (line 747) | pub fn diff_home_end(
function diff_hunk_add (line 762) | pub fn diff_hunk_add(
function diff_hunk_revert (line 775) | pub fn diff_hunk_revert(
function diff_lines_revert (line 788) | pub fn diff_lines_revert(
function diff_lines_stage (line 800) | pub fn diff_lines_stage(
function diff_lines_unstage (line 812) | pub fn diff_lines_unstage(
function diff_hunk_remove (line 824) | pub fn diff_hunk_remove(
function close_fuzzy_finder (line 837) | pub fn close_fuzzy_finder(
function close_popup (line 850) | pub fn close_popup(key_config: &SharedKeyConfig) -> CommandText {
function scroll_popup (line 860) | pub fn scroll_popup(key_config: &SharedKeyConfig) -> CommandText {
function close_msg (line 871) | pub fn close_msg(key_config: &SharedKeyConfig) -> CommandText {
function validate_msg (line 882) | pub fn validate_msg(key_config: &SharedKeyConfig) -> CommandText {
function abort_merge (line 894) | pub fn abort_merge(key_config: &SharedKeyConfig) -> CommandText {
function abort_revert (line 905) | pub fn abort_revert(key_config: &SharedKeyConfig) -> CommandText {
function view_submodules (line 916) | pub fn view_submodules(
function view_remotes (line 929) | pub fn view_remotes(key_config: &SharedKeyConfig) -> CommandText {
function update_remote_name (line 940) | pub fn update_remote_name(
function update_remote_url (line 954) | pub fn update_remote_url(
function create_remote (line 968) | pub fn create_remote(
function delete_remote_popup (line 981) | pub fn delete_remote_popup(
function remote_confirm_name_msg (line 994) | pub fn remote_confirm_name_msg(
function remote_confirm_url_msg (line 1008) | pub fn remote_confirm_url_msg(
function open_submodule (line 1022) | pub fn open_submodule(
function open_submodule_parent (line 1035) | pub fn open_submodule_parent(
function update_submodule (line 1049) | pub fn update_submodule(
function continue_rebase (line 1062) | pub fn continue_rebase(
function abort_rebase (line 1075) | pub fn abort_rebase(key_config: &SharedKeyConfig) -> CommandText {
function select_staging (line 1086) | pub fn select_staging(
function select_unstaged (line 1098) | pub fn select_unstaged(
function undo_commit (line 1110) | pub fn undo_commit(key_config: &SharedKeyConfig) -> CommandText {
function commit_open (line 1120) | pub fn commit_open(key_config: &SharedKeyConfig) -> CommandText {
function commit_open_editor (line 1130) | pub fn commit_open_editor(
function commit_next_msg_from_history (line 1143) | pub fn commit_next_msg_from_history(
function commit_submit (line 1156) | pub fn commit_submit(
function newline (line 1169) | pub fn newline(key_config: &SharedKeyConfig) -> CommandText {
function toggle_verify (line 1180) | pub fn toggle_verify(
function commit_amend (line 1196) | pub fn commit_amend(key_config: &SharedKeyConfig) -> CommandText {
function commit_signoff (line 1206) | pub fn commit_signoff(
function edit_item (line 1218) | pub fn edit_item(key_config: &SharedKeyConfig) -> CommandText {
function stage_item (line 1228) | pub fn stage_item(key_config: &SharedKeyConfig) -> CommandText {
function stage_all (line 1239) | pub fn stage_all(key_config: &SharedKeyConfig) -> CommandText {
function unstage_item (line 1249) | pub fn unstage_item(key_config: &SharedKeyConfig) -> CommandText {
function unstage_all (line 1260) | pub fn unstage_all(key_config: &SharedKeyConfig) -> CommandText {
function reset_item (line 1270) | pub fn reset_item(key_config: &SharedKeyConfig) -> CommandText {
function ignore_item (line 1281) | pub fn ignore_item(key_config: &SharedKeyConfig) -> CommandText {
function diff_focus_left (line 1293) | pub fn diff_focus_left(
function diff_focus_right (line 1305) | pub fn diff_focus_right(
function quit (line 1317) | pub fn quit(key_config: &SharedKeyConfig) -> CommandText {
function confirm_action (line 1327) | pub fn confirm_action(
function stashing_save (line 1339) | pub fn stashing_save(
function stashing_toggle_indexed (line 1351) | pub fn stashing_toggle_indexed(
function stashing_toggle_untracked (line 1364) | pub fn stashing_toggle_untracked(
function stashing_confirm_msg (line 1378) | pub fn stashing_confirm_msg(
function stashlist_apply (line 1390) | pub fn stashlist_apply(
function stashlist_drop (line 1402) | pub fn stashlist_drop(
function stashlist_pop (line 1420) | pub fn stashlist_pop(
function stashlist_inspect (line 1432) | pub fn stashlist_inspect(
function log_details_toggle (line 1444) | pub fn log_details_toggle(
function commit_details_open (line 1457) | pub fn commit_details_open(
function blame_file (line 1470) | pub fn blame_file(key_config: &SharedKeyConfig) -> CommandText {
function open_file_history (line 1480) | pub fn open_file_history(
function open_line_number_popup (line 1492) | pub fn open_line_number_popup(
function log_tag_commit (line 1504) | pub fn log_tag_commit(
function log_checkout_commit (line 1516) | pub fn log_checkout_commit(
function inspect_file_tree (line 1529) | pub fn inspect_file_tree(
function revert_commit (line 1541) | pub fn revert_commit(
function log_reset_commit (line 1554) | pub fn log_reset_commit(
function log_reword_commit (line 1566) | pub fn log_reword_commit(
function log_find_commit (line 1579) | pub fn log_find_commit(
function log_close_search (line 1591) | pub fn log_close_search(
function reset_commit (line 1604) | pub fn reset_commit(key_config: &SharedKeyConfig) -> CommandText {
function reset_branch (line 1615) | pub fn reset_branch(key_config: &SharedKeyConfig) -> CommandText {
function reset_type (line 1626) | pub fn reset_type(key_config: &SharedKeyConfig) -> CommandText {
function tag_commit_confirm_msg (line 1637) | pub fn tag_commit_confirm_msg(
function tag_annotate_msg (line 1655) | pub fn tag_annotate_msg(
function create_branch_confirm_msg (line 1668) | pub fn create_branch_confirm_msg(
function open_branch_create_popup (line 1681) | pub fn open_branch_create_popup(
function rename_branch_confirm_msg (line 1693) | pub fn rename_branch_confirm_msg(
function rename_branch_popup (line 1706) | pub fn rename_branch_popup(
function delete_branch_popup (line 1718) | pub fn delete_branch_popup(
function merge_branch_popup (line 1730) | pub fn merge_branch_popup(
function branch_popup_rebase (line 1743) | pub fn branch_popup_rebase(
function compare_with_head (line 1756) | pub fn compare_with_head(
function compare_commits (line 1769) | pub fn compare_commits(
function select_branch_popup (line 1782) | pub fn select_branch_popup(
function toggle_branch_popup (line 1794) | pub fn toggle_branch_popup(
function open_branch_select_popup (line 1808) | pub fn open_branch_select_popup(
function open_tags_popup (line 1821) | pub fn open_tags_popup(
function delete_tag_popup (line 1833) | pub fn delete_tag_popup(
function select_tag (line 1845) | pub fn select_tag(key_config: &SharedKeyConfig) -> CommandText {
function status_push (line 1856) | pub fn status_push(key_config: &SharedKeyConfig) -> CommandText {
function status_force_push (line 1866) | pub fn status_force_push(
function status_fetch (line 1879) | pub fn status_fetch(key_config: &SharedKeyConfig) -> CommandText {
function status_pull (line 1889) | pub fn status_pull(key_config: &SharedKeyConfig) -> CommandText {
function fetch_remotes (line 1900) | pub fn fetch_remotes(
function find_commit_sha (line 1913) | pub fn find_commit_sha(
function goto_line (line 1926) | pub fn goto_line(key_config: &SharedKeyConfig) -> CommandText {
FILE: src/tabs/files.rs
type FilesTab (line 14) | pub struct FilesTab {
method new (line 22) | pub fn new(
method update (line 34) | pub fn update(&mut self) -> Result<()> {
method anything_pending (line 45) | pub fn anything_pending(&self) -> bool {
method update_async (line 50) | pub fn update_async(
method file_finder_update (line 61) | pub fn file_finder_update(&mut self, file: &Path) {
method draw (line 67) | fn draw(
method commands (line 80) | fn commands(
method event (line 92) | fn event(
method is_visible (line 103) | fn is_visible(&self) -> bool {
method hide (line 107) | fn hide(&mut self) {
method show (line 111) | fn show(&mut self) -> Result<()> {
FILE: src/tabs/revlog.rs
type LogSearchResult (line 45) | struct LogSearchResult {
type LogSearch (line 51) | enum LogSearch {
type Revlog (line 63) | pub struct Revlog {
method new (line 81) | pub fn new(env: &Environment) -> Self {
method any_work_pending (line 114) | pub fn any_work_pending(&self) -> bool {
method is_search_pending (line 123) | const fn is_search_pending(&self) -> bool {
method update (line 128) | pub fn update(&mut self) -> Result<()> {
method update_git (line 154) | pub fn update_git(
method selected_commit (line 203) | fn selected_commit(&self) -> Option<CommitId> {
method selected_commit_tags (line 207) | fn selected_commit_tags(
method select_commit (line 219) | pub fn select_commit(&mut self, id: CommitId) -> Result<()> {
method revert_commit (line 223) | fn revert_commit(&self) -> Result<()> {
method inspect_commit (line 232) | fn inspect_commit(&self) {
method search (line 244) | pub fn search(&mut self, options: LogFilterSearchOptions) {
method cancel_search (line 280) | fn cancel_search(&mut self) -> bool {
method update_search_state (line 292) | fn update_search_state(&mut self) {
method is_in_search_mode (line 346) | const fn is_in_search_mode(&self) -> bool {
method draw_search (line 350) | fn draw_search(&self, f: &mut Frame, area: Rect) {
method can_close_search (line 400) | const fn can_close_search(&self) -> bool {
method can_start_search (line 404) | fn can_start_search(&self) -> bool {
method draw (line 410) | fn draw(&self, f: &mut Frame, area: Rect) -> Result<()> {
method event (line 452) | fn event(&mut self, ev: &Event) -> Result<EventState> {
method commands (line 615) | fn commands(
method is_visible (line 736) | fn is_visible(&self) -> bool {
method hide (line 740) | fn hide(&mut self) {
method show (line 745) | fn show(&mut self) -> Result<()> {
FILE: src/tabs/stashing.rs
type StashingOptions (line 28) | pub struct StashingOptions {
type Stashing (line 33) | pub struct Stashing {
method new (line 48) | pub fn new(env: &Environment) -> Self {
method update (line 72) | pub fn update(&self) -> Result<()> {
method anything_pending (line 83) | pub fn anything_pending(&self) -> bool {
method update_git (line 88) | pub fn update_git(
method get_option_text (line 101) | fn get_option_text(&self) -> Vec<Line<'_>> {
method draw (line 136) | fn draw(
method commands (line 171) | fn commands(
method event (line 207) | fn event(
method is_visible (line 254) | fn is_visible(&self) -> bool {
method hide (line 258) | fn hide(&mut self) {
method show (line 262) | fn show(&mut self) -> Result<()> {
FILE: src/tabs/stashlist.rs
type StashList (line 16) | pub struct StashList {
method new (line 26) | pub fn new(env: &Environment) -> Self {
method update (line 40) | pub fn update(&mut self) -> Result<()> {
method apply_stash (line 49) | fn apply_stash(&self) {
method drop_stash (line 65) | fn drop_stash(&self) {
method pop_stash (line 77) | fn pop_stash(&self) {
method inspect (line 85) | fn inspect(&self) {
method action_confirmed (line 96) | pub fn action_confirmed(
method drop (line 110) | fn drop(
method pop (line 125) | fn pop(&mut self, repo: &RepoPath, id: CommitId) -> Result<()> {
method draw (line 138) | fn draw(
method commands (line 150) | fn commands(
method event (line 190) | fn event(
method is_visible (line 224) | fn is_visible(&self) -> bool {
method hide (line 228) | fn hide(&mut self) {
method show (line 232) | fn show(&mut self) -> Result<()> {
FILE: src/tabs/status.rs
type Focus (line 36) | enum Focus {
method toggled_focus (line 44) | const fn toggled_focus(&self) -> Self {
type DiffTarget (line 55) | enum DiffTarget {
type RemoteStatus (line 60) | struct RemoteStatus {
type Status (line 65) | pub struct Status {
method new (line 158) | pub fn new(env: &Environment) -> Self {
method draw_branch_state (line 206) | fn draw_branch_state(
method repo_state_text (line 245) | fn repo_state_text(repo: &RepoPath, state: &RepoState) -> String {
method draw_repo_state (line 287) | fn draw_repo_state(
method repo_state_unclean (line 316) | fn repo_state_unclean(&self) -> bool {
method can_focus_diff (line 320) | fn can_focus_diff(&self) -> bool {
method is_focus_on_diff (line 328) | fn is_focus_on_diff(&self) -> bool {
method switch_focus (line 332) | fn switch_focus(&mut self, f: Focus) -> Result<bool> {
method set_diff_target (line 361) | fn set_diff_target(&mut self, target: DiffTarget) {
method selected_path (line 369) | pub fn selected_path(&self) -> Option<(String, bool)> {
method update (line 384) | pub fn update(&mut self) -> Result<()> {
method anything_pending (line 411) | pub fn anything_pending(&self) -> bool {
method check_remotes (line 417) | fn check_remotes(&mut self) {
method update_git (line 431) | pub fn update_git(
method get_files_changes (line 454) | pub fn get_files_changes(&self) -> Result<Vec<StatusItem>> {
method update_status (line 458) | fn update_status(&mut self) -> Result<()> {
method update_diff (line 486) | pub fn update_diff(&mut self) -> Result<()> {
method request_diff (line 527) | fn request_diff(
method reset (line 543) | pub fn reset(&self, item: &ResetItem) -> bool {
method last_file_moved (line 558) | pub fn last_file_moved(&mut self) -> Result<()> {
method push (line 565) | fn push(&self, force: bool) {
method fetch (line 584) | fn fetch(&self) {
method pull (line 590) | fn pull(&self) {
method undo_last_commit (line 596) | fn undo_last_commit(&self) {
method branch_compare (line 601) | fn branch_compare(&mut self) {
method can_push (line 612) | fn can_push(&self) -> bool {
method can_fetch (line 621) | const fn can_fetch(&self) -> bool {
method can_abort_merge (line 626) | fn can_abort_merge(&self) -> bool {
method pending_rebase (line 630) | fn pending_rebase(&self) -> bool {
method pending_revert (line 634) | fn pending_revert(&self) -> bool {
method revert_pending_state (line 638) | pub fn revert_pending_state(&self) {
method abort_rebase (line 646) | pub fn abort_rebase(&self) {
method continue_rebase (line 654) | fn continue_rebase(&self) {
method commands_nav (line 662) | fn commands_nav(
method can_commit (line 708) | fn can_commit(&self) -> bool {
method draw (line 87) | fn draw(
method commands (line 716) | fn commands(
method event (line 815) | fn event(
method is_visible (line 959) | fn is_visible(&self) -> bool {
method hide (line 963) | fn hide(&mut self) {
method show (line 970) | fn show(&mut self) -> Result<()> {
FILE: src/ui/mod.rs
function calc_scroll_top (line 20) | pub const fn calc_scroll_top(
type Size (line 36) | pub struct Size {
method new (line 42) | pub const fn new(width: u16, height: u16) -> Self {
method from (line 48) | fn from(r: Rect) -> Self {
function centered_rect (line 58) | pub fn centered_rect(
function rect_inside (line 89) | pub fn rect_inside(min: Size, max: Size, r: Rect) -> Rect {
function centered_rect_absolute (line 113) | pub fn centered_rect_absolute(
function common_nav (line 127) | pub fn common_nav(
function test_small_rect_in_rect (line 163) | fn test_small_rect_in_rect() {
function test_small_rect_in_rect2 (line 193) | fn test_small_rect_in_rect2() {
FILE: src/ui/reflow.rs
constant NBSP (line 6) | const NBSP: &str = "\u{00a0}";
type LineComposer (line 11) | pub trait LineComposer<'a> {
method next_line (line 12) | fn next_line(&mut self) -> Option<(&[StyledGrapheme<'a>], u16)>;
type WordWrapper (line 16) | pub struct WordWrapper<'a, 'b> {
function new (line 26) | pub fn new(
function next_line (line 42) | fn next_line(&mut self) -> Option<(&[StyledGrapheme<'a>], u16)> {
type LineTruncator (line 143) | pub struct LineTruncator<'a, 'b> {
function new (line 152) | pub fn new(
function set_horizontal_offset (line 164) | pub const fn set_horizontal_offset(
function next_line (line 173) | fn next_line(&mut self) -> Option<(&[StyledGrapheme<'a>], u16)> {
type Composer (line 244) | enum Composer {
function run_composer (line 249) | fn run_composer(
function line_composer_one_line (line 281) | fn line_composer_one_line() {
function line_composer_short_lines (line 302) | fn line_composer_short_lines() {
function line_composer_long_word (line 320) | fn line_composer_long_word() {
function line_composer_long_sentence (line 346) | fn line_composer_long_sentence() {
function line_composer_zero_width (line 380) | fn line_composer_zero_width() {
function line_composer_max_line_width_of_1 (line 397) | fn line_composer_max_line_width_of_1() {
function line_composer_max_line_width_of_1_double_width_characters (line 417) | fn line_composer_max_line_width_of_1_double_width_characters() {
function line_composer_word_wrapper_mixed_length (line 434) | fn line_composer_word_wrapper_mixed_length() {
function line_composer_double_width_chars (line 455) | fn line_composer_double_width_chars() {
function line_composer_leading_whitespace_removal (line 482) | fn line_composer_leading_whitespace_removal() {
function line_composer_lots_of_spaces (line 501) | fn line_composer_lots_of_spaces() {
function line_composer_char_plus_lots_of_spaces (line 518) | fn line_composer_char_plus_lots_of_spaces() {
function line_composer_word_wrapper_double_width_chars_mixed_with_spaces (line 537) | fn line_composer_word_wrapper_double_width_chars_mixed_with_spaces(
function line_composer_word_wrapper_nbsp (line 568) | fn line_composer_word_wrapper_nbsp() {
function line_composer_word_wrapper_preserve_indentation (line 595) | fn line_composer_word_wrapper_preserve_indentation() {
function line_composer_word_wrapper_preserve_indentation_with_wrap (line 610) | fn line_composer_word_wrapper_preserve_indentation_with_wrap() {
function line_composer_word_wrapper_preserve_indentation_lots_of_whitespace (line 627) | fn line_composer_word_wrapper_preserve_indentation_lots_of_whitespace(
FILE: src/ui/scrollbar.rs
type Orientation (line 15) | pub enum Orientation {
type Scrollbar (line 21) | struct Scrollbar {
method new (line 30) | fn new(max: usize, pos: usize, orientation: Orientation) -> Self {
method render_vertical (line 40) | fn render_vertical(self, area: Rect, buf: &mut Buffer) {
method render_horizontal (line 77) | fn render_horizontal(self, area: Rect, buf: &mut Buffer) {
method render (line 121) | fn render(self, area: Rect, buf: &mut Buffer) {
function draw_scrollbar (line 131) | pub fn draw_scrollbar(
FILE: src/ui/scrolllist.rs
type ScrollableList (line 12) | struct ScrollableList<'b, L, S>
function new (line 29) | fn new(items: L) -> Self {
function block (line 37) | fn block(mut self, block: Block<'b>) -> Self {
method render (line 48) | fn render(self, area: Rect, buf: &mut Buffer) {
function draw_list (line 57) | pub fn draw_list<'b, L, S>(
function draw_list_block (line 77) | pub fn draw_list_block<'b, L, S>(
FILE: src/ui/stateful_paragraph.rs
function get_line_offset (line 14) | const fn get_line_offset(
type StatefulParagraph (line 31) | pub struct StatefulParagraph<'a> {
type ScrollPos (line 45) | pub struct ScrollPos {
type ParagraphState (line 51) | pub struct ParagraphState {
method lines (line 61) | pub const fn lines(self) -> u16 {
method height (line 65) | pub const fn height(self) -> u16 {
method scroll (line 69) | pub const fn scroll(self) -> ScrollPos {
method set_scroll (line 73) | pub const fn set_scroll(&mut self, scroll: ScrollPos) {
function new (line 79) | pub fn new<T>(text: T) -> Self
function block (line 92) | pub fn block(mut self, block: Block<'a>) -> Self {
function wrap (line 97) | pub const fn wrap(mut self, wrap: Wrap) -> Self {
type State (line 104) | type State = ParagraphState;
method render (line 106) | fn render(
FILE: src/ui/style.rs
type SharedTheme (line 10) | pub type SharedTheme = Rc<Theme>;
type Theme (line 14) | pub struct Theme {
method scroll_bar_pos (line 42) | pub fn scroll_bar_pos(&self) -> Style {
method block (line 46) | pub fn block(&self, focus: bool) -> Style {
method title (line 54) | pub fn title(&self, focused: bool) -> Style {
method branch (line 64) | pub fn branch(&self, selected: bool, head: bool) -> Style {
method tab (line 79) | pub fn tab(&self, selected: bool) -> Style {
method tags (line 89) | pub fn tags(&self, selected: bool) -> Style {
method text (line 100) | pub fn text(&self, enabled: bool, selected: bool) -> Style {
method item (line 111) | pub fn item(&self, typ: StatusItemType, selected: bool) -> Style {
method file_tree_item (line 134) | pub fn file_tree_item(
method apply_select (line 148) | const fn apply_select(
method option (line 164) | pub fn option(&self, on: bool) -> Style {
method diff_hunk_marker (line 172) | pub fn diff_hunk_marker(&self, selected: bool) -> Style {
method diff_line (line 180) | pub fn diff_line(
method text_danger (line 205) | pub fn text_danger(&self) -> Style {
method line_break (line 209) | pub fn line_break(&self) -> String {
method commandbar (line 213) | pub fn commandbar(&self, enabled: bool) -> Style {
method commit_hash (line 222) | pub fn commit_hash(&self, selected: bool) -> Style {
method commit_unhighlighted (line 229) | pub fn commit_unhighlighted(&self) -> Style {
method log_marker (line 233) | pub fn log_marker(&self, selected: bool) -> Style {
method commit_time (line 243) | pub fn commit_time(&self, selected: bool) -> Style {
method commit_author (line 250) | pub fn commit_author(&self, selected: bool) -> Style {
method commit_hash_in_blame (line 257) | pub fn commit_hash_in_blame(
method push_gauge (line 270) | pub fn push_gauge(&self) -> Style {
method attention_block (line 276) | pub fn attention_block() -> Style {
method load_patch (line 280) | fn load_patch(theme_path: &PathBuf) -> Result<ThemePatch> {
method load_old_theme (line 286) | fn load_old_theme(theme_path: &PathBuf) -> Result<Self> {
method save_patch (line 293) | fn save_patch(&self, theme_path: &PathBuf) -> Result<()> {
method get_syntax (line 303) | pub fn get_syntax(&self) -> String {
method init (line 307) | pub fn init(theme_path: &PathBuf) -> Self {
method default (line 331) | fn default() -> Self {
function test_smoke (line 370) | fn test_smoke() {
FILE: src/ui/syntax_text.rs
constant DEFAULT_SYNTAX_THEME (line 24) | pub const DEFAULT_SYNTAX_THEME: &str = "base16-eighties.dark";
type SyntaxLine (line 26) | struct SyntaxLine {
type SyntaxText (line 30) | pub struct SyntaxText {
method new (line 71) | pub fn new(
method path (line 171) | pub fn path(&self) -> &Path {
type AsyncProgressBuffer (line 40) | pub struct AsyncProgressBuffer {
method new (line 48) | pub const fn new(total: usize, min_interval: Duration) -> Self {
method send_progress (line 57) | pub fn send_progress(&mut self) -> ProgressPercent {
method update (line 62) | pub fn update(&mut self, current: usize) -> bool {
function from (line 177) | fn from(v: &'a SyntaxText) -> Self {
function syntact_style_to_tui (line 203) | fn syntact_style_to_tui(style: &Style) -> ratatui::style::Style {
type JobState (line 225) | enum JobState {
type AsyncSyntaxJob (line 231) | pub struct AsyncSyntaxJob {
method new (line 237) | pub fn new(
method result (line 251) | pub fn result(&self) -> Option<SyntaxText> {
type Notification (line 266) | type Notification = AsyncAppNotification;
type Progress (line 267) | type Progress = ProgressPercent;
method run (line 269) | fn run(
FILE: src/watcher.rs
type RepoWatcher (line 8) | pub struct RepoWatcher {
method new (line 13) | pub fn new(workdir: &str) -> Self {
method receiver (line 41) | pub fn receiver(&self) -> crossbeam_channel::Receiver<()> {
method forwarder (line 45) | fn forwarder(
function create_watcher (line 67) | fn create_watcher(
Condensed preview — 211 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (1,387K chars).
[
{
"path": ".cargo/config.toml",
"chars": 216,
"preview": "[target.aarch64-unknown-linux-gnu]\nlinker = \"aarch64-linux-gnu-gcc\"\n\n[target.arm-unknown-linux-gnueabihf]\nlinker = \"arm-"
},
{
"path": ".clippy.toml",
"chars": 52,
"preview": "msrv = \"1.88.0\"\ncognitive-complexity-threshold = 18\n"
},
{
"path": ".editorconfig",
"chars": 38,
"preview": "root = true\n[*.rs]\nindent_style = tab\n"
},
{
"path": ".github/FUNDING.yml",
"chars": 18,
"preview": "github: extrawurst"
},
{
"path": ".github/ISSUE_TEMPLATE/bug_report.md",
"chars": 703,
"preview": "---\nname: Bug report\nabout: Create a report to help us improve\ntitle: ''\nlabels: 'bug'\nassignees: ''\n\n---\n\n**Describe th"
},
{
"path": ".github/ISSUE_TEMPLATE/feature_request.md",
"chars": 610,
"preview": "---\nname: Feature request\nabout: Suggest an idea for this project\ntitle: ''\nlabels: 'feature-request'\nassignees: ''\n\n---"
},
{
"path": ".github/PULL_REQUEST_TEMPLATE.md",
"chars": 398,
"preview": "<!---\nThank you for contributing to GitUI! Please fill out the template below, and remove or add any\ninformation as you "
},
{
"path": ".github/dependabot.yml",
"chars": 291,
"preview": "version: 2\nupdates:\n- package-ecosystem: cargo\n directory: \"/\"\n schedule:\n interval: daily\n open-pull-requests-lim"
},
{
"path": ".github/stale.yml",
"chars": 718,
"preview": "# Number of days of inactivity before an issue becomes stale\ndaysUntilStale: 180\n# Number of days of inactivity before a"
},
{
"path": ".github/workflows/brew.yml",
"chars": 570,
"preview": "name: brew update\n\non:\n # only manually\n workflow_dispatch:\n inputs:\n tag-name:\n required: true\n "
},
{
"path": ".github/workflows/cd.yml",
"chars": 4117,
"preview": "name: CD\n\non:\n push:\n tags:\n - \"*\"\n workflow_dispatch:\n\npermissions:\n contents: write\n\njobs:\n release:\n s"
},
{
"path": ".github/workflows/ci.yml",
"chars": 8769,
"preview": "name: CI\n\non:\n schedule:\n - cron: \"0 2 * * *\"\n push:\n branches: [\"*\"]\n pull_request:\n branches: [master]\n\nen"
},
{
"path": ".github/workflows/nightly.yml",
"chars": 4451,
"preview": "name: Build Nightly Releases\n\non:\n schedule:\n - cron: \"0 3 * * *\"\n workflow_dispatch:\n\nenv:\n CARGO_TERM_COLOR: alw"
},
{
"path": ".gitignore",
"chars": 50,
"preview": "/target\n/release\n.DS_Store\n/.idea/\nflamegraph.svg\n"
},
{
"path": ".vscode/launch.json",
"chars": 299,
"preview": "{\n \"version\": \"0.2.0\",\n \"configurations\": [\n {\n \"name\": \"(OSX) Launch\",\n \"type\": \"lld"
},
{
"path": ".vscode/settings.json",
"chars": 97,
"preview": "{\n \"editor.formatOnSave\": true,\n \"workbench.settings.enableNaturalLanguageSearch\": false,\n}"
},
{
"path": "CHANGELOG.md",
"chars": 58933,
"preview": "# Changelog\n\nAll notable changes to this project will be documented in this file.\n\nThe format is based on [Keep a Change"
},
{
"path": "CONTRIBUTING.md",
"chars": 934,
"preview": "# Contributing\n\nWe’re glad you found this document that is intended to make contributing to\nGitUI as easy as possible!\n\n"
},
{
"path": "Cargo.toml",
"chars": 2921,
"preview": "[package]\nname = \"gitui\"\nversion = \"0.28.0\"\nauthors = [\"extrawurst <mail@rusticorn.com>\"]\ndescription = \"blazing fast te"
},
{
"path": "FAQ.md",
"chars": 1626,
"preview": "\n\n## <a name=\"table-of-contents\"></a> Table of Contents\n\n1. [\"Bad Credentials\" Error](#credentials)\n2. [Custom key bindi"
},
{
"path": "KEY_CONFIG.md",
"chars": 2148,
"preview": "# Key Config\n\nThe default keys are based on arrow keys to navigate.\n\nHowever popular demand lead to fully customizabilit"
},
{
"path": "LICENSE.md",
"chars": 1065,
"preview": "MIT License\n\nCopyright (c) 2025 gitui-org\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\n"
},
{
"path": "Makefile",
"chars": 3606,
"preview": "\n.PHONY: debug build-release release-linux-musl test clippy clippy-pedantic install install-debug sort\n\nARGS=-l\n# ARGS=-"
},
{
"path": "NIGHTLIES.md",
"chars": 997,
"preview": "# Nightlies\n\n**Use with caution as these binaries are build nightly and might be broken**\n\nWhen you find problems please"
},
{
"path": "README.md",
"chars": 10314,
"preview": "<h1 align=\"center\">\n<img width=\"300px\" src=\"assets/logo.png\" />\n\n[![CI][s0]][l0] [![crates][s1]][l1] ![MIT][s2] [\n\n## Configuration\n\nTo change the colors of the default "
},
{
"path": "assets/expandable-commands.drawio",
"chars": 1113,
"preview": "<mxfile host=\"app.diagrams.net\" modified=\"2020-05-23T13:11:59.516Z\" agent=\"5.0 (Macintosh; Intel Mac OS X 10_15_4) Apple"
},
{
"path": "assets/log-commit-info.drawio",
"chars": 2506,
"preview": "<mxfile host=\"app.diagrams.net\" modified=\"2020-06-02T16:58:00.925Z\" agent=\"5.0 (Macintosh; Intel Mac OS X 10_15_4) Apple"
},
{
"path": "assets/options.drawio",
"chars": 1495,
"preview": "<mxfile host=\"app.diagrams.net\" modified=\"2021-08-17T21:58:53.216Z\" agent=\"5.0 (Macintosh; Intel Mac OS X 10_15_7) Apple"
},
{
"path": "assets/stashing.drawio",
"chars": 6510,
"preview": "<mxfile host=\"app.diagrams.net\" modified=\"2020-05-19T12:41:55.023Z\" agent=\"5.0 (Macintosh; Intel Mac OS X 10_15_4) Apple"
},
{
"path": "asyncgit/Cargo.toml",
"chars": 1567,
"preview": "[package]\nname = \"asyncgit\"\nversion = \"0.28.0\"\nauthors = [\"extrawurst <mail@rusticorn.com>\"]\nedition = \"2021\"\ndescriptio"
},
{
"path": "asyncgit/README.md",
"chars": 719,
"preview": "# asyncgit\n\n*allow using git2 in an asynchronous context*\n\nThis crate is designed as part of the [gitui](http://gitui.or"
},
{
"path": "asyncgit/src/asyncjob/mod.rs",
"chars": 6895,
"preview": "//! provides `AsyncJob` trait and `AsyncSingleJob` struct\n\n#![deny(clippy::expect_used)]\n\nuse crate::error::Result;\nuse "
},
{
"path": "asyncgit/src/blame.rs",
"chars": 3515,
"preview": "use crate::{\n\terror::Result,\n\thash,\n\tsync::{self, CommitId, FileBlame, RepoPath},\n\tAsyncGitNotification,\n};\nuse crossbea"
},
{
"path": "asyncgit/src/branches.rs",
"chars": 1502,
"preview": "use crate::{\n\tasyncjob::{AsyncJob, RunParams},\n\terror::Result,\n\tsync::{branch::get_branches_info, BranchInfo, RepoPath},"
},
{
"path": "asyncgit/src/cached/branchname.rs",
"chars": 887,
"preview": "use crate::{\n\terror::Result,\n\tsync::{self, branch::get_branch_name, RepoPathRef},\n};\nuse sync::Head;\n\n///\npub struct Bra"
},
{
"path": "asyncgit/src/cached/mod.rs",
"chars": 190,
"preview": "//! cached lookups:\n//! parts of the sync api that might take longer\n//! to compute but change seldom so doing them asyn"
},
{
"path": "asyncgit/src/commit_files.rs",
"chars": 2792,
"preview": "use crate::{\n\terror::Result,\n\tsync::{self, commit_files::OldNew, CommitId, RepoPath},\n\tAsyncGitNotification, StatusItem,"
},
{
"path": "asyncgit/src/diff.rs",
"chars": 4201,
"preview": "use crate::{\n\terror::Result,\n\thash,\n\tsync::{\n\t\tself, commit_files::OldNew, diff::DiffOptions, CommitId,\n\t\tRepoPath,\n\t},\n"
},
{
"path": "asyncgit/src/error.rs",
"chars": 8259,
"preview": "use std::{\n\tnum::TryFromIntError, path::StripPrefixError,\n\tstring::FromUtf8Error,\n};\nuse thiserror::Error;\n\n///\n#[derive"
},
{
"path": "asyncgit/src/fetch_job.rs",
"chars": 1295,
"preview": "//!\n\nuse crate::{\n\tasyncjob::{AsyncJob, RunParams},\n\terror::Result,\n\tsync::remotes::fetch_all,\n\tsync::{cred::BasicAuthCr"
},
{
"path": "asyncgit/src/filter_commits.rs",
"chars": 4162,
"preview": "use rayon::{\n\tprelude::ParallelIterator,\n\tslice::{ParallelSlice, ParallelSliceMut},\n};\n\nuse crate::{\n\tasyncjob::{AsyncJo"
},
{
"path": "asyncgit/src/lib.rs",
"chars": 3272,
"preview": "/*!\n`AsyncGit` is a library that provides non-blocking access to Git\noperations, enabling `GitUI` to perform potentially"
},
{
"path": "asyncgit/src/progress.rs",
"chars": 987,
"preview": "//!\n\nuse easy_cast::{Conv, ConvFloat};\nuse std::cmp;\n\n///\n#[derive(Clone, Copy, Default, Debug, PartialEq, Eq)]\npub stru"
},
{
"path": "asyncgit/src/pull.rs",
"chars": 3374,
"preview": "use crate::{\n\terror::{Error, Result},\n\tsync::{\n\t\tcred::BasicAuthCredential,\n\t\tremotes::{fetch, push::ProgressNotificatio"
},
{
"path": "asyncgit/src/push.rs",
"chars": 3513,
"preview": "use crate::{\n\terror::{Error, Result},\n\tsync::{\n\t\tcred::BasicAuthCredential,\n\t\tremotes::push::push_raw,\n\t\tremotes::push::"
},
{
"path": "asyncgit/src/push_tags.rs",
"chars": 3169,
"preview": "use crate::{\n\terror::{Error, Result},\n\tsync::{\n\t\tcred::BasicAuthCredential,\n\t\tremotes::tags::{push_tags, PushTagsProgres"
},
{
"path": "asyncgit/src/remote_progress.rs",
"chars": 2806,
"preview": "//!\n\nuse crate::{\n\terror::Result,\n\tprogress::ProgressPercent,\n\tsync::remotes::push::{AsyncProgress, ProgressNotification"
},
{
"path": "asyncgit/src/remote_tags.rs",
"chars": 1676,
"preview": "//!\n\nuse crate::{\n\tasyncjob::{AsyncJob, RunParams},\n\terror::Result,\n\tsync::cred::BasicAuthCredential,\n\tsync::{\n\t\tremotes"
},
{
"path": "asyncgit/src/revlog.rs",
"chars": 9005,
"preview": "use crate::{\n\terror::Result,\n\tsync::{\n\t\tgix_repo, repo, CommitId, LogWalker, LogWalkerWithoutFilter,\n\t\tRepoPath, SharedC"
},
{
"path": "asyncgit/src/status.rs",
"chars": 3790,
"preview": "use crate::{\n\terror::Result,\n\thash,\n\tsync::{\n\t\tself, status::StatusType, RepoPath, ShowUntrackedFilesConfig,\n\t},\n\tAsyncG"
},
{
"path": "asyncgit/src/sync/blame.rs",
"chars": 5764,
"preview": "//! Sync git API for fetching a file blame\n\nuse super::{utils, CommitId, RepoPath};\nuse crate::{\n\terror::{Error, Result}"
},
{
"path": "asyncgit/src/sync/branch/merge_commit.rs",
"chars": 5713,
"preview": "//! merging from upstream\n\nuse super::BranchType;\nuse crate::{\n\terror::{Error, Result},\n\tsync::{merge_msg, repository::r"
},
{
"path": "asyncgit/src/sync/branch/merge_ff.rs",
"chars": 2705,
"preview": "//! merging from upstream\n\nuse super::BranchType;\nuse crate::{\n\terror::{Error, Result},\n\tsync::{repository::repo, RepoPa"
},
{
"path": "asyncgit/src/sync/branch/merge_rebase.rs",
"chars": 6610,
"preview": "//! merging from upstream (rebase)\n\nuse crate::{\n\terror::{Error, Result},\n\tsync::{\n\t\trebase::conflict_free_rebase, repos"
},
{
"path": "asyncgit/src/sync/branch/mod.rs",
"chars": 25432,
"preview": "//! branch functions\n\npub mod merge_commit;\npub mod merge_ff;\npub mod merge_rebase;\npub mod rename;\n\nuse super::{utils::"
},
{
"path": "asyncgit/src/sync/branch/rename.rs",
"chars": 1322,
"preview": "//! renaming of branches\n\nuse crate::{\n\terror::Result,\n\tsync::{repository::repo, RepoPath},\n};\nuse scopetime::scope_time"
},
{
"path": "asyncgit/src/sync/commit.rs",
"chars": 13405,
"preview": "//! Git Api for Commits\nuse super::{CommitId, RepoPath};\nuse crate::sync::sign::{SignBuilder, SignError};\nuse crate::{\n\t"
},
{
"path": "asyncgit/src/sync/commit_details.rs",
"chars": 4407,
"preview": "use super::{commits_info::get_message, CommitId, RepoPath};\nuse crate::{error::Result, sync::repository::repo};\nuse git2"
},
{
"path": "asyncgit/src/sync/commit_files.rs",
"chars": 5953,
"preview": "//! Functions for getting infos about files in commits\n\nuse super::{diff::DiffOptions, CommitId, RepoPath};\nuse crate::{"
},
{
"path": "asyncgit/src/sync/commit_filter.rs",
"chars": 4354,
"preview": "use super::{\n\tcommit_details::get_author_of_commit,\n\tcommit_files::get_commit_diff, CommitId,\n};\nuse crate::error::Resul"
},
{
"path": "asyncgit/src/sync/commit_revert.rs",
"chars": 909,
"preview": "use super::{CommitId, RepoPath};\nuse crate::{\n\terror::Result,\n\tsync::{repository::repo, utils::read_file},\n};\nuse scopet"
},
{
"path": "asyncgit/src/sync/commits_info.rs",
"chars": 8084,
"preview": "use std::fmt::Display;\n\nuse super::RepoPath;\nuse crate::{\n\terror::Result,\n\tsync::{\n\t\tcommit_details::get_author_of_commi"
},
{
"path": "asyncgit/src/sync/config.rs",
"chars": 4263,
"preview": "use crate::error::Result;\nuse git2::Repository;\nuse scopetime::scope_time;\nuse serde::{Deserialize, Serialize};\n\nuse sup"
},
{
"path": "asyncgit/src/sync/cred.rs",
"chars": 8996,
"preview": "//! credentials git helper\n\nuse super::{\n\tremotes::{\n\t\tget_default_remote_for_fetch_in_repo,\n\t\tget_default_remote_for_pu"
},
{
"path": "asyncgit/src/sync/diff.rs",
"chars": 14349,
"preview": "//! sync git api for fetching a diff\n\nuse super::{\n\tcommit_files::{\n\t\tget_commit_diff, get_compare_commits_diff, OldNew,"
},
{
"path": "asyncgit/src/sync/hooks.rs",
"chars": 11289,
"preview": "use super::{repository::repo, RepoPath};\nuse crate::{\n\terror::Result,\n\tsync::{\n\t\tbranch::get_branch_upstream_merge,\n\t\tco"
},
{
"path": "asyncgit/src/sync/hunks.rs",
"chars": 3844,
"preview": "use super::{\n\tdiff::{get_diff_raw, DiffOptions, HunkHeader},\n\tRepoPath,\n};\nuse crate::{\n\terror::{Error, Result},\n\thash,\n"
},
{
"path": "asyncgit/src/sync/ignore.rs",
"chars": 3922,
"preview": "use super::{utils::work_dir, RepoPath};\nuse crate::{\n\terror::{Error, Result},\n\tsync::repository::repo,\n};\nuse scopetime:"
},
{
"path": "asyncgit/src/sync/logwalker.rs",
"chars": 9670,
"preview": "use super::{CommitId, SharedCommitFilterFn};\nuse crate::error::Result;\nuse git2::{Commit, Oid, Repository};\nuse gix::rev"
},
{
"path": "asyncgit/src/sync/merge.rs",
"chars": 3608,
"preview": "use crate::{\n\terror::{Error, Result},\n\tsync::{\n\t\tbranch::merge_commit::commit_merge_with_head,\n\t\trebase::{\n\t\t\tabort_reba"
},
{
"path": "asyncgit/src/sync/mod.rs",
"chars": 9111,
"preview": "//! sync git api\n\n//TODO: remove once we have this activated on the toplevel\n#![deny(clippy::expect_used)]\n\npub mod blam"
},
{
"path": "asyncgit/src/sync/patches.rs",
"chars": 1584,
"preview": "use super::diff::{get_diff_raw, DiffOptions, HunkHeader};\nuse crate::error::{Error, Result};\nuse git2::{Diff, DiffLine, "
},
{
"path": "asyncgit/src/sync/rebase.rs",
"chars": 7378,
"preview": "use git2::{BranchType, Repository};\nuse scopetime::scope_time;\n\nuse crate::{\n\terror::{Error, Result},\n\tsync::repository:"
},
{
"path": "asyncgit/src/sync/remotes/callbacks.rs",
"chars": 5436,
"preview": "use super::push::ProgressNotification;\nuse crate::{error::Result, sync::cred::BasicAuthCredential};\nuse crossbeam_channe"
},
{
"path": "asyncgit/src/sync/remotes/mod.rs",
"chars": 14132,
"preview": "//!\n\nmod callbacks;\npub(crate) mod push;\npub(crate) mod tags;\n\nuse crate::{\n\terror::{Error, Result},\n\tsync::{\n\t\tcred::Ba"
},
{
"path": "asyncgit/src/sync/remotes/push.rs",
"chars": 10721,
"preview": "use crate::{\n\terror::{Error, Result},\n\tprogress::ProgressPercent,\n\tsync::{\n\t\tbranch::branch_set_upstream_after_push,\n\t\tc"
},
{
"path": "asyncgit/src/sync/remotes/tags.rs",
"chars": 9954,
"preview": "//!\n\nuse super::push::AsyncProgress;\nuse crate::{\n\terror::Result,\n\tprogress::ProgressPercent,\n\tsync::{\n\t\tcred::BasicAuth"
},
{
"path": "asyncgit/src/sync/repository.rs",
"chars": 1425,
"preview": "use std::{\n\tcell::RefCell,\n\tpath::{Path, PathBuf},\n};\n\nuse git2::{Repository, RepositoryOpenFlags};\n\nuse crate::error::R"
},
{
"path": "asyncgit/src/sync/reset.rs",
"chars": 7260,
"preview": "use super::{utils::get_head_repo, CommitId, RepoPath};\nuse crate::{error::Result, sync::repository::repo};\nuse git2::{bu"
},
{
"path": "asyncgit/src/sync/reword.rs",
"chars": 5207,
"preview": "use git2::{Oid, RebaseOptions, Repository};\n\nuse super::{\n\tcommit::signature_allow_undefined_name,\n\trepo,\n\tutils::{bytes"
},
{
"path": "asyncgit/src/sync/sign.rs",
"chars": 11813,
"preview": "//! Sign commit data.\n\nuse ssh_key::{HashAlg, LineEnding, PrivateKey};\nuse std::path::PathBuf;\n\n/// Error type for [`Sig"
},
{
"path": "asyncgit/src/sync/staging/discard_tracked.rs",
"chars": 5961,
"preview": "use super::{apply_selection, load_file};\nuse crate::{\n\terror::Result,\n\tsync::{\n\t\tdiff::DiffLinePosition, patches::get_fi"
},
{
"path": "asyncgit/src/sync/staging/mod.rs",
"chars": 4139,
"preview": "mod discard_tracked;\nmod stage_tracked;\n\npub use discard_tracked::discard_lines;\npub use stage_tracked::stage_lines;\n\nus"
},
{
"path": "asyncgit/src/sync/staging/stage_tracked.rs",
"chars": 3728,
"preview": "use super::apply_selection;\nuse crate::{\n\terror::{Error, Result},\n\tsync::{\n\t\tdiff::DiffLinePosition, patches::get_file_d"
},
{
"path": "asyncgit/src/sync/stash.rs",
"chars": 8305,
"preview": "use super::{CommitId, RepoPath};\nuse crate::{\n\terror::{Error, Result},\n\tsync::repository::repo,\n};\nuse git2::{\n\tbuild::C"
},
{
"path": "asyncgit/src/sync/state.rs",
"chars": 804,
"preview": "use super::RepoPath;\nuse crate::{error::Result, sync::repository::repo};\nuse git2::RepositoryState;\nuse scopetime::scope"
},
{
"path": "asyncgit/src/sync/status.rs",
"chars": 8519,
"preview": "//! sync git api for fetching a status\n\nuse crate::{\n\terror::Result,\n\tsync::{\n\t\tconfig::untracked_files_config_repo,\n\t\tr"
},
{
"path": "asyncgit/src/sync/submodules.rs",
"chars": 4280,
"preview": "use std::path::{Path, PathBuf};\n\nuse git2::{\n\tRepository, RepositoryOpenFlags, Submodule,\n\tSubmoduleUpdateOptions,\n};\nus"
},
{
"path": "asyncgit/src/sync/tags.rs",
"chars": 5038,
"preview": "use super::{get_commits_info, CommitId, RepoPath};\nuse crate::{\n\terror::Result,\n\tsync::{gix_repo, repository::repo},\n};\n"
},
{
"path": "asyncgit/src/sync/tree.rs",
"chars": 4992,
"preview": "use super::{CommitId, RepoPath};\nuse crate::{\n\terror::{Error, Result},\n\tsync::repository::repo,\n};\nuse git2::{Oid, Repos"
},
{
"path": "asyncgit/src/sync/utils.rs",
"chars": 11948,
"preview": "//! sync git api (various methods)\n\nuse super::{\n\trepository::repo, CommitId, RepoPath, ShowUntrackedFilesConfig,\n};\nuse"
},
{
"path": "asyncgit/src/tags.rs",
"chars": 3183,
"preview": "use crate::{\n\tasyncjob::{AsyncJob, AsyncSingleJob, RunParams},\n\terror::Result,\n\thash,\n\tsync::{self, RepoPath},\n\tAsyncGit"
},
{
"path": "asyncgit/src/treefiles.rs",
"chars": 1566,
"preview": "use crate::{\n\tasyncjob::{AsyncJob, RunParams},\n\terror::Result,\n\tsync::{tree_files, CommitId, RepoPath, TreeFile},\n\tAsync"
},
{
"path": "build.rs",
"chars": 1224,
"preview": "use chrono::TimeZone;\n\nfn get_git_hash() -> String {\n\tuse std::process::Command;\n\n\t// Allow builds from `git archive` ge"
},
{
"path": "deny.toml",
"chars": 2078,
"preview": "[licenses]\nallow = [\n \"MIT\",\n \"Apache-2.0\",\n \"BSD-2-Clause\",\n \"BSD-3-Clause\",\n \"CC0-1.0\",\n \"ISC\",\n "
},
{
"path": "filetreelist/Cargo.toml",
"chars": 575,
"preview": "[package]\nname = \"filetreelist\"\nversion = \"0.5.3\"\nauthors = [\"extrawurst <mail@rusticorn.com>\"]\nedition = \"2021\"\ndescrip"
},
{
"path": "filetreelist/README.md",
"chars": 429,
"preview": "# filetreelist\n\nThis crate is designed as part of the [gitui](http://gitui.org) project.\n\n`filetreelist` provides a very"
},
{
"path": "filetreelist/src/error.rs",
"chars": 298,
"preview": "use std::{num::TryFromIntError, path::PathBuf};\nuse thiserror::Error;\n\n#[derive(Error, Debug)]\npub enum Error {\n\t#[error"
},
{
"path": "filetreelist/src/filetree.rs",
"chars": 11146,
"preview": "use crate::{\n\terror::Result, filetreeitems::FileTreeItems,\n\ttree_iter::TreeIterator, TreeItemInfo,\n};\nuse std::{cell::Ce"
},
{
"path": "filetreelist/src/filetreeitems.rs",
"chars": 19876,
"preview": "use crate::{\n\terror::Error,\n\titem::{FileTreeItemKind, PathCollapsed},\n\tFileTreeItem,\n};\nuse crate::{error::Result, treei"
},
{
"path": "filetreelist/src/item.rs",
"chars": 4844,
"preview": "use crate::error::Result;\nuse std::path::{Path, PathBuf};\n\n/// holds the information shared among all `FileTreeItem` in "
},
{
"path": "filetreelist/src/lib.rs",
"chars": 800,
"preview": "// #![forbid(missing_docs)]\n#![forbid(unsafe_code)]\n#![deny(\n\tmismatched_lifetime_syntaxes,\n\tunused_imports,\n\tunused_mus"
},
{
"path": "filetreelist/src/tree_iter.rs",
"chars": 570,
"preview": "use crate::{item::FileTreeItem, treeitems_iter::TreeItemsIterator};\n\npub struct TreeIterator<'a> {\n\titem_iter: TreeItems"
},
{
"path": "filetreelist/src/treeitems_iter.rs",
"chars": 1080,
"preview": "use crate::{filetreeitems::FileTreeItems, item::FileTreeItem};\n\npub struct TreeItemsIterator<'a> {\n\ttree: &'a FileTreeIt"
},
{
"path": "git2-hooks/Cargo.toml",
"chars": 607,
"preview": "[package]\nname = \"git2-hooks\"\nversion = \"0.6.0\"\nauthors = [\"extrawurst <mail@rusticorn.com>\"]\nedition = \"2021\"\ndescripti"
},
{
"path": "git2-hooks/README.md",
"chars": 196,
"preview": "# git2-hooks\n\nadds git hook functionality on top of git2-rs\n\n## todo\n\n- [ ] unittest coverage symlinks from `.git/hooks/"
},
{
"path": "git2-hooks/src/error.rs",
"chars": 543,
"preview": "use thiserror::Error;\n\n/// crate specific error type\n#[derive(Error, Debug)]\npub enum HooksError {\n\t#[error(\"git error:{"
},
{
"path": "git2-hooks/src/hookspath.rs",
"chars": 9488,
"preview": "use git2::Repository;\n\nuse crate::{error::Result, HookResult, HooksError};\n\nuse std::{\n\tffi::{OsStr, OsString},\n\tpath::{"
},
{
"path": "git2-hooks/src/lib.rs",
"chars": 26416,
"preview": "//! git2-rs addon supporting git hooks\n//!\n//! we look for hooks in the following locations:\n//! * whatever `config.hoo"
},
{
"path": "git2-testing/Cargo.toml",
"chars": 456,
"preview": "[package]\nname = \"git2-testing\"\nversion = \"0.1.0\"\nauthors = [\"extrawurst <mail@rusticorn.com>\"]\nedition = \"2021\"\ndescrip"
},
{
"path": "git2-testing/README.md",
"chars": 105,
"preview": "# git2-testing\n\n*convenience functions on top of git2-rs for convenient unittest repository generation*\n\n"
},
{
"path": "git2-testing/src/lib.rs",
"chars": 2230,
"preview": "#![deny(mismatched_lifetime_syntaxes)]\n\nuse git2::Repository;\nuse tempfile::TempDir;\n\n/// initialize test repo in temp p"
},
{
"path": "invalidstring/Cargo.toml",
"chars": 401,
"preview": "[package]\nname = \"invalidstring\"\nversion = \"0.1.3\"\nauthors = [\"extrawurst <mail@rusticorn.com>\"]\nedition = \"2021\"\ndescri"
},
{
"path": "invalidstring/README.md",
"chars": 204,
"preview": "# invalidstring\n\n*just for testing invalid string data*\n\nThis crate is part of the [gitui](http://gitui.org) project. We"
},
{
"path": "invalidstring/src/lib.rs",
"chars": 289,
"preview": "#![deny(mismatched_lifetime_syntaxes)]\n\n/// uses unsafe to postfix the string with invalid utf8 data\n#[allow(invalid_fro"
},
{
"path": "rust-toolchain.toml",
"chars": 51,
"preview": "[toolchain]\nchannel = \"stable\"\nprofile = \"default\"\n"
},
{
"path": "rustfmt.toml",
"chars": 48,
"preview": "max_width=70\nhard_tabs=true\nnewline_style=\"Unix\""
},
{
"path": "scopetime/Cargo.toml",
"chars": 440,
"preview": "[package]\nname = \"scopetime\"\nversion = \"0.1.2\"\nauthors = [\"extrawurst <mail@rusticorn.com>\"]\nedition = \"2021\"\ndescriptio"
},
{
"path": "scopetime/README.md",
"chars": 524,
"preview": "# scopetime\n\n*log runtime of arbitrary scope*\n\nThis crate is part of the [gitui](http://gitui.org) project and can be us"
},
{
"path": "scopetime/src/lib.rs",
"chars": 1179,
"preview": "//! simple macro to insert a scope based runtime measure that logs the result\n\n#![forbid(unsafe_code)]\n#![deny(mismatche"
},
{
"path": "src/app.rs",
"chars": 30849,
"preview": "use crate::{\n\taccessors,\n\targs::CliArgs,\n\tcmdbar::CommandBar,\n\tcomponents::{\n\t\tcommand_pump, event_pump, CommandInfo, Co"
},
{
"path": "src/args.rs",
"chars": 5876,
"preview": "use crate::bug_report;\nuse anyhow::{anyhow, Context, Result};\nuse asyncgit::sync::RepoPath;\nuse clap::{\n\tbuilder::ArgPre"
},
{
"path": "src/bug_report.rs",
"chars": 483,
"preview": "use bugreport::{\n\tbugreport,\n\tcollector::{\n\t\tCommandLine, CompileTimeInformation, EnvironmentVariables,\n\t\tOperatingSyste"
},
{
"path": "src/clipboard.rs",
"chars": 3822,
"preview": "use anyhow::{anyhow, Result};\nuse std::io::Write;\nuse std::path::PathBuf;\nuse std::process::{Command, Stdio};\nuse which:"
},
{
"path": "src/cmdbar.rs",
"chars": 3803,
"preview": "use crate::{\n\tcomponents::CommandInfo, keys::SharedKeyConfig, strings,\n\tui::style::SharedTheme,\n};\nuse ratatui::{\n\tlayou"
},
{
"path": "src/components/changes.rs",
"chars": 6980,
"preview": "use super::{\n\tstatus_tree::StatusTreeComponent,\n\tutils::filetree::{FileTreeItem, FileTreeItemKind},\n\tCommandBlocking, Dr"
},
{
"path": "src/components/command.rs",
"chars": 1312,
"preview": "use crate::strings::order;\n\n///\n#[derive(Clone, PartialEq, PartialOrd, Ord, Eq)]\npub struct CommandText {\n\t///\n\tpub name"
},
{
"path": "src/components/commit_details/compare_details.rs",
"chars": 3221,
"preview": "use std::borrow::Cow;\n\nuse crate::{\n\tapp::Environment,\n\tcomponents::{\n\t\tcommit_details::style::{style_detail, Detail},\n\t"
},
{
"path": "src/components/commit_details/details.rs",
"chars": 10961,
"preview": "use crate::{\n\tapp::Environment,\n\tcomponents::{\n\t\tcommit_details::style::style_detail,\n\t\tdialog_paragraph,\n\t\tutils::{scro"
},
{
"path": "src/components/commit_details/mod.rs",
"chars": 5432,
"preview": "mod compare_details;\nmod details;\nmod style;\n\nuse super::{\n\tcommand_pump, event_pump, CommandBlocking, CommandInfo,\n\tCom"
},
{
"path": "src/components/commit_details/style.rs",
"chars": 846,
"preview": "use crate::{strings, ui::style::SharedTheme};\nuse ratatui::text::Span;\nuse std::borrow::Cow;\n\npub enum Detail {\n\tAuthor,"
},
{
"path": "src/components/commitlist.rs",
"chars": 24011,
"preview": "use super::utils::logitems::{ItemBatch, LogEntry};\nuse crate::{\n\tapp::Environment,\n\tcomponents::{\n\t\tutils::string_width_"
},
{
"path": "src/components/cred.rs",
"chars": 3620,
"preview": "use anyhow::Result;\nuse crossterm::event::Event;\nuse ratatui::{layout::Rect, Frame};\n\nuse asyncgit::sync::cred::BasicAut"
},
{
"path": "src/components/diff.rs",
"chars": 22529,
"preview": "use super::{\n\tutils::scroll_horizontal::HorizontalScroll,\n\tutils::scroll_vertical::VerticalScroll, CommandBlocking,\n\tDir"
},
{
"path": "src/components/mod.rs",
"chars": 6632,
"preview": "/*!\nComponents are the visible building blocks in gitui.\n\nThey have a state, handle events, and render to the terminal:\n"
},
{
"path": "src/components/revision_files.rs",
"chars": 13186,
"preview": "use super::{\n\tutils::scroll_vertical::VerticalScroll, CommandBlocking,\n\tCommandInfo, Component, DrawableComponent, Event"
},
{
"path": "src/components/status_tree.rs",
"chars": 15054,
"preview": "use super::{\n\tutils::{\n\t\tfiletree::{FileTreeItem, FileTreeItemKind},\n\t\tstatustree::{MoveSelection, StatusTree},\n\t},\n\tCom"
},
{
"path": "src/components/syntax_text.rs",
"chars": 6234,
"preview": "use super::{\n\tCommandBlocking, CommandInfo, Component, DrawableComponent,\n\tEventState,\n};\nuse crate::{\n\tapp::Environment"
},
{
"path": "src/components/textinput.rs",
"chars": 18194,
"preview": "use crate::app::Environment;\nuse crate::keys::key_match;\nuse crate::ui::Size;\nuse crate::{\n\tcomponents::{\n\t\tvisibility_b"
},
{
"path": "src/components/utils/emoji.rs",
"chars": 358,
"preview": "use once_cell::sync::Lazy;\nuse std::borrow::Cow;\n\nstatic EMOJI_REPLACER: Lazy<gh_emoji::Replacer> =\n\tLazy::new(gh_emoji:"
},
{
"path": "src/components/utils/filetree.rs",
"chars": 8376,
"preview": "//TODO: remove in favour of new `filetreelist` crate\n\nuse anyhow::{bail, Result};\nuse asyncgit::StatusItem;\nuse std::{\n\t"
},
{
"path": "src/components/utils/logitems.rs",
"chars": 4647,
"preview": "use asyncgit::sync::{CommitId, CommitInfo};\nuse chrono::{DateTime, Duration, Local, Utc};\nuse indexmap::IndexSet;\nuse st"
},
{
"path": "src/components/utils/mod.rs",
"chars": 1573,
"preview": "use chrono::{DateTime, Local, Utc};\nuse unicode_width::UnicodeWidthStr;\n\n#[cfg(feature = \"ghemoji\")]\npub mod emoji;\npub "
},
{
"path": "src/components/utils/scroll_horizontal.rs",
"chars": 2412,
"preview": "use crate::{\n\tcomponents::HorizontalScrollType,\n\tui::{draw_scrollbar, style::SharedTheme, Orientation},\n};\nuse ratatui::"
},
{
"path": "src/components/utils/scroll_vertical.rs",
"chars": 4884,
"preview": "use crate::{\n\tcomponents::ScrollType,\n\tui::{draw_scrollbar, style::SharedTheme, Orientation},\n};\nuse ratatui::{layout::R"
},
{
"path": "src/components/utils/statustree.rs",
"chars": 19332,
"preview": "use super::filetree::{\n\tFileTreeItem, FileTreeItemKind, FileTreeItems, PathCollapsed,\n};\nuse anyhow::Result;\nuse asyncgi"
},
{
"path": "src/input.rs",
"chars": 3254,
"preview": "use crate::notify_mutex::NotifiableMutex;\nuse anyhow::Result;\nuse crossbeam_channel::{unbounded, Receiver, Sender};\nuse "
},
{
"path": "src/keys/key_config.rs",
"chars": 6819,
"preview": "use anyhow::Result;\nuse crossterm::event::{KeyCode, KeyModifiers};\nuse std::{fs::canonicalize, path::PathBuf, rc::Rc};\n\n"
},
{
"path": "src/keys/key_list.rs",
"chars": 12847,
"preview": "use crossterm::event::{KeyCode, KeyEvent, KeyModifiers};\nuse serde::{Deserialize, Serialize};\nuse std::{fs::File, path::"
},
{
"path": "src/keys/mod.rs",
"chars": 123,
"preview": "mod key_config;\nmod key_list;\nmod symbols;\n\npub use key_config::{KeyConfig, SharedKeyConfig};\npub use key_list::key_matc"
},
{
"path": "src/keys/symbols.rs",
"chars": 3282,
"preview": "use std::{fs::File, io::Read, path::PathBuf};\n\nuse anyhow::Result;\nuse serde::{Deserialize, Serialize};\n\n#[derive(Debug,"
},
{
"path": "src/main.rs",
"chars": 9820,
"preview": "//!\n//! The gitui program is a text-based UI for working with a Git repository.\n//! The main navigation occurs between a"
},
{
"path": "src/notify_mutex.rs",
"chars": 891,
"preview": "use std::sync::{Arc, Condvar, Mutex};\n\n/// combines a `Mutex` and `Condvar` to allow waiting for a change in the variabl"
},
{
"path": "src/options.rs",
"chars": 3548,
"preview": "use anyhow::Result;\nuse asyncgit::sync::{\n\tdiff::DiffOptions, repo_dir, RepoPathRef,\n\tShowUntrackedFilesConfig,\n};\nuse r"
},
{
"path": "src/popup_stack.rs",
"chars": 298,
"preview": "use crate::queue::StackablePopupOpen;\n\n#[derive(Default)]\npub struct PopupStack {\n\tstack: Vec<StackablePopupOpen>,\n}\n\nim"
},
{
"path": "src/popups/blame_file.rs",
"chars": 18881,
"preview": "use crate::{\n\tapp::Environment,\n\tcomponents::{\n\t\tstring_width_align, time_to_string, visibility_blocking,\n\t\tCommandBlock"
},
{
"path": "src/popups/branchlist.rs",
"chars": 17844,
"preview": "use crate::components::{\n\tvisibility_blocking, CommandBlocking, CommandInfo, Component,\n\tDrawableComponent, EventState, "
},
{
"path": "src/popups/checkout_option.rs",
"chars": 4851,
"preview": "use crate::components::{\n\tvisibility_blocking, CommandBlocking, CommandInfo, Component,\n\tDrawableComponent, EventState,\n"
},
{
"path": "src/popups/commit.rs",
"chars": 14004,
"preview": "use crate::components::{\n\tvisibility_blocking, CommandBlocking, CommandInfo, Component,\n\tDrawableComponent, EventState, "
},
{
"path": "src/popups/compare_commits.rs",
"chars": 6201,
"preview": "use crate::components::{\n\tcommand_pump, event_pump, visibility_blocking, CommandBlocking,\n\tCommandInfo, CommitDetailsCom"
},
{
"path": "src/popups/confirm.rs",
"chars": 6363,
"preview": "use crate::{\n\tapp::Environment,\n\tcomponents::{\n\t\tvisibility_blocking, CommandBlocking, CommandInfo, Component,\n\t\tDrawabl"
},
{
"path": "src/popups/create_branch.rs",
"chars": 3456,
"preview": "use crate::components::{\n\tvisibility_blocking, CommandBlocking, CommandInfo, Component,\n\tDrawableComponent, EventState, "
},
{
"path": "src/popups/create_remote.rs",
"chars": 4362,
"preview": "use anyhow::Result;\nuse asyncgit::sync::{self, validate_remote_name, RepoPathRef};\nuse crossterm::event::Event;\nuse easy"
},
{
"path": "src/popups/externaleditor.rs",
"chars": 4061,
"preview": "use crate::{\n\tapp::Environment,\n\tcomponents::{\n\t\tvisibility_blocking, CommandBlocking, CommandInfo, Component,\n\t\tDrawabl"
},
{
"path": "src/popups/fetch.rs",
"chars": 4291,
"preview": "use crate::{\n\tapp::Environment,\n\tcomponents::{\n\t\tvisibility_blocking, CommandBlocking, CommandInfo, Component,\n\t\tCredCom"
},
{
"path": "src/popups/file_revlog.rs",
"chars": 14800,
"preview": "use crate::{\n\tapp::Environment,\n\tcomponents::{\n\t\tevent_pump, visibility_blocking, CommandBlocking,\n\t\tCommandInfo, Compon"
},
{
"path": "src/popups/fuzzy_find.rs",
"chars": 7904,
"preview": "use crate::components::{\n\tvisibility_blocking, CommandBlocking, CommandInfo, Component,\n\tDrawableComponent, EventState, "
},
{
"path": "src/popups/goto_line.rs",
"chars": 3821,
"preview": "use crate::{\n\tapp::Environment,\n\tcomponents::{\n\t\tvisibility_blocking, CommandBlocking, CommandInfo, Component,\n\t\tDrawabl"
},
{
"path": "src/popups/help.rs",
"chars": 5274,
"preview": "use crate::components::{\n\tvisibility_blocking, CommandBlocking, CommandInfo, Component,\n\tDrawableComponent, EventState,\n"
},
{
"path": "src/popups/inspect_commit.rs",
"chars": 6769,
"preview": "use crate::components::{\n\tcommand_pump, event_pump, visibility_blocking, CommandBlocking,\n\tCommandInfo, CommitDetailsCom"
},
{
"path": "src/popups/log_search.rs",
"chars": 13580,
"preview": "use crate::components::{\n\tvisibility_blocking, CommandBlocking, CommandInfo, Component,\n\tDrawableComponent, EventState, "
},
{
"path": "src/popups/mod.rs",
"chars": 2334,
"preview": "mod blame_file;\nmod branchlist;\nmod checkout_option;\nmod commit;\nmod compare_commits;\nmod confirm;\nmod create_branch;\nmo"
},
{
"path": "src/popups/msg.rs",
"chars": 4272,
"preview": "use crate::components::{\n\tvisibility_blocking, CommandBlocking, CommandInfo, Component,\n\tDrawableComponent, EventState, "
},
{
"path": "src/popups/options.rs",
"chars": 7895,
"preview": "use crate::{\n\tapp::Environment,\n\tcomponents::{\n\t\tstring_width_align, visibility_blocking, CommandBlocking,\n\t\tCommandInfo"
},
{
"path": "src/popups/pull.rs",
"chars": 5996,
"preview": "use crate::{\n\tapp::Environment,\n\tcomponents::{\n\t\tvisibility_blocking, CommandBlocking, CommandInfo, Component,\n\t\tCredCom"
},
{
"path": "src/popups/push.rs",
"chars": 7646,
"preview": "use crate::{\n\tapp::Environment,\n\tcomponents::{\n\t\tvisibility_blocking, CommandBlocking, CommandInfo, Component,\n\t\tCredCom"
},
{
"path": "src/popups/push_tags.rs",
"chars": 5728,
"preview": "use crate::{\n\tapp::Environment,\n\tcomponents::{\n\t\tvisibility_blocking, CommandBlocking, CommandInfo, Component,\n\t\tCredCom"
},
{
"path": "src/popups/remotelist.rs",
"chars": 11185,
"preview": "use std::cell::Cell;\n\nuse asyncgit::sync::{get_remote_url, get_remotes, RepoPathRef};\nuse ratatui::{\n\tlayout::{\n\t\tAlignm"
},
{
"path": "src/popups/rename_branch.rs",
"chars": 3953,
"preview": "use crate::components::{\n\tvisibility_blocking, CommandBlocking, CommandInfo, Component,\n\tDrawableComponent, EventState, "
},
{
"path": "src/popups/rename_remote.rs",
"chars": 3748,
"preview": "use anyhow::Result;\nuse asyncgit::sync::{self, RepoPathRef};\nuse crossterm::event::Event;\nuse easy_cast::Cast;\nuse ratat"
},
{
"path": "src/popups/reset.rs",
"chars": 5304,
"preview": "use crate::components::{\n\tvisibility_blocking, CommandBlocking, CommandInfo, Component,\n\tDrawableComponent, EventState,\n"
},
{
"path": "src/popups/revision_files.rs",
"chars": 3139,
"preview": "use crate::components::{\n\tvisibility_blocking, CommandBlocking, CommandInfo, Component,\n\tDrawableComponent, EventState, "
},
{
"path": "src/popups/stashmsg.rs",
"chars": 3061,
"preview": "use crate::components::{\n\tvisibility_blocking, CommandBlocking, CommandInfo, Component,\n\tDrawableComponent, EventState, "
},
{
"path": "src/popups/submodules.rs",
"chars": 11903,
"preview": "use crate::{\n\tapp::Environment,\n\tcomponents::{\n\t\tvisibility_blocking, CommandBlocking, CommandInfo, Component,\n\t\tDrawabl"
},
{
"path": "src/popups/tag_commit.rs",
"chars": 4953,
"preview": "use crate::components::{\n\tvisibility_blocking, CommandBlocking, CommandInfo, Component,\n\tDrawableComponent, EventState, "
},
{
"path": "src/popups/taglist.rs",
"chars": 11416,
"preview": "use crate::components::{\n\ttime_to_string, visibility_blocking, CommandBlocking,\n\tCommandInfo, Component, DrawableCompone"
},
{
"path": "src/popups/update_remote_url.rs",
"chars": 3048,
"preview": "use anyhow::Result;\nuse asyncgit::sync::{self, RepoPathRef};\nuse crossterm::event::Event;\n\nuse crate::{\n\tapp::Environmen"
},
{
"path": "src/queue.rs",
"chars": 3387,
"preview": "use crate::{\n\tcomponents::FuzzyFinderTarget,\n\tpopups::{\n\t\tAppOption, BlameFileOpen, FileRevOpen, FileTreeOpen,\n\t\tInspect"
},
{
"path": "src/spinner.rs",
"chars": 1326,
"preview": "use ratatui::{\n\tbackend::{Backend, CrosstermBackend},\n\tTerminal,\n};\nuse std::{cell::Cell, char, io};\n\n// static SPINNER_"
},
{
"path": "src/string_utils.rs",
"chars": 1094,
"preview": "use unicode_segmentation::UnicodeSegmentation;\nuse unicode_width::UnicodeWidthStr;\n\n///\npub fn trim_length_left(s: &str,"
},
{
"path": "src/strings.rs",
"chars": 43710,
"preview": "use std::borrow::Cow;\n\nuse asyncgit::sync::CommitId;\nuse unicode_truncate::UnicodeTruncateStr;\nuse unicode_width::Unicod"
},
{
"path": "src/tabs/files.rs",
"chars": 1933,
"preview": "use std::path::{Path, PathBuf};\n\nuse crate::{\n\tapp::Environment,\n\tcomponents::{\n\t\tvisibility_blocking, CommandBlocking, "
},
{
"path": "src/tabs/mod.rs",
"chars": 626,
"preview": "/*!\nThe tabs module contains a struct for each of the tabs visible in the\nui:\n\n- [`Status`]: Stage changes, push, pull\n-"
},
{
"path": "src/tabs/revlog.rs",
"chars": 17565,
"preview": "use crate::{\n\tapp::Environment,\n\tcomponents::{\n\t\tvisibility_blocking, CommandBlocking, CommandInfo,\n\t\tCommitDetailsCompo"
},
{
"path": "src/tabs/stashing.rs",
"chars": 5719,
"preview": "use crate::{\n\taccessors,\n\tapp::Environment,\n\tcomponents::{\n\t\tcommand_pump, event_pump, visibility_blocking,\n\t\tCommandBlo"
},
{
"path": "src/tabs/stashlist.rs",
"chars": 4667,
"preview": "use crate::{\n\tapp::Environment,\n\tcomponents::{\n\t\tvisibility_blocking, CommandBlocking, CommandInfo,\n\t\tCommitList, Compon"
},
{
"path": "src/tabs/status.rs",
"chars": 21344,
"preview": "use crate::{\n\taccessors,\n\tapp::Environment,\n\tcomponents::{\n\t\tcommand_pump, event_pump, visibility_blocking,\n\t\tChangesCom"
},
{
"path": "src/ui/mod.rs",
"chars": 4396,
"preview": "mod reflow;\nmod scrollbar;\nmod scrolllist;\nmod stateful_paragraph;\npub mod style;\nmod syntax_text;\n\nuse filetreelist::Mo"
},
{
"path": "src/ui/reflow.rs",
"chars": 16773,
"preview": "use crate::string_utils::trim_offset;\nuse easy_cast::Cast;\nuse ratatui::text::StyledGrapheme;\nuse unicode_width::Unicode"
}
]
// ... and 11 more files (download for full content)
About this extraction
This page contains the full source code of the gitui-org/gitui GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 211 files (1.2 MB), approximately 356.5k tokens, and a symbol index with 2506 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.
Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.