Compare commits

..

1257 commits

Author SHA1 Message Date
WaterWhisperer
8619c07f3f
Open editor in diff view (#2904) 2026-04-10 19:38:29 +01:00
Christoph Rüßler
7c538e3873
Add first snapshot tests using insta (#2813) 2026-04-07 10:33:43 +01:00
extrawurst
b1db21e10a
fix panic when opening submodule (#2896)
this is caused by us dropping the git notify channel and creating a new one when opening the submodule.

closes #2895
2026-03-31 12:38:05 +01:00
extrawurst
1083006a55 cleanup deny ignore
after we finally upgraded ratatui
2026-03-31 12:09:57 +01:00
Jean-Yves LENHOF
693defde15
docs(README): add mise alternative method installation (#2817) 2026-03-29 20:17:28 +01:00
pm100
8b2de1171b
migrate from tui-textarea to ratatui-textarea (#2889) 2026-03-29 00:45:37 +01:00
extrawurst
0cf38b5def
use tombi for toml formatting (#2894) 2026-03-28 21:02:05 +01:00
extrawurst
a57cbf2806 fix changelog 2026-03-25 10:29:03 +01:00
Tillerino
3ad42d23c4
take "error" result from binary search into account (#2767)
Co-authored-by: extrawurst <776816+extrawurst@users.noreply.github.com>
2026-03-25 10:27:25 +01:00
extrawurst
e24fb45df1 version bump and formatting 2026-03-25 00:15:29 +01:00
extrawurst
6aeffb4be8 cargo sort 2026-03-21 12:55:31 +00:00
extrawurst
09b68f8f5b more cargo pumps 2026-03-21 12:49:38 +00:00
extrawurst
98e130f8c8 release prep 2026-03-21 12:45:40 +00:00
extrawurst
d47f29effb
update chrono two-face and anyhow (#2881) 2026-03-20 12:47:53 +00:00
Danny Stoll
49555ce966
perf: prevent repeated status fetches in large repos (#2824)
* perf: prevent repeated status fetches in large repos

Replace time-based cache invalidation with a generation counter.

The old `StatusParams` included a millisecond timestamp (tick) in its
hash, causing the cache to invalidate on every UI tick. For large repos
with millions of files, this led to repeated index loading and 5+ minute load times.

This change uses a different strategy to manage cache invalidation:
- Remove tick from StatusParams hash
- Add generation counter that increments after each fetch completes
- Include generation in cache hash

This ensures:
- No repeated fetches while one is already pending, making gitui usable
  in large repos
- New fetch starts immediately after completion, keeping gitui responsive
  in small repos
- External file changes will still be detected on the next fetch cycle

* fix changelog

---------

Co-authored-by: Daniel Stoll <dstoll@radix.trade>
Co-authored-by: extrawurst <776816+extrawurst@users.noreply.github.com>
Co-authored-by: extrawurst <mail@rusticorn.com>
2026-03-20 01:12:14 +00:00
hlsxx
3cf7a818d1
feat: build.rs version message (#2839)
* feat: build.rs version message

* doc: changelog

---------

Co-authored-by: extrawurst <776816+extrawurst@users.noreply.github.com>
2026-03-20 00:35:21 +00:00
extrawurst
b18becdec6
bump env_logger (#2880) 2026-03-20 00:17:31 +00:00
xvchris
06a3f660b2
fix: guard rename/update_url actions against empty remote list (#2870)
* fix: guard rename/update_url actions against empty remote list

The rename_remote() and update_remote_url() event handlers in
RemoteListPopup did not check valid_selection() before indexing
into self.remote_names, causing a panic (index out of bounds)
when no remotes are configured.

The delete_remote() handler already had this guard. This commit
adds the same valid_selection() check to the other two handlers
for consistency.

Fixes #2868
Fixes #2869

* chore: add changelog entry and sort Cargo.toml dependencies

* revert: restore original Cargo.toml formatting
2026-03-19 22:51:09 +00:00
Ang
09d6726675
Fix typos (#2649)
Found via `typos --hidden --format brief`
2026-03-19 22:29:53 +00:00
extrawurst
5d6731326e
Rust msrv bump to 1.88 (#2879) 2026-03-19 18:33:52 +00:00
extrawurst
268d8ab175 fix time CVE 2026-03-19 15:34:03 +00:00
extrawurst
28cd5e7bb2 clippy fixes 2026-03-19 14:13:55 +00:00
dependabot[bot]
e29b9010e5
Bump clap from 4.5.56 to 4.5.57 (#2857)
Bumps [clap](https://github.com/clap-rs/clap) from 4.5.56 to 4.5.57.
- [Release notes](https://github.com/clap-rs/clap/releases)
- [Changelog](https://github.com/clap-rs/clap/blob/master/CHANGELOG.md)
- [Commits](https://github.com/clap-rs/clap/compare/clap_complete-v4.5.56...clap_complete-v4.5.57)

---
updated-dependencies:
- dependency-name: clap
  dependency-version: 4.5.57
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-02-04 07:06:47 -05:00
dependabot[bot]
485708539e
Bump git2 from 0.20.3 to 0.20.4 (#2856)
Bumps [git2](https://github.com/rust-lang/git2-rs) from 0.20.3 to 0.20.4.
- [Changelog](https://github.com/rust-lang/git2-rs/blob/git2-0.20.4/CHANGELOG.md)
- [Commits](https://github.com/rust-lang/git2-rs/compare/git2-0.20.3...git2-0.20.4)

---
updated-dependencies:
- dependency-name: git2
  dependency-version: 0.20.4
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-02-03 08:04:25 -05:00
dependabot[bot]
84d39309db
Bump clap from 4.5.55 to 4.5.56 (#2855)
Bumps [clap](https://github.com/clap-rs/clap) from 4.5.55 to 4.5.56.
- [Release notes](https://github.com/clap-rs/clap/releases)
- [Changelog](https://github.com/clap-rs/clap/blob/master/CHANGELOG.md)
- [Commits](https://github.com/clap-rs/clap/compare/clap_complete-v4.5.55...clap_complete-v4.5.56)

---
updated-dependencies:
- dependency-name: clap
  dependency-version: 4.5.56
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-01-30 09:54:21 -05:00
dependabot[bot]
96cac13013
Bump clap from 4.5.53 to 4.5.55 (#2854)
Bumps [clap](https://github.com/clap-rs/clap) from 4.5.53 to 4.5.55.
- [Release notes](https://github.com/clap-rs/clap/releases)
- [Changelog](https://github.com/clap-rs/clap/blob/master/CHANGELOG.md)
- [Commits](https://github.com/clap-rs/clap/compare/clap_complete-v4.5.53...clap_complete-v4.5.55)

---
updated-dependencies:
- dependency-name: clap
  dependency-version: 4.5.55
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-01-28 13:29:20 -05:00
Christoph Rüßler
b65de976e6
Fix clippy issues on nightly (#2852)
This fixes issues found by `clippy 0.1.95 (f134bbc78d 2026-01-24)`.
2026-01-25 16:20:23 -05:00
dependabot[bot]
13d75df6cd
Bump gix from 0.77.0 to 0.78.0 (#2849) 2026-01-23 21:05:05 +01:00
dependabot[bot]
054c09a089
Bump thiserror from 2.0.17 to 2.0.18 (#2847)
Bumps [thiserror](https://github.com/dtolnay/thiserror) from 2.0.17 to 2.0.18.
- [Release notes](https://github.com/dtolnay/thiserror/releases)
- [Commits](https://github.com/dtolnay/thiserror/compare/2.0.17...2.0.18)

---
updated-dependencies:
- dependency-name: thiserror
  dependency-version: 2.0.18
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-01-19 14:22:12 -05:00
extrawurst
1c118d75f3
proper pre-push hook implementation (#2811) 2026-01-18 17:37:45 +01:00
dependabot[bot]
7747d829cc
Bump unicode-truncate from 2.0.0 to 2.0.1 (#2846)
Bumps [unicode-truncate](https://github.com/Aetf/unicode-truncate) from 2.0.0 to 2.0.1.
- [Release notes](https://github.com/Aetf/unicode-truncate/releases)
- [Changelog](https://github.com/Aetf/unicode-truncate/blob/master/CHANGELOG.md)
- [Commits](https://github.com/Aetf/unicode-truncate/compare/v2.0.0...v2.0.1)

---
updated-dependencies:
- dependency-name: unicode-truncate
  dependency-version: 2.0.1
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-01-16 07:57:22 -05:00
extrawurst
d3dda1ec92
use nextest (#2833) 2026-01-15 17:29:15 -05:00
dependabot[bot]
71208ed570
Bump the cargo-minor group across 1 directory with 2 updates (#2836)
Bumps the cargo-minor group with 2 updates in the / directory: [indexmap](https://github.com/indexmap-rs/indexmap) and [serial_test](https://github.com/palfrey/serial_test).


Updates `indexmap` from 2.12.1 to 2.13.0
- [Changelog](https://github.com/indexmap-rs/indexmap/blob/main/RELEASES.md)
- [Commits](https://github.com/indexmap-rs/indexmap/compare/2.12.1...2.13.0)

Updates `serial_test` from 3.2.0 to 3.3.1
- [Release notes](https://github.com/palfrey/serial_test/releases)
- [Commits](https://github.com/palfrey/serial_test/compare/v3.2.0...v3.3.1)

---
updated-dependencies:
- dependency-name: indexmap
  dependency-version: 2.13.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: cargo-minor
- dependency-name: serial_test
  dependency-version: 3.3.1
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: cargo-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-01-13 11:09:41 -05:00
extrawurst
450caed840 add ignore for another CVE 2026-01-10 12:28:42 -05:00
dependabot[bot]
68ca5701f1
Bump gix from 0.75.0 to 0.77.0 (#2826)
* Bump gix from 0.75.0 to 0.77.0

Bumps [gix](https://github.com/GitoxideLabs/gitoxide) from 0.75.0 to 0.77.0.
- [Release notes](https://github.com/GitoxideLabs/gitoxide/releases)
- [Changelog](https://github.com/GitoxideLabs/gitoxide/blob/main/CHANGELOG.md)
- [Commits](https://github.com/GitoxideLabs/gitoxide/compare/gix-v0.75.0...gix-v0.77.0)

---
updated-dependencies:
- dependency-name: gix
  dependency-version: 0.77.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

* Adapt to API changes

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Christoph Rüßler <christoph.ruessler@mailbox.org>
2026-01-10 12:07:36 -05:00
dependabot[bot]
d68f366b1b
Bump tempfile from 3.23.0 to 3.24.0 in the cargo-minor group (#2819)
Bumps the cargo-minor group with 1 update: [tempfile](https://github.com/Stebalien/tempfile).


Updates `tempfile` from 3.23.0 to 3.24.0
- [Changelog](https://github.com/Stebalien/tempfile/blob/master/CHANGELOG.md)
- [Commits](https://github.com/Stebalien/tempfile/compare/v3.23.0...v3.24.0)

---
updated-dependencies:
- dependency-name: tempfile
  dependency-version: 3.24.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: cargo-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-12-31 10:58:25 -05:00
Christoph Rüßler
7f9aa97ada
Don't stop status iter on error, log warning instead (#2821) 2025-12-31 10:55:31 -05:00
extrawurst
463c411db5 svg logo 2025-12-18 14:41:21 -05:00
extrawurst
18624da9ed
try fixing homebrew ci step (#2814) 2025-12-17 19:13:31 -05:00
extrawurst
050b6ed620 make lfs support 1.0 featre 2025-12-17 19:09:48 -05:00
extrawurst
1099f11ff8 update readme 2025-12-17 19:08:04 -05:00
extrawurst
5527160424 give job needed permissions 2025-12-14 15:43:03 -05:00
extrawurst
4dae9f01d0 try fixing cd 2025-12-14 15:25:08 -05:00
extrawurst
e21ebd0ac8 bump filetreelist 2025-12-14 15:04:00 -05:00
extrawurst
c06235a1d5 bump git2-hooks 2025-12-14 15:01:52 -05:00
extrawurst
dd0c050b63 missing lock file update 2025-12-14 14:58:37 -05:00
extrawurst
197bcb5fc9 version bumps for release 0.28 2025-12-14 14:57:20 -05:00
extrawurst
6d62241c8b Revert "Add snapshot test using insta (#2411)"
This reverts commit e53692e781.
2025-12-08 13:44:18 -05:00
dependabot[bot]
20fec1364f
Bump indexmap from 2.12.0 to 2.12.1 (#2799)
Bumps [indexmap](https://github.com/indexmap-rs/indexmap) from 2.12.0 to 2.12.1.
- [Changelog](https://github.com/indexmap-rs/indexmap/blob/main/RELEASES.md)
- [Commits](https://github.com/indexmap-rs/indexmap/compare/2.12.0...2.12.1)

---
updated-dependencies:
- dependency-name: indexmap
  dependency-version: 2.12.1
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-12-06 19:27:09 -03:00
extrawurst
feed9ff133 git2 upgrade 2025-12-06 17:59:46 -03:00
dependabot[bot]
b32db3c258
Bump clap from 4.5.51 to 4.5.53 (#2796)
Bumps [clap](https://github.com/clap-rs/clap) from 4.5.51 to 4.5.53.
- [Release notes](https://github.com/clap-rs/clap/releases)
- [Changelog](https://github.com/clap-rs/clap/blob/master/CHANGELOG.md)
- [Commits](https://github.com/clap-rs/clap/compare/clap_complete-v4.5.51...clap_complete-v4.5.53)

---
updated-dependencies:
- dependency-name: clap
  dependency-version: 4.5.53
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-12-06 17:57:45 -03:00
Christoph Rüßler
e53692e781
Add snapshot test using insta (#2411) 2025-12-06 17:52:44 -03:00
dependabot[bot]
b6ce67dad3
Bump easy-cast from 0.5.3 to 0.5.4 (#2798)
Bumps [easy-cast](https://github.com/kas-gui/easy-cast) from 0.5.3 to 0.5.4.
- [Release notes](https://github.com/kas-gui/easy-cast/releases)
- [Changelog](https://github.com/kas-gui/easy-cast/blob/master/CHANGELOG.md)
- [Commits](https://github.com/kas-gui/easy-cast/compare/0.5.3...0.5.4)

---
updated-dependencies:
- dependency-name: easy-cast
  dependency-version: 0.5.4
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-12-06 09:59:48 -03:00
dependabot[bot]
9460a070a6
Bump log from 0.4.28 to 0.4.29 (#2802)
Bumps [log](https://github.com/rust-lang/log) from 0.4.28 to 0.4.29.
- [Release notes](https://github.com/rust-lang/log/releases)
- [Changelog](https://github.com/rust-lang/log/blob/master/CHANGELOG.md)
- [Commits](https://github.com/rust-lang/log/compare/0.4.28...0.4.29)

---
updated-dependencies:
- dependency-name: log
  dependency-version: 0.4.29
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-12-05 12:22:43 -03:00
0x61nas
e1029b00d1
feat(cli): add the ability to specify a custom keybinding/symbols file via the cli (#2731) 2025-12-01 13:19:33 -03:00
dependabot[bot]
92e3b739d7
Bump ron from 0.11.0 to 0.12.0 (#2797)
Bumps [ron](https://github.com/ron-rs/ron) from 0.11.0 to 0.12.0.
- [Release notes](https://github.com/ron-rs/ron/releases)
- [Changelog](https://github.com/ron-rs/ron/blob/master/CHANGELOG.md)
- [Commits](https://github.com/ron-rs/ron/compare/v0.11.0...v0.12.0)

---
updated-dependencies:
- dependency-name: ron
  dependency-version: 0.12.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-12-01 12:42:32 -03:00
dependabot[bot]
579a8278bb
Bump bytesize from 2.2.0 to 2.3.1 in the cargo-minor group (#2795)
Bumps the cargo-minor group with 1 update: [bytesize](https://github.com/bytesize-rs/bytesize).


Updates `bytesize` from 2.2.0 to 2.3.1
- [Release notes](https://github.com/bytesize-rs/bytesize/releases)
- [Changelog](https://github.com/bytesize-rs/bytesize/blob/master/CHANGELOG.md)
- [Commits](https://github.com/bytesize-rs/bytesize/compare/bytesize-v2.2.0...bytesize-v2.3.1)

---
updated-dependencies:
- dependency-name: bytesize
  dependency-version: 2.3.1
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: cargo-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-12-01 09:53:38 -03:00
extrawurst
1e80480122 missing changelog 2025-11-29 09:39:10 -03:00
extrawurst
16fdfc27ea cleanup changelog 2025-11-29 09:34:31 -03:00
extrawurst
3dbf1202a0
cargo updates (#2794) 2025-11-28 19:50:29 -03:00
Fatpandac
3082396bf1
Support choosing checkout branch method when status is not empty (#2494) 2025-11-28 18:36:40 -03:00
dependabot[bot]
0cce6907a2
Bump bytesize from 2.1.0 to 2.2.0 in the cargo-minor group (#2779)
Bumps the cargo-minor group with 1 update: [bytesize](https://github.com/bytesize-rs/bytesize).


Updates `bytesize` from 2.1.0 to 2.2.0
- [Release notes](https://github.com/bytesize-rs/bytesize/releases)
- [Changelog](https://github.com/bytesize-rs/bytesize/blob/master/CHANGELOG.md)
- [Commits](https://github.com/bytesize-rs/bytesize/compare/bytesize-v2.1.0...bytesize-v2.2.0)

---
updated-dependencies:
- dependency-name: bytesize
  dependency-version: 2.2.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: cargo-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: extrawurst <776816+extrawurst@users.noreply.github.com>
2025-11-28 18:17:17 -03:00
Christian Zangl
ffa6d9e57b
add cli flag to open files tab with selected file (#2746) 2025-11-28 17:53:07 -03:00
Christoph Rüßler
a6d6f31885
Overwrite committer on amend when gpgsign = false (#2792) 2025-11-28 17:11:22 -03:00
Christoph Rüßler
3de0b23ef2
Take workdir into account in gix_repo (#2789)
* Use gix_repo in get_tags

* Take workdir into account in gix_repo
2025-11-27 12:12:08 -03:00
Christoph Rüßler
8a57155c4d
Add test for gix_repo respecting workdir (#2790) 2025-11-27 07:43:34 -03:00
dependabot[bot]
37d3dd291a
Bump openssl-sys from 0.9.110 to 0.9.111 (#2776)
Bumps [openssl-sys](https://github.com/rust-openssl/rust-openssl) from 0.9.110 to 0.9.111.
- [Release notes](https://github.com/rust-openssl/rust-openssl/releases)
- [Commits](https://github.com/rust-openssl/rust-openssl/compare/openssl-sys-v0.9.110...openssl-sys-v0.9.111)

---
updated-dependencies:
- dependency-name: openssl-sys
  dependency-version: 0.9.111
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-11-12 12:11:15 -03:00
andrea-berling
2ab4143d6b
Add Go to line feature for the blame view (#2262) 2025-11-12 11:57:06 -03:00
xlai89
cb17cfe105
feat: support pre-push hooks (#2737)
Co-authored-by: extrawurst <776816+extrawurst@users.noreply.github.com>
2025-10-31 08:57:27 +01:00
extrawurst
2374e00302
validate path on startup for gix aswell (#2768)
so far we only try to open using the legacy libgit2 based one
2025-10-29 22:07:46 +01:00
dependabot[bot]
844a208506
Bump two-face from 0.4.3 to 0.4.4 (#2764)
Bumps [two-face](https://github.com/CosmicHorrorDev/two-face) from 0.4.3 to 0.4.4.
- [Release notes](https://github.com/CosmicHorrorDev/two-face/releases)
- [Changelog](https://github.com/CosmicHorrorDev/two-face/blob/v0.4.4/CHANGELOG.md)
- [Commits](https://github.com/CosmicHorrorDev/two-face/compare/v0.4.3...v0.4.4)

---
updated-dependencies:
- dependency-name: two-face
  dependency-version: 0.4.4
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-10-29 20:03:16 +01:00
dependabot[bot]
b997820331
Bump backtrace from 0.3.74 to 0.3.76 (#2762)
Bumps [backtrace](https://github.com/rust-lang/backtrace-rs) from 0.3.74 to 0.3.76.
- [Release notes](https://github.com/rust-lang/backtrace-rs/releases)
- [Changelog](https://github.com/rust-lang/backtrace-rs/blob/master/CHANGELOG.md)
- [Commits](https://github.com/rust-lang/backtrace-rs/compare/0.3.74...backtrace-v0.3.76)

---
updated-dependencies:
- dependency-name: backtrace
  dependency-version: 0.3.76
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-10-29 18:14:57 +01:00
dependabot[bot]
7265477da1
Bump openssl-sys from 0.9.108 to 0.9.110 (#2760)
Bumps [openssl-sys](https://github.com/rust-openssl/rust-openssl) from 0.9.108 to 0.9.110.
- [Release notes](https://github.com/rust-openssl/rust-openssl/releases)
- [Commits](https://github.com/rust-openssl/rust-openssl/compare/openssl-sys-v0.9.108...openssl-sys-v0.9.110)

---
updated-dependencies:
- dependency-name: openssl-sys
  dependency-version: 0.9.110
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-10-29 18:14:42 +01:00
dependabot[bot]
a3e6dade73
Bump anyhow from 1.0.98 to 1.0.100 (#2758)
Bumps [anyhow](https://github.com/dtolnay/anyhow) from 1.0.98 to 1.0.100.
- [Release notes](https://github.com/dtolnay/anyhow/releases)
- [Commits](https://github.com/dtolnay/anyhow/compare/1.0.98...1.0.100)

---
updated-dependencies:
- dependency-name: anyhow
  dependency-version: 1.0.100
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-10-29 16:17:59 +01:00
dependabot[bot]
6320765f9a
Bump clap from 4.5.38 to 4.5.50 (#2761)
Bumps [clap](https://github.com/clap-rs/clap) from 4.5.38 to 4.5.50.
- [Release notes](https://github.com/clap-rs/clap/releases)
- [Changelog](https://github.com/clap-rs/clap/blob/master/CHANGELOG.md)
- [Commits](https://github.com/clap-rs/clap/compare/clap_complete-v4.5.38...clap_complete-v4.5.50)

---
updated-dependencies:
- dependency-name: clap
  dependency-version: 4.5.50
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-10-29 16:17:46 +01:00
extrawurst
58490961c5
update log (#2766) 2025-10-29 15:27:28 +01:00
extrawurst
1032cccd55
cleanup format strings (#2765) 2025-10-29 13:58:27 +01:00
dependabot[bot]
35fa0ce8d2
Bump struct-patch from 0.9.4 to 0.10.4 (#2759)
Bumps [struct-patch](https://github.com/yanganto/struct-patch) from 0.9.4 to 0.10.4.
- [Commits](https://github.com/yanganto/struct-patch/compare/v0.9.4...v0.10.4)

---
updated-dependencies:
- dependency-name: struct-patch
  dependency-version: 0.10.4
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-10-29 13:32:15 +01:00
dependabot[bot]
598a1b5880
Bump chrono from 0.4.41 to 0.4.42 (#2757)
Bumps [chrono](https://github.com/chronotope/chrono) from 0.4.41 to 0.4.42.
- [Release notes](https://github.com/chronotope/chrono/releases)
- [Changelog](https://github.com/chronotope/chrono/blob/main/CHANGELOG.md)
- [Commits](https://github.com/chronotope/chrono/compare/v0.4.41...v0.4.42)

---
updated-dependencies:
- dependency-name: chrono
  dependency-version: 0.4.42
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-10-29 13:15:25 +01:00
dependabot[bot]
a1db2428d5
Bump notify-debouncer-mini from 0.6.0 to 0.7.0 (#2756)
Bumps [notify-debouncer-mini](https://github.com/notify-rs/notify) from 0.6.0 to 0.7.0.
- [Release notes](https://github.com/notify-rs/notify/releases)
- [Changelog](https://github.com/notify-rs/notify/blob/main/CHANGELOG.md)
- [Commits](https://github.com/notify-rs/notify/compare/debouncer-full-0.6.0...debouncer-mini-0.7.0)

---
updated-dependencies:
- dependency-name: notify-debouncer-mini
  dependency-version: 0.7.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-10-29 12:02:46 +01:00
dependabot[bot]
1a197a7d13
Bump ron from 0.10.1 to 0.11.0 (#2755)
Bumps [ron](https://github.com/ron-rs/ron) from 0.10.1 to 0.11.0.
- [Release notes](https://github.com/ron-rs/ron/releases)
- [Changelog](https://github.com/ron-rs/ron/blob/master/CHANGELOG.md)
- [Commits](https://github.com/ron-rs/ron/compare/v0.10.1...v0.11.0)

---
updated-dependencies:
- dependency-name: ron
  dependency-version: 0.11.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-10-29 10:27:18 +01:00
Kristoffer Plagborg Bak Sørensen
2ced3f9acc
fix: disable blame and history popup for untracked files (#2489)
* fix: disable blame and history popup for untracked files

An untracked file does not have any history data. Right now when
you press `B` for the blame popup or the `H` for the history popup
you get an empty popup where the title spins endlessly trying to find
the file in the commit history, and show relevant information.
This commit disables the two actions in the `StatusTreeComponent`, when the
selected item is a file which is not tracked by git.

---------

Co-authored-by: extrawurst <mail@rusticorn.com>
2025-10-28 22:41:36 +01:00
extrawurst
7c7698d5a2
cleanup some expects (#2754) 2025-10-28 22:40:44 +01:00
xlai89
eb48b3788f
feat: message tab supports pageup and pagedown (gitui-org#2623) (#2730)
Co-authored-by: extrawurst <776816+extrawurst@users.noreply.github.com>
2025-10-28 19:01:33 +01:00
wugeer
7674dae0cc
fix: When the terminal is insufficient to display all the commands, the cmdbar_bg configuration color does not fully take effect (#2348)
Co-authored-by: extrawurst <776816+extrawurst@users.noreply.github.com>
2025-10-28 18:42:54 +01:00
dependabot[bot]
3e98a40206
Bump which from 7.0.2 to 8.0.0 (#2723)
Bumps [which](https://github.com/harryfei/which-rs) from 7.0.2 to 8.0.0.
- [Release notes](https://github.com/harryfei/which-rs/releases)
- [Changelog](https://github.com/harryfei/which-rs/blob/master/CHANGELOG.md)
- [Commits](https://github.com/harryfei/which-rs/compare/7.0.2...8.0.0)

---
updated-dependencies:
- dependency-name: which
  dependency-version: 8.0.0
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-10-28 18:36:18 +01:00
0x61nas
ec65b372e2
refactor(args.rs): make the flags id slices and the default values as a const (#2733)
Co-authored-by: extrawurst <776816+extrawurst@users.noreply.github.com>
2025-10-28 18:03:48 +01:00
extrawurst
6bb216c0d4
fix warnings and deny deprecated (#2753)
* fix gitoxide warning

* do not allow deprecated fn
2025-10-28 18:03:19 +01:00
linkmauve
88ace76db5
Fix typos (#2740)
Thanks to typos for finding those!

Co-authored-by: extrawurst <776816+extrawurst@users.noreply.github.com>
2025-10-28 16:35:05 +01:00
linkmauve
26a38183cf
Fix all mismatched_lifetime_syntaxes warnings (#2727) 2025-10-28 16:34:42 +01:00
dependabot[bot]
5f23781c10
Bump the cargo-minor group across 1 directory with 8 updates (#2750)
Bumps the cargo-minor group with 7 updates in the / directory:

| Package | From | To |
| --- | --- | --- |
| [bitflags](https://github.com/bitflags/bitflags) | `2.9.1` | `2.10.0` |
| [bytesize](https://github.com/bytesize-rs/bytesize) | `2.0.1` | `2.1.0` |
| [indexmap](https://github.com/indexmap-rs/indexmap) | `2.9.0` | `2.12.0` |
| [notify](https://github.com/notify-rs/notify) | `8.0.0` | `8.2.0` |
| [rayon-core](https://github.com/rayon-rs/rayon) | `1.12.1` | `1.13.0` |
| [syntect](https://github.com/trishume/syntect) | `5.2.0` | `5.3.0` |
| [rayon](https://github.com/rayon-rs/rayon) | `1.10.0` | `1.11.0` |



Updates `bitflags` from 2.9.1 to 2.10.0
- [Release notes](https://github.com/bitflags/bitflags/releases)
- [Changelog](https://github.com/bitflags/bitflags/blob/main/CHANGELOG.md)
- [Commits](https://github.com/bitflags/bitflags/compare/2.9.1...2.10.0)

Updates `bytesize` from 2.0.1 to 2.1.0
- [Release notes](https://github.com/bytesize-rs/bytesize/releases)
- [Changelog](https://github.com/bytesize-rs/bytesize/blob/master/CHANGELOG.md)
- [Commits](https://github.com/bytesize-rs/bytesize/compare/bytesize-v2.0.1...bytesize-v2.1.0)

Updates `indexmap` from 2.9.0 to 2.12.0
- [Changelog](https://github.com/indexmap-rs/indexmap/blob/main/RELEASES.md)
- [Commits](https://github.com/indexmap-rs/indexmap/compare/2.9.0...2.12.0)

Updates `notify` from 8.0.0 to 8.2.0
- [Release notes](https://github.com/notify-rs/notify/releases)
- [Changelog](https://github.com/notify-rs/notify/blob/main/CHANGELOG.md)
- [Commits](https://github.com/notify-rs/notify/compare/notify-8.0.0...notify-8.2.0)

Updates `rayon-core` from 1.12.1 to 1.13.0
- [Changelog](https://github.com/rayon-rs/rayon/blob/main/RELEASES.md)
- [Commits](https://github.com/rayon-rs/rayon/compare/rayon-core-v1.12.1...rayon-core-v1.13.0)

Updates `serde` from 1.0.219 to 1.0.228
- [Release notes](https://github.com/serde-rs/serde/releases)
- [Commits](https://github.com/serde-rs/serde/compare/v1.0.219...v1.0.228)

Updates `syntect` from 5.2.0 to 5.3.0
- [Release notes](https://github.com/trishume/syntect/releases)
- [Changelog](https://github.com/trishume/syntect/blob/master/CHANGELOG.md)
- [Commits](https://github.com/trishume/syntect/compare/v5.2.0...v5.3.0)

Updates `rayon` from 1.10.0 to 1.11.0
- [Changelog](https://github.com/rayon-rs/rayon/blob/main/RELEASES.md)
- [Commits](https://github.com/rayon-rs/rayon/compare/rayon-core-v1.10.0...rayon-core-v1.11.0)

---
updated-dependencies:
- dependency-name: bitflags
  dependency-version: 2.10.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: cargo-minor
- dependency-name: bytesize
  dependency-version: 2.1.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: cargo-minor
- dependency-name: indexmap
  dependency-version: 2.12.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: cargo-minor
- dependency-name: notify
  dependency-version: 8.2.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: cargo-minor
- dependency-name: rayon-core
  dependency-version: 1.13.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: cargo-minor
- dependency-name: serde
  dependency-version: 1.0.228
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: cargo-minor
- dependency-name: syntect
  dependency-version: 5.3.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: cargo-minor
- dependency-name: rayon
  dependency-version: 1.11.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: cargo-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: extrawurst <776816+extrawurst@users.noreply.github.com>
2025-10-28 16:34:17 +01:00
Christoph Rüßler
dd475801ae
Restore default for showUntrackedFiles (#2751)
This restores the behaviour of `gitui` <= 0.27.
2025-10-28 15:59:03 +01:00
Christoph Rüßler
db211e5ac7
Update gix from 0.71.0 to 0.74.1 (#2745)
* Update gix from 0.71.0 to 0.74.1

* Bump MSRC from 1.81 to 1.82

This is required by `gitoxide` 0.74.

* Address clippy issues

* Add getrandom and rustix to deny.toml

* Document reasons for ignoring duplicates
2025-10-27 18:37:30 +01:00
extrawurst
e5ebb2239f fix nightly error 2025-10-22 10:47:59 +02:00
linkmauve
180368621e
Print a nicer error when failing to create cache directory (#2728)
Same as in 1d2248571d for the config
directory, when the cache directory fails to get created for whichever
reason, we currently exit gitui with a pretty undescriptive error.

Improves on #2684.
Fixes #2652.
2025-10-09 06:01:37 -03:00
Łukasz Sobczak
1d2248571d
Improve error message on config dir error (#2684) 2025-10-02 21:26:12 -03:00
Christoph Rüßler
f5893d991f
Split cargo group into cargo-minor and cargo-patch (#2719) 2025-09-17 09:50:38 -03:00
Christoph Rüßler
047f1402de
Address clippy issues (#2718) 2025-09-15 16:26:52 -03:00
Christoph Rüßler
60912c0b47
Derive Default per clippy recommendation (#2712) 2025-09-08 16:49:15 +02:00
Christoph Rüßler
8bff603a72
Address clippy issues on nightly (#2707) 2025-09-08 10:31:11 +02:00
Christoph Rüßler
fd46b9a0c1
Use gitoxide for get_tags (#2664) 2025-08-06 14:26:19 -07:00
Christoph Rüßler
fdd5a19d20
Extract GixError (#2687) 2025-08-05 12:55:59 -07:00
Christoph Rüßler
57d7d00701
Fix clippy errors on nightly (#2688) 2025-08-05 10:02:26 -07:00
Christoph Rüßler
6685f9adb3
Use gitoxide in get_status (#2673) 2025-07-28 19:28:02 +02:00
Peer Sommerlund
6d0a2ec115
Minimal docs at module level (#2639) 2025-06-24 09:23:02 +02:00
extrawurst
950e703cab
fix nightly ci (#2663) 2025-06-07 23:21:34 +02:00
Peer Sommerlund
dea3d25bcc
Avoid exposing internal tuple in CommitList.marked (#2638)
* Document tuple used in CommitList.marked

* Remove CommitList::marked() to avoid exposing internal structure
2025-05-29 14:12:12 +02:00
extrawurst
69fd7e664c bump git2-hooks version 2025-05-26 23:22:26 +02:00
dependabot[bot]
07cd313087
Bump chrono from 0.4.40 to 0.4.41 (#2629)
Bumps [chrono](https://github.com/chronotope/chrono) from 0.4.40 to 0.4.41.
- [Release notes](https://github.com/chronotope/chrono/releases)
- [Changelog](https://github.com/chronotope/chrono/blob/main/CHANGELOG.md)
- [Commits](https://github.com/chronotope/chrono/compare/v0.4.40...v0.4.41)

---
updated-dependencies:
- dependency-name: chrono
  dependency-version: 0.4.41
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: extrawurst <776816+extrawurst@users.noreply.github.com>
2025-05-26 23:08:24 +02:00
Christoph Rüßler
2d7d1730ba
Use gitoxide in get_commit_info (#2654)
* Implement From<gix::ObjectId> for CommitId

* Use gitoxide in get_commit_info
2025-05-26 23:06:36 +02:00
Christoph Rüßler
7625277953
Have cargo-sort 2.0.1 sort Cargo.toml's (#2653) 2025-05-24 22:14:26 +02:00
extrawurst
849824ab5a new cargo-sort 2025-05-23 20:08:12 +02:00
dependabot[bot]
fe5e780719
Bump struct-patch from 0.9.2 to 0.9.4 (#2632)
Bumps [struct-patch](https://github.com/yanganto/struct-patch) from 0.9.2 to 0.9.4.
- [Release notes](https://github.com/yanganto/struct-patch/releases)
- [Commits](https://github.com/yanganto/struct-patch/compare/v0.9.2...v0.9.4)

---
updated-dependencies:
- dependency-name: struct-patch
  dependency-version: 0.9.4
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: extrawurst <776816+extrawurst@users.noreply.github.com>
2025-05-20 11:18:08 +02:00
Christoph Rüßler
fa3d751621
Group Dependabot updates (#2647) 2025-05-20 11:16:17 +02:00
Christoph Rüßler
3b22a4f3b9
Simplify code for moving selection (#2645) 2025-05-20 11:15:51 +02:00
Christoph Rüßler
534da90b12
Update scc and sdd to non-yanked versions (#2646) 2025-05-19 10:34:19 +02:00
dependabot[bot]
9fa80bf370
Bump bitflags from 2.9.0 to 2.9.1 (#2642)
Bumps [bitflags](https://github.com/bitflags/bitflags) from 2.9.0 to 2.9.1.
- [Release notes](https://github.com/bitflags/bitflags/releases)
- [Changelog](https://github.com/bitflags/bitflags/blob/main/CHANGELOG.md)
- [Commits](https://github.com/bitflags/bitflags/compare/2.9.0...2.9.1)

---
updated-dependencies:
- dependency-name: bitflags
  dependency-version: 2.9.1
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-05-18 14:33:06 +02:00
extrawurst
a2e86c2ea3
fix cargo wix install (#2644) 2025-05-18 00:48:17 +02:00
dependabot[bot]
7065812c8d
Bump clap from 4.5.37 to 4.5.38 (#2636)
Bumps [clap](https://github.com/clap-rs/clap) from 4.5.37 to 4.5.38.
- [Release notes](https://github.com/clap-rs/clap/releases)
- [Changelog](https://github.com/clap-rs/clap/blob/master/CHANGELOG.md)
- [Commits](https://github.com/clap-rs/clap/compare/clap_complete-v4.5.37...clap_complete-v4.5.38)

---
updated-dependencies:
- dependency-name: clap
  dependency-version: 4.5.38
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-05-12 17:49:53 +02:00
dependabot[bot]
d9c199500f
Bump git2 from 0.20.1 to 0.20.2 (#2633)
Bumps [git2](https://github.com/rust-lang/git2-rs) from 0.20.1 to 0.20.2.
- [Changelog](https://github.com/rust-lang/git2-rs/blob/master/CHANGELOG.md)
- [Commits](https://github.com/rust-lang/git2-rs/compare/git2-0.20.1...git2-0.20.2)

---
updated-dependencies:
- dependency-name: git2
  dependency-version: 0.20.2
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-05-06 16:54:44 +02:00
dependabot[bot]
0a09c13a61
Bump openssl-sys from 0.9.107 to 0.9.108 (#2631)
Bumps [openssl-sys](https://github.com/sfackler/rust-openssl) from 0.9.107 to 0.9.108.
- [Release notes](https://github.com/sfackler/rust-openssl/releases)
- [Commits](https://github.com/sfackler/rust-openssl/compare/openssl-sys-v0.9.107...openssl-sys-v0.9.108)

---
updated-dependencies:
- dependency-name: openssl-sys
  dependency-version: 0.9.108
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-05-01 07:45:00 +02:00
extrawurst
3453e4624d fix nightly 2025-04-30 10:37:38 +02:00
Fatpandac
706cdf9243
feat: file and status tab support pageup and pagedown (#2496) 2025-04-21 20:23:05 +02:00
Lena
ee5c243cbf
Improve error messages (#2617) 2025-04-21 20:22:25 +02:00
dependabot[bot]
ae7b7b0c21
Bump clap from 4.5.36 to 4.5.37 (#2621)
Bumps [clap](https://github.com/clap-rs/clap) from 4.5.36 to 4.5.37.
- [Release notes](https://github.com/clap-rs/clap/releases)
- [Changelog](https://github.com/clap-rs/clap/blob/master/CHANGELOG.md)
- [Commits](https://github.com/clap-rs/clap/compare/clap_complete-v4.5.36...clap_complete-v4.5.37)

---
updated-dependencies:
- dependency-name: clap
  dependency-version: 4.5.37
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-04-21 20:20:40 +02:00
extrawurst
1205cd620b Revert "Show cursor on panic (#2620)"
This reverts commit 9056e5e75c.
2025-04-18 10:08:02 +02:00
extrawurst
9271b4116e remove duplicate panic handler 2025-04-18 10:05:23 +02:00
Johannes Agricola
9056e5e75c
Show cursor on panic (#2620)
ratatui::Terminal starts by hiding the cursor. If we panic and abort,
that Terminal instance is not dropped, which leaves restoring the cursor
state to us.

Co-authored-by: Naseschwarz <naseschwarz@0x53a.de>
2025-04-18 09:53:25 +02:00
dependabot[bot]
782ec070b4
Bump once_cell from 1.21.1 to 1.21.3 (#2588)
Bumps [once_cell](https://github.com/matklad/once_cell) from 1.21.1 to 1.21.3.
- [Changelog](https://github.com/matklad/once_cell/blob/master/CHANGELOG.md)
- [Commits](https://github.com/matklad/once_cell/compare/v1.21.1...v1.21.3)

---
updated-dependencies:
- dependency-name: once_cell
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: extrawurst <776816+extrawurst@users.noreply.github.com>
2025-04-18 09:32:27 +02:00
Johannes Agricola
baa1822180
Remove some #[allow]s II (#2616)
Co-authored-by: Naseschwarz <naseschwarz@0x53a.de>
2025-04-18 09:29:06 +02:00
Johannes Agricola
e08d954573
Clean up a few #[allow]s (#2614) 2025-04-16 10:45:08 +02:00
Joshix-1
0e3767102a
use gix_path::env::shell() to get the shell in git2-hooks (#2612)
Co-authored-by: Johannes Agricola <naseschwarz@users.noreply.github.com>
2025-04-16 10:41:14 +02:00
Johannes Agricola
7f88934d05
Bump msrv to 1.81 (#2613)
Co-authored-by: Naseschwarz <naseschwarz@0x53a.de>
2025-04-16 09:21:06 +02:00
dependabot[bot]
489918eb51
Bump env_logger from 0.11.6 to 0.11.8 (#2610)
Bumps [env_logger](https://github.com/rust-cli/env_logger) from 0.11.6 to 0.11.8.
- [Release notes](https://github.com/rust-cli/env_logger/releases)
- [Changelog](https://github.com/rust-cli/env_logger/blob/main/CHANGELOG.md)
- [Commits](https://github.com/rust-cli/env_logger/compare/v0.11.6...v0.11.8)

---
updated-dependencies:
- dependency-name: env_logger
  dependency-version: 0.11.8
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-04-16 09:18:43 +02:00
dependabot[bot]
29c8f48bc8
Bump clap from 4.5.35 to 4.5.36 (#2608)
Bumps [clap](https://github.com/clap-rs/clap) from 4.5.35 to 4.5.36.
- [Release notes](https://github.com/clap-rs/clap/releases)
- [Changelog](https://github.com/clap-rs/clap/blob/master/CHANGELOG.md)
- [Commits](https://github.com/clap-rs/clap/compare/clap_complete-v4.5.35...clap_complete-v4.5.36)

---
updated-dependencies:
- dependency-name: clap
  dependency-version: 4.5.36
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-04-16 09:18:32 +02:00
Joshix-1
711210b97b
Run git-hooks more correctly (#2483) 2025-04-15 15:01:39 +02:00
extrawurst
9781608584
upgrade dirs (#2569) 2025-04-15 09:22:50 +02:00
dependabot[bot]
7c41e99f98
Bump shellexpand from 3.1.0 to 3.1.1 (#2607)
Bumps [shellexpand](https://gitlab.com/ijackson/rust-shellexpand) from 3.1.0 to 3.1.1.
- [Commits](https://gitlab.com/ijackson/rust-shellexpand/compare/shellexpand-3.1.0...shellexpand-3.1.1)

---
updated-dependencies:
- dependency-name: shellexpand
  dependency-version: 3.1.1
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-04-15 08:54:08 +02:00
dependabot[bot]
44d4a8deb1
Bump clap from 4.5.34 to 4.5.35 (#2592)
Bumps [clap](https://github.com/clap-rs/clap) from 4.5.34 to 4.5.35.
- [Release notes](https://github.com/clap-rs/clap/releases)
- [Changelog](https://github.com/clap-rs/clap/blob/master/CHANGELOG.md)
- [Commits](https://github.com/clap-rs/clap/compare/clap_complete-v4.5.34...clap_complete-v4.5.35)

---
updated-dependencies:
- dependency-name: clap
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: extrawurst <776816+extrawurst@users.noreply.github.com>
2025-04-14 09:08:25 +02:00
dependabot[bot]
aa513b40e1
Bump ron from 0.9.0 to 0.10.1 (#2601)
Bumps [ron](https://github.com/ron-rs/ron) from 0.9.0 to 0.10.1.
- [Release notes](https://github.com/ron-rs/ron/releases)
- [Changelog](https://github.com/ron-rs/ron/blob/master/CHANGELOG.md)
- [Commits](https://github.com/ron-rs/ron/compare/v0.9.0...v0.10.1)

---
updated-dependencies:
- dependency-name: ron
  dependency-version: 0.10.1
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-04-14 09:08:12 +02:00
dependabot[bot]
f67eed9c9d
Bump indexmap from 2.8.0 to 2.9.0 (#2602)
Bumps [indexmap](https://github.com/indexmap-rs/indexmap) from 2.8.0 to 2.9.0.
- [Changelog](https://github.com/indexmap-rs/indexmap/blob/main/RELEASES.md)
- [Commits](https://github.com/indexmap-rs/indexmap/compare/2.8.0...2.9.0)

---
updated-dependencies:
- dependency-name: indexmap
  dependency-version: 2.9.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-04-14 09:08:00 +02:00
dependabot[bot]
670b22eacf
Bump struct-patch from 0.9.0 to 0.9.2 (#2604)
Bumps [struct-patch](https://github.com/yanganto/struct-patch) from 0.9.0 to 0.9.2.
- [Release notes](https://github.com/yanganto/struct-patch/releases)
- [Commits](https://github.com/yanganto/struct-patch/compare/v0.9.0...v0.9.2)

---
updated-dependencies:
- dependency-name: struct-patch
  dependency-version: 0.9.2
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-04-14 09:07:49 +02:00
dependabot[bot]
741de5fad5
Bump openssl-sys from 0.9.106 to 0.9.107 (#2605)
Bumps [openssl-sys](https://github.com/sfackler/rust-openssl) from 0.9.106 to 0.9.107.
- [Release notes](https://github.com/sfackler/rust-openssl/releases)
- [Commits](https://github.com/sfackler/rust-openssl/compare/openssl-sys-v0.9.106...openssl-sys-v0.9.107)

---
updated-dependencies:
- dependency-name: openssl-sys
  dependency-version: 0.9.107
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-04-14 09:07:37 +02:00
dependabot[bot]
7804ce7004
Bump anyhow from 1.0.97 to 1.0.98 (#2606)
Bumps [anyhow](https://github.com/dtolnay/anyhow) from 1.0.97 to 1.0.98.
- [Release notes](https://github.com/dtolnay/anyhow/releases)
- [Commits](https://github.com/dtolnay/anyhow/compare/1.0.97...1.0.98)

---
updated-dependencies:
- dependency-name: anyhow
  dependency-version: 1.0.98
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-04-14 09:04:55 +02:00
Peer Sommerlund
313220ae94
Fix #315 run brew test in CI (#2596)
Co-authored-by: extrawurst <776816+extrawurst@users.noreply.github.com>
2025-04-11 08:44:49 +03:00
extrawurst
e299017e79 crossbeam-channel upgrade 2025-04-11 06:08:26 +03:00
extrawurst
eeebb0a647
fix cargo deny advisory warning (#2598) 2025-04-07 18:02:34 +02:00
extrawurst
cc907996f4
fix latest clippy (#2597) 2025-04-07 16:06:00 +02:00
Johannes Agricola
7f75307f6e
Resolve core.hooksPath relative to GIT_WORK_TREE (#2571)
* Resolve core.hooksPath relative to GIT_WORK_TREE

git supports relative values in core.hooksPath.

`man git-config`:

> A relative path is taken as relative to the directory where the hooks are
> run (see the "DESCRIPTION" section of githooks[5]).

`man githooks`:

> Before Git invokes a hook, it changes its working directory to either
> $GIT_DIR in a bare repository or the root of the working tree in a >
> non-bare repository.

I.e. relative paths in core.hooksPath in non-bare repositories are always
relative to GIT_WORK_TREE.

There is a further exception; I believe this is not considered for path
resolution:

> An exception are hooks triggered during a push (pre-receive, update,
> post-receive, post-update, push-to-checkout) which are always executed
> in $GIT_DIR.

* Favor Repository::workdir() over path().parent()

This more clearly errors in case of bare repositories instead of using
the parent directory of the bare repository.

---------

Co-authored-by: Naseschwarz <naseschwarz@0x53a.de>
Co-authored-by: extrawurst <776816+extrawurst@users.noreply.github.com>
2025-04-04 15:28:44 +02:00
Johannes Agricola
89f73d2ec2
Disable dotted range commit yanking (#2577)
The algorithm for computing marked_consecutive assumes that commits are
consecutive in the commit graph if they are consecutive in the
linearized log used in` commitlist.rs`. That is not universally correct,
as siblings may also be displayed consecutively in this list.

For now, this just removes generating commit lists in dotted range
notation.

Co-authored-by: Naseschwarz <naseschwarz@0x53a.de>
2025-04-04 15:27:50 +02:00
dependabot[bot]
156381155e
Bump clap from 4.5.32 to 4.5.34 (#2585)
Bumps [clap](https://github.com/clap-rs/clap) from 4.5.32 to 4.5.34.
- [Release notes](https://github.com/clap-rs/clap/releases)
- [Changelog](https://github.com/clap-rs/clap/blob/master/CHANGELOG.md)
- [Commits](https://github.com/clap-rs/clap/compare/clap_complete-v4.5.32...clap_complete-v4.5.34)

---
updated-dependencies:
- dependency-name: clap
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-03-27 13:22:09 +08:00
dependabot[bot]
369ea6a140
Bump struct-patch from 0.8.7 to 0.9.0 (#2583)
Bumps [struct-patch](https://github.com/yanganto/struct-patch) from 0.8.7 to 0.9.0.
- [Release notes](https://github.com/yanganto/struct-patch/releases)
- [Commits](https://github.com/yanganto/struct-patch/compare/v0.8.7...v0.9.0)

---
updated-dependencies:
- dependency-name: struct-patch
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-03-26 07:23:23 +01:00
Johannes Agricola
6a884d1388
Consolidate recent theme change docs in THEMES.md (#2575) 2025-03-22 19:43:43 +01:00
Clément
65b57c4b60
feat(ui): Add use_selection_fg flag to control selection foreground color (#2515) 2025-03-22 14:11:54 +01:00
Johannes Agricola
dcd9a00ff3
Check cargo sort in make check (#2572)
This is run in CI, so running this offline avoids an unnecessary CI
failure.
2025-03-22 11:53:03 +01:00
Lena
ad32993721
Support loading custom syntax themes from a file (#2565) 2025-03-20 00:31:09 +01:00
Lena
92ef9f6fde
Respect .mailmap (#2485) 2025-03-19 14:44:02 +01:00
Lena
3ede6b56f1
Set the terminal title to gitui ({repo_path}) (#2484) 2025-03-18 19:23:16 +01:00
extrawurst
5755c096b4 update base64 and ron 2025-03-18 18:59:19 +01:00
vlad-anger
979fa68837
Push with refspec (#2542)
* push: respect `branch.*.merge` when push default is upstream
2025-03-18 18:35:39 +01:00
Johannes Agricola
a91132d187
Bump git2 from 0.20.0 to 0.20.1 (#2567)
* Bump git2 from 0.20.0 to 0.20.1

Bumps [git2](https://github.com/rust-lang/git2-rs) from 0.20.0 to 0.20.1.
- [Changelog](https://github.com/rust-lang/git2-rs/blob/master/CHANGELOG.md)
- [Commits](https://github.com/rust-lang/git2-rs/compare/git2-0.20.0...git2-0.20.1)

---
updated-dependencies:
- dependency-name: git2
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>

* Avoid passing reference into struct

git2-rs changed the lifetime of the result of Patch::hunk() to reference
the patch struct:
https://github.com/rust-lang/git2-rs/pull/1141

Thus, returning a patch struct and a reference into it is flagged by the
borrow checker upon move when returning.

This patch avoids returning both alltogether by separating retrieving
and retrieving the patch.

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Naseschwarz <naseschwarz@0x53a.de>
2025-03-18 09:09:11 +01:00
Vasileios Manolopoulos
597e944af9
Select out of the defaults syntect syntax themes in theme.ron (#2532) 2025-03-16 22:06:28 +01:00
Vasley
4031b0d1a7
Change links to point to gitui-org instead of extrawurst (#2538) 2025-03-16 15:33:28 +01:00
Wessam
4ad2c4b271
Dismiss commit msg key (#2563) 2025-03-16 15:32:43 +01:00
Lena
4ccdeed3a2
Add --logfile (#2539)
Co-authored-by: extrawurst <776816+extrawurst@users.noreply.github.com>
2025-03-16 15:30:02 +01:00
Fatpandac
1f3bd0ff70
add .editorconfig file (#2497)
* chore: add .editorconfig file

* feat: add root config to .editorconfig

Co-authored-by: Johannes Agricola <naseschwarz@users.noreply.github.com>

---------

Co-authored-by: Johannes Agricola <naseschwarz@users.noreply.github.com>
2025-03-16 15:27:31 +01:00
dependabot[bot]
35b2529c52
Bump bitflags from 2.8.0 to 2.9.0 (#2544)
Bumps [bitflags](https://github.com/bitflags/bitflags) from 2.8.0 to 2.9.0.
- [Release notes](https://github.com/bitflags/bitflags/releases)
- [Changelog](https://github.com/bitflags/bitflags/blob/main/CHANGELOG.md)
- [Commits](https://github.com/bitflags/bitflags/compare/2.8.0...2.9.0)

---
updated-dependencies:
- dependency-name: bitflags
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-03-15 10:07:15 +01:00
dependabot[bot]
9b3a4eb3d8
Bump chrono from 0.4.39 to 0.4.40 (#2540)
Bumps [chrono](https://github.com/chronotope/chrono) from 0.4.39 to 0.4.40.
- [Release notes](https://github.com/chronotope/chrono/releases)
- [Changelog](https://github.com/chronotope/chrono/blob/main/CHANGELOG.md)
- [Commits](https://github.com/chronotope/chrono/compare/v0.4.39...v0.4.40)

---
updated-dependencies:
- dependency-name: chrono
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-03-15 10:07:03 +01:00
dependabot[bot]
6eafd9ead4
Bump bytesize from 2.0.0 to 2.0.1 (#2545)
Bumps [bytesize](https://github.com/bytesize-rs/bytesize) from 2.0.0 to 2.0.1.
- [Release notes](https://github.com/bytesize-rs/bytesize/releases)
- [Changelog](https://github.com/bytesize-rs/bytesize/blob/master/CHANGELOG.md)
- [Commits](https://github.com/bytesize-rs/bytesize/commits/bytesize-v2.0.1)

---
updated-dependencies:
- dependency-name: bytesize
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-03-15 09:45:39 +01:00
Johannes Agricola
3c1e35eec7
Copy text using OSC52 (#2548)
* Copy text using OSC52 if X/Wayland methods fail
* Move Wayland/X string copying out of copy_string

Copying logic seems too nested to comprehend with the introcution of two
paths towards OSC52 otherwise.

---------

Co-authored-by: Naseschwarz <naseschwarz@0x53a.de>
Co-authored-by: extrawurst <776816+extrawurst@users.noreply.github.com>
2025-03-15 09:33:54 +01:00
Johannes Agricola
381ab45d3a
Ignore RUSTSEC-2024-0436 (#2562)
cargo deny reports:

```
ID: RUSTSEC-2024-0436
Advisory: https://rustsec.org/advisories/RUSTSEC-2024-0436
The creator of the crate `paste` has stated in the [`README.md`](https://github.com/dtolnay/paste/blob/master/README.md)
that this project is not longer maintained as well as archived the repository
Announcement: https://github.com/dtolnay/paste
Solution: No safe upgrade is available!
paste v1.0.15
└── ratatui v0.29.0
    ├── gitui v0.27.0
    └── tui-textarea v0.7.0
        └── gitui v0.27.0 (*)
```

In https://github.com/gitui-org/gitui/issues/2554 the decision was made
to ignore this advisory, as ratatui already has removed paste in
https://github.com/ratatui/ratatui/pull/1713 and we are just waiting for
an upstream release.

Co-authored-by: Naseschwarz <naseschwarz@0x53a.de>
2025-03-14 22:20:10 +01:00
dependabot[bot]
22aae374f1
Bump once_cell from 1.21.0 to 1.21.1 (#2560)
Bumps [once_cell](https://github.com/matklad/once_cell) from 1.21.0 to 1.21.1.
- [Changelog](https://github.com/matklad/once_cell/blob/master/CHANGELOG.md)
- [Commits](https://github.com/matklad/once_cell/compare/v1.21.0...v1.21.1)

---
updated-dependencies:
- dependency-name: once_cell
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-03-14 08:41:53 +01:00
dependabot[bot]
0b0d057af4
Bump once_cell from 1.20.3 to 1.21.0 (#2557)
Bumps [once_cell](https://github.com/matklad/once_cell) from 1.20.3 to 1.21.0.
- [Changelog](https://github.com/matklad/once_cell/blob/master/CHANGELOG.md)
- [Commits](https://github.com/matklad/once_cell/compare/v1.20.3...v1.21.0)

---
updated-dependencies:
- dependency-name: once_cell
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-03-11 09:19:19 +01:00
dependabot[bot]
5ad4cb0502
Bump clap from 4.5.31 to 4.5.32 (#2558)
Bumps [clap](https://github.com/clap-rs/clap) from 4.5.31 to 4.5.32.
- [Release notes](https://github.com/clap-rs/clap/releases)
- [Changelog](https://github.com/clap-rs/clap/blob/master/CHANGELOG.md)
- [Commits](https://github.com/clap-rs/clap/compare/v4.5.31...clap_complete-v4.5.32)

---
updated-dependencies:
- dependency-name: clap
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-03-11 09:18:57 +01:00
dependabot[bot]
e2558fd33b
Bump indexmap from 2.7.1 to 2.8.0 (#2559)
Bumps [indexmap](https://github.com/indexmap-rs/indexmap) from 2.7.1 to 2.8.0.
- [Changelog](https://github.com/indexmap-rs/indexmap/blob/main/RELEASES.md)
- [Commits](https://github.com/indexmap-rs/indexmap/compare/2.7.1...2.8.0)

---
updated-dependencies:
- dependency-name: indexmap
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-03-11 09:18:37 +01:00
dependabot[bot]
02d8c6bca4
Bump serde from 1.0.218 to 1.0.219 (#2555)
Bumps [serde](https://github.com/serde-rs/serde) from 1.0.218 to 1.0.219.
- [Release notes](https://github.com/serde-rs/serde/releases)
- [Commits](https://github.com/serde-rs/serde/compare/v1.0.218...v1.0.219)

---
updated-dependencies:
- dependency-name: serde
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-03-10 09:54:52 +01:00
extrawurst
7756f0369a fix link 2025-03-09 12:21:09 +01:00
extrawurst
c95cea96af more docs on keycodes 2025-03-09 12:17:58 +01:00
dependabot[bot]
d7e9ffd4f9
Bump thiserror from 2.0.11 to 2.0.12 (#2549)
Bumps [thiserror](https://github.com/dtolnay/thiserror) from 2.0.11 to 2.0.12.
- [Release notes](https://github.com/dtolnay/thiserror/releases)
- [Commits](https://github.com/dtolnay/thiserror/compare/2.0.11...2.0.12)

---
updated-dependencies:
- dependency-name: thiserror
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-03-06 08:57:47 +01:00
dependabot[bot]
7cf7a9e408
Bump anyhow from 1.0.96 to 1.0.97 (#2550)
Bumps [anyhow](https://github.com/dtolnay/anyhow) from 1.0.96 to 1.0.97.
- [Release notes](https://github.com/dtolnay/anyhow/releases)
- [Commits](https://github.com/dtolnay/anyhow/compare/1.0.96...1.0.97)

---
updated-dependencies:
- dependency-name: anyhow
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-03-06 08:53:00 +01:00
dependabot[bot]
687d429c6f
Bump clap from 4.5.30 to 4.5.31 (#2536)
Bumps [clap](https://github.com/clap-rs/clap) from 4.5.30 to 4.5.31.
- [Release notes](https://github.com/clap-rs/clap/releases)
- [Changelog](https://github.com/clap-rs/clap/blob/master/CHANGELOG.md)
- [Commits](https://github.com/clap-rs/clap/compare/clap_complete-v4.5.30...v4.5.31)

---
updated-dependencies:
- dependency-name: clap
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-02-26 10:45:15 +01:00
dependabot[bot]
4afcd66754
Bump bytesize from 1.3.2 to 2.0.0 (#2535)
Bumps [bytesize](https://github.com/bytesize-rs/bytesize) from 1.3.2 to 2.0.0.
- [Release notes](https://github.com/bytesize-rs/bytesize/releases)
- [Changelog](https://github.com/bytesize-rs/bytesize/blob/master/CHANGELOG.md)
- [Commits](https://github.com/bytesize-rs/bytesize/commits)

---
updated-dependencies:
- dependency-name: bytesize
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-02-26 10:44:55 +01:00
dependabot[bot]
9fd82f0d43
Bump clap from 4.5.29 to 4.5.30 (#2520)
Bumps [clap](https://github.com/clap-rs/clap) from 4.5.29 to 4.5.30.
- [Release notes](https://github.com/clap-rs/clap/releases)
- [Changelog](https://github.com/clap-rs/clap/blob/master/CHANGELOG.md)
- [Commits](https://github.com/clap-rs/clap/compare/clap_complete-v4.5.29...clap_complete-v4.5.30)

---
updated-dependencies:
- dependency-name: clap
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-02-24 11:39:19 +01:00
dependabot[bot]
7e46e353ae
Bump log from 0.4.25 to 0.4.26 (#2533)
Bumps [log](https://github.com/rust-lang/log) from 0.4.25 to 0.4.26.
- [Release notes](https://github.com/rust-lang/log/releases)
- [Changelog](https://github.com/rust-lang/log/blob/master/CHANGELOG.md)
- [Commits](https://github.com/rust-lang/log/compare/0.4.25...0.4.26)

---
updated-dependencies:
- dependency-name: log
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-02-24 11:36:19 +01:00
extrawurst
6372f815e5 update license 2025-02-24 11:20:42 +01:00
Lena
7f30be88e3
Remove redundant to_str() conversion (#2527) 2025-02-21 09:04:27 +01:00
dependabot[bot]
e611e79274
Bump anyhow from 1.0.95 to 1.0.96 (#2530)
Bumps [anyhow](https://github.com/dtolnay/anyhow) from 1.0.95 to 1.0.96.
- [Release notes](https://github.com/dtolnay/anyhow/releases)
- [Commits](https://github.com/dtolnay/anyhow/compare/1.0.95...1.0.96)

---
updated-dependencies:
- dependency-name: anyhow
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-02-21 09:02:54 +01:00
dependabot[bot]
cb6441220c
Bump serde from 1.0.217 to 1.0.218 (#2529)
Bumps [serde](https://github.com/serde-rs/serde) from 1.0.217 to 1.0.218.
- [Release notes](https://github.com/serde-rs/serde/releases)
- [Commits](https://github.com/serde-rs/serde/compare/v1.0.217...v1.0.218)

---
updated-dependencies:
- dependency-name: serde
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-02-21 09:02:44 +01:00
Lena
338dc2f4de
Improve syntax detection (#2524)
* Improve syntax detection

Currently, gitui prioritizes file extensions for syntax detection.
When a file lacks an extension (e.g. Makefile), or the extension
isn't tied to a specific format (e.g. .lock), it disables syntax
highlighting. Gitui will now try to detect the syntax from the entire
filename and the first line of the file using find_syntax_for_file().

---------

Co-authored-by: extrawurst <mail@rusticorn.com>
2025-02-19 14:13:47 +01:00
dependabot[bot]
cb4294a72b
Bump openssl-sys from 0.9.105 to 0.9.106 (#2519)
Bumps [openssl-sys](https://github.com/sfackler/rust-openssl) from 0.9.105 to 0.9.106.
- [Release notes](https://github.com/sfackler/rust-openssl/releases)
- [Commits](https://github.com/sfackler/rust-openssl/compare/openssl-sys-v0.9.105...openssl-sys-v0.9.106)

---
updated-dependencies:
- dependency-name: openssl-sys
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-02-17 12:16:18 +01:00
dependabot[bot]
498be5fd1c
Bump clap from 4.5.28 to 4.5.29 (#2516)
Bumps [clap](https://github.com/clap-rs/clap) from 4.5.28 to 4.5.29.
- [Release notes](https://github.com/clap-rs/clap/releases)
- [Changelog](https://github.com/clap-rs/clap/blob/master/CHANGELOG.md)
- [Commits](https://github.com/clap-rs/clap/compare/clap_complete-v4.5.28...clap_complete-v4.5.29)

---
updated-dependencies:
- dependency-name: clap
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-02-12 09:51:02 +01:00
dependabot[bot]
8ff4ee3ba9
Bump bytesize from 1.3.1 to 1.3.2 (#2517)
Bumps [bytesize](https://github.com/bytesize-rs/bytesize) from 1.3.1 to 1.3.2.
- [Release notes](https://github.com/bytesize-rs/bytesize/releases)
- [Changelog](https://github.com/bytesize-rs/bytesize/blob/master/CHANGELOG.md)
- [Commits](https://github.com/bytesize-rs/bytesize/compare/v1.3.1...v1.3.2)

---
updated-dependencies:
- dependency-name: bytesize
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-02-12 09:50:47 +01:00
dependabot[bot]
6ab6418974
Bump bytesize from 1.3.0 to 1.3.1 (#2513)
Bumps [bytesize](https://github.com/bytesize-rs/bytesize) from 1.3.0 to 1.3.1.
- [Release notes](https://github.com/bytesize-rs/bytesize/releases)
- [Changelog](https://github.com/bytesize-rs/bytesize/blob/master/CHANGELOG.md)
- [Commits](https://github.com/bytesize-rs/bytesize/compare/v1.3.0...v1.3.1)

---
updated-dependencies:
- dependency-name: bytesize
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-02-11 09:43:08 +01:00
Lena
491a95ba39
Update valid colors link in THEMES.md to ratatui (#2511) 2025-02-09 09:46:04 +01:00
dependabot[bot]
85d4399a17
Bump which from 7.0.1 to 7.0.2 (#2508)
Bumps [which](https://github.com/harryfei/which-rs) from 7.0.1 to 7.0.2.
- [Release notes](https://github.com/harryfei/which-rs/releases)
- [Changelog](https://github.com/harryfei/which-rs/blob/master/CHANGELOG.md)
- [Commits](https://github.com/harryfei/which-rs/compare/7.0.1...7.0.2)

---
updated-dependencies:
- dependency-name: which
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-02-07 13:43:13 +01:00
dependabot[bot]
a19c4c4cd2
Bump once_cell from 1.20.2 to 1.20.3 (#2509)
Bumps [once_cell](https://github.com/matklad/once_cell) from 1.20.2 to 1.20.3.
- [Changelog](https://github.com/matklad/once_cell/blob/master/CHANGELOG.md)
- [Commits](https://github.com/matklad/once_cell/compare/v1.20.2...v1.20.3)

---
updated-dependencies:
- dependency-name: once_cell
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-02-07 13:43:02 +01:00
dependabot[bot]
d3fd54c690
Bump two-face from 0.4.2 to 0.4.3 (#2507)
Bumps [two-face](https://github.com/CosmicHorrorDev/two-face) from 0.4.2 to 0.4.3.
- [Release notes](https://github.com/CosmicHorrorDev/two-face/releases)
- [Changelog](https://github.com/CosmicHorrorDev/two-face/blob/main/CHANGELOG.md)
- [Commits](https://github.com/CosmicHorrorDev/two-face/compare/v0.4.2...v0.4.3)

---
updated-dependencies:
- dependency-name: two-face
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-02-05 08:31:00 +01:00
dependabot[bot]
e3e0189520
Bump clap from 4.5.27 to 4.5.28 (#2506)
Bumps [clap](https://github.com/clap-rs/clap) from 4.5.27 to 4.5.28.
- [Release notes](https://github.com/clap-rs/clap/releases)
- [Changelog](https://github.com/clap-rs/clap/blob/master/CHANGELOG.md)
- [Commits](https://github.com/clap-rs/clap/compare/clap_complete-v4.5.27...clap_complete-v4.5.28)

---
updated-dependencies:
- dependency-name: clap
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-02-04 12:49:52 +01:00
dependabot[bot]
28c2677a75
Bump openssl-sys from 0.9.104 to 0.9.105 (#2504)
Bumps [openssl-sys](https://github.com/sfackler/rust-openssl) from 0.9.104 to 0.9.105.
- [Release notes](https://github.com/sfackler/rust-openssl/releases)
- [Commits](https://github.com/sfackler/rust-openssl/compare/openssl-sys-v0.9.104...openssl-sys-v0.9.105)

---
updated-dependencies:
- dependency-name: openssl-sys
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-02-03 08:46:12 +01:00
tommady
50c6562492
fix: File selector should jump back and highlight unstaged files (#2499)
Co-authored-by: extrawurst <mail@rusticorn.com>
2025-01-29 17:30:20 +01:00
dependabot[bot]
232ad89a4d
Bump two-face from 0.4.1 to 0.4.2 (#2500)
Bumps [two-face](https://github.com/CosmicHorrorDev/two-face) from 0.4.1 to 0.4.2.
- [Release notes](https://github.com/CosmicHorrorDev/two-face/releases)
- [Changelog](https://github.com/CosmicHorrorDev/two-face/blob/main/CHANGELOG.md)
- [Commits](https://github.com/CosmicHorrorDev/two-face/compare/v0.4.1...v0.4.2)

---
updated-dependencies:
- dependency-name: two-face
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-01-27 17:36:23 +01:00
dependabot[bot]
58b4e2c21e
Bump gix from 0.69.1 to 0.70.0 (#2492)
Bumps [gix](https://github.com/GitoxideLabs/gitoxide) from 0.69.1 to 0.70.0.
- [Release notes](https://github.com/GitoxideLabs/gitoxide/releases)
- [Changelog](https://github.com/GitoxideLabs/gitoxide/blob/main/CHANGELOG.md)
- [Commits](https://github.com/GitoxideLabs/gitoxide/compare/gix-v0.69.1...gix-v0.70.0)

---
updated-dependencies:
- dependency-name: gix
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-01-21 12:07:56 +01:00
dependabot[bot]
2701f48e8a
Bump clap from 4.5.26 to 4.5.27 (#2495)
Bumps [clap](https://github.com/clap-rs/clap) from 4.5.26 to 4.5.27.
- [Release notes](https://github.com/clap-rs/clap/releases)
- [Changelog](https://github.com/clap-rs/clap/blob/master/CHANGELOG.md)
- [Commits](https://github.com/clap-rs/clap/compare/clap_complete-v4.5.26...clap_complete-v4.5.27)

---
updated-dependencies:
- dependency-name: clap
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-01-21 11:58:32 +01:00
dependabot[bot]
4057e01df2
Bump two-face from 0.4.0 to 0.4.1 (#2491)
Bumps [two-face](https://github.com/CosmicHorrorDev/two-face) from 0.4.0 to 0.4.1.
- [Release notes](https://github.com/CosmicHorrorDev/two-face/releases)
- [Changelog](https://github.com/CosmicHorrorDev/two-face/blob/main/CHANGELOG.md)
- [Commits](https://github.com/CosmicHorrorDev/two-face/compare/v0.4.0...v0.4.1)

---
updated-dependencies:
- dependency-name: two-face
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-01-20 17:30:17 +01:00
dependabot[bot]
92cafb20ef
Bump indexmap from 2.7.0 to 2.7.1 (#2493)
Bumps [indexmap](https://github.com/indexmap-rs/indexmap) from 2.7.0 to 2.7.1.
- [Changelog](https://github.com/indexmap-rs/indexmap/blob/master/RELEASES.md)
- [Commits](https://github.com/indexmap-rs/indexmap/compare/2.7.0...2.7.1)

---
updated-dependencies:
- dependency-name: indexmap
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-01-20 17:30:03 +01:00
dependabot[bot]
8ea28a4e86
Bump bitflags from 2.7.0 to 2.8.0 (#2487)
Bumps [bitflags](https://github.com/bitflags/bitflags) from 2.7.0 to 2.8.0.
- [Release notes](https://github.com/bitflags/bitflags/releases)
- [Changelog](https://github.com/bitflags/bitflags/blob/main/CHANGELOG.md)
- [Commits](https://github.com/bitflags/bitflags/compare/2.7.0...2.8.0)

---
updated-dependencies:
- dependency-name: bitflags
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-01-16 09:30:39 +01:00
dependabot[bot]
9b3bc7b2b9
Bump log from 0.4.22 to 0.4.25 (#2481)
Bumps [log](https://github.com/rust-lang/log) from 0.4.22 to 0.4.25.
- [Release notes](https://github.com/rust-lang/log/releases)
- [Changelog](https://github.com/rust-lang/log/blob/master/CHANGELOG.md)
- [Commits](https://github.com/rust-lang/log/compare/0.4.22...0.4.25)

---
updated-dependencies:
- dependency-name: log
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-01-15 08:27:24 +01:00
extrawurst
99f6967144 prepare release 2025-01-14 21:03:20 +01:00
imgbot[bot]
36aed540e6
[ImgBot] Optimize images (#2480)
*Total -- 1,282.23kb -> 875.14kb (31.75%)

/assets/add-remote.png -- 129.57kb -> 67.05kb (48.26%)
/assets/gitui-signing.png -- 36.67kb -> 22.29kb (39.22%)
/assets/logo.png -- 55.26kb -> 35.84kb (35.14%)
/assets/syntax-highlighting-blame.png -- 1,060.72kb -> 749.97kb (29.3%)

Signed-off-by: ImgBotApp <ImgBotHelp@gmail.com>
Co-authored-by: ImgBotApp <ImgBotHelp@gmail.com>
2025-01-13 22:31:37 +01:00
extrawurst
9847fe6e26 polish changelog 2025-01-13 22:29:21 +01:00
extrawurst
7ec62279e8
prepare for rc (#2479) 2025-01-13 22:26:14 +01:00
dependabot[bot]
f505970778
Bump thiserror from 2.0.10 to 2.0.11 (#2478)
Bumps [thiserror](https://github.com/dtolnay/thiserror) from 2.0.10 to 2.0.11.
- [Release notes](https://github.com/dtolnay/thiserror/releases)
- [Commits](https://github.com/dtolnay/thiserror/compare/2.0.10...2.0.11)

---
updated-dependencies:
- dependency-name: thiserror
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-01-13 10:31:03 +01:00
dependabot[bot]
e6a272b499
Bump itertools from 0.13.0 to 0.14.0 (#2461)
Bumps [itertools](https://github.com/rust-itertools/itertools) from 0.13.0 to 0.14.0.
- [Changelog](https://github.com/rust-itertools/itertools/blob/master/CHANGELOG.md)
- [Commits](https://github.com/rust-itertools/itertools/compare/v0.13.0...v0.14.0)

---
updated-dependencies:
- dependency-name: itertools
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: extrawurst <776816+extrawurst@users.noreply.github.com>
2025-01-11 11:23:13 +01:00
extrawurst
778ac16cf7
upgrade notify an cleanup advisory (#2475) 2025-01-10 16:16:28 +01:00
extrawurst
01ad0615b2
git2 upgrade (#2473) 2025-01-10 15:28:52 +01:00
extrawurst
89db83839d
Gix upgrade (#2472) 2025-01-10 14:19:27 +01:00
extrawurst
40c79028ff
cargo updates (#2474) 2025-01-10 13:43:08 +01:00
extrawurst
d72617f5c2
some cargo upgrades (#2471) 2025-01-10 12:55:30 +01:00
extrawurst
69fdf90f39 upgrade after warning fix
see https://github.com/yanganto/struct-patch/issues/73
2025-01-10 11:59:53 +01:00
dependabot[bot]
149e8e4c62
Bump bitflags from 2.6.0 to 2.7.0 (#2469)
Bumps [bitflags](https://github.com/bitflags/bitflags) from 2.6.0 to 2.7.0.
- [Release notes](https://github.com/bitflags/bitflags/releases)
- [Changelog](https://github.com/bitflags/bitflags/blob/main/CHANGELOG.md)
- [Commits](https://github.com/bitflags/bitflags/compare/2.6.0...2.7.0)

---
updated-dependencies:
- dependency-name: bitflags
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-01-10 10:29:08 +01:00
Maksim Bondarenkov
fd0ccfb844
deps: Update cc (#2468)
Co-authored-by: extrawurst <776816+extrawurst@users.noreply.github.com>
2025-01-09 22:20:58 +01:00
dependabot[bot]
378f7fe446
Bump anyhow from 1.0.94 to 1.0.95 (#2455)
Bumps [anyhow](https://github.com/dtolnay/anyhow) from 1.0.94 to 1.0.95.
- [Release notes](https://github.com/dtolnay/anyhow/releases)
- [Commits](https://github.com/dtolnay/anyhow/compare/1.0.94...1.0.95)

---
updated-dependencies:
- dependency-name: anyhow
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: extrawurst <776816+extrawurst@users.noreply.github.com>
2025-01-09 21:42:57 +01:00
dependabot[bot]
d72de1d654
Bump env_logger from 0.11.5 to 0.11.6 (#2456)
Bumps [env_logger](https://github.com/rust-cli/env_logger) from 0.11.5 to 0.11.6.
- [Release notes](https://github.com/rust-cli/env_logger/releases)
- [Changelog](https://github.com/rust-cli/env_logger/blob/main/CHANGELOG.md)
- [Commits](https://github.com/rust-cli/env_logger/compare/v0.11.5...v0.11.6)

---
updated-dependencies:
- dependency-name: env_logger
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: extrawurst <776816+extrawurst@users.noreply.github.com>
2025-01-09 21:42:45 +01:00
dependabot[bot]
c2795a3819
Bump serde from 1.0.216 to 1.0.217 (#2459)
Bumps [serde](https://github.com/serde-rs/serde) from 1.0.216 to 1.0.217.
- [Release notes](https://github.com/serde-rs/serde/releases)
- [Commits](https://github.com/serde-rs/serde/compare/v1.0.216...v1.0.217)

---
updated-dependencies:
- dependency-name: serde
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-01-09 18:04:00 +01:00
dependabot[bot]
c3bdc3ba8e
Bump tempfile from 3.14.0 to 3.15.0 (#2463)
Bumps [tempfile](https://github.com/Stebalien/tempfile) from 3.14.0 to 3.15.0.
- [Changelog](https://github.com/Stebalien/tempfile/blob/master/CHANGELOG.md)
- [Commits](https://github.com/Stebalien/tempfile/compare/v3.14.0...v3.15.0)

---
updated-dependencies:
- dependency-name: tempfile
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-01-09 18:03:43 +01:00
extrawurst
66af52ae70 rust 1.84 update 2025-01-09 17:38:44 +01:00
dependabot[bot]
27e28d5f51
Bump which from 7.0.0 to 7.0.1 (#2454)
Bumps [which](https://github.com/harryfei/which-rs) from 7.0.0 to 7.0.1.
- [Release notes](https://github.com/harryfei/which-rs/releases)
- [Changelog](https://github.com/harryfei/which-rs/blob/master/CHANGELOG.md)
- [Commits](https://github.com/harryfei/which-rs/compare/7.0.0...7.0.1)

---
updated-dependencies:
- dependency-name: which
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-12-20 12:10:27 +01:00
dependabot[bot]
0ff55670b7
Bump crossbeam-channel from 0.5.13 to 0.5.14 (#2451)
Bumps [crossbeam-channel](https://github.com/crossbeam-rs/crossbeam) from 0.5.13 to 0.5.14.
- [Release notes](https://github.com/crossbeam-rs/crossbeam/releases)
- [Changelog](https://github.com/crossbeam-rs/crossbeam/blob/master/CHANGELOG.md)
- [Commits](https://github.com/crossbeam-rs/crossbeam/compare/crossbeam-channel-0.5.13...crossbeam-channel-0.5.14)

---
updated-dependencies:
- dependency-name: crossbeam-channel
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-12-16 13:21:09 +01:00
dependabot[bot]
11bae8978d
Bump easy-cast from 0.5.2 to 0.5.3 (#2449) 2024-12-13 09:02:29 +01:00
dependabot[bot]
b50531156c
Bump serde from 1.0.215 to 1.0.216 (#2448)
Bumps [serde](https://github.com/serde-rs/serde) from 1.0.215 to 1.0.216.
- [Release notes](https://github.com/serde-rs/serde/releases)
- [Commits](https://github.com/serde-rs/serde/compare/v1.0.215...v1.0.216)

---
updated-dependencies:
- dependency-name: serde
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-12-11 12:33:51 +01:00
dependabot[bot]
181ddc5f3f
Bump chrono from 0.4.38 to 0.4.39 (#2446)
Bumps [chrono](https://github.com/chronotope/chrono) from 0.4.38 to 0.4.39.
- [Release notes](https://github.com/chronotope/chrono/releases)
- [Changelog](https://github.com/chronotope/chrono/blob/main/CHANGELOG.md)
- [Commits](https://github.com/chronotope/chrono/compare/v0.4.38...v0.4.39)

---
updated-dependencies:
- dependency-name: chrono
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-12-10 22:56:47 +01:00
dependabot[bot]
22d3302b53
Bump anyhow from 1.0.93 to 1.0.94 (#2442)
Bumps [anyhow](https://github.com/dtolnay/anyhow) from 1.0.93 to 1.0.94.
- [Release notes](https://github.com/dtolnay/anyhow/releases)
- [Commits](https://github.com/dtolnay/anyhow/compare/1.0.93...1.0.94)

---
updated-dependencies:
- dependency-name: anyhow
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-12-10 22:56:10 +01:00
extrawurst
640bf333fc
Fix ci (#2447) 2024-12-10 09:44:01 +01:00
dependabot[bot]
809cb41668
Bump serial_test from 3.1.1 to 3.2.0 (#2428)
Bumps [serial_test](https://github.com/palfrey/serial_test) from 3.1.1 to 3.2.0.
- [Release notes](https://github.com/palfrey/serial_test/releases)
- [Commits](https://github.com/palfrey/serial_test/compare/v3.1.1...v3.2.0)

---
updated-dependencies:
- dependency-name: serial_test
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-11-15 10:40:20 +01:00
dependabot[bot]
ea2f5f5ecc
Bump serde from 1.0.214 to 1.0.215 (#2430)
Bumps [serde](https://github.com/serde-rs/serde) from 1.0.214 to 1.0.215.
- [Release notes](https://github.com/serde-rs/serde/releases)
- [Commits](https://github.com/serde-rs/serde/compare/v1.0.214...v1.0.215)

---
updated-dependencies:
- dependency-name: serde
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-11-15 10:40:02 +01:00
Christoph Rüßler
b49bacfac1
Bump gix from 0.66.0 to 0.67.0 and adapt to API changes (#2422)
* Bump gix from 0.66.0 to 0.67.0

Bumps [gix](https://github.com/GitoxideLabs/gitoxide) from 0.66.0 to 0.67.0.
- [Release notes](https://github.com/GitoxideLabs/gitoxide/releases)
- [Changelog](https://github.com/GitoxideLabs/gitoxide/blob/main/CHANGELOG.md)
- [Commits](https://github.com/GitoxideLabs/gitoxide/compare/gix-v0.66.0...gix-v0.67.0)

---
updated-dependencies:
- dependency-name: gix
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

* Adapt to API changes

* Update backtrace

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-11-08 09:53:43 +01:00
dependabot[bot]
42f476baaf
Bump anyhow from 1.0.92 to 1.0.93 (#2424)
Bumps [anyhow](https://github.com/dtolnay/anyhow) from 1.0.92 to 1.0.93.
- [Release notes](https://github.com/dtolnay/anyhow/releases)
- [Commits](https://github.com/dtolnay/anyhow/compare/1.0.92...1.0.93)

---
updated-dependencies:
- dependency-name: anyhow
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-11-08 09:48:58 +01:00
dependabot[bot]
7c93ac9545
Bump tempfile from 3.13.0 to 3.14.0 (#2425)
Bumps [tempfile](https://github.com/Stebalien/tempfile) from 3.13.0 to 3.14.0.
- [Changelog](https://github.com/Stebalien/tempfile/blob/master/CHANGELOG.md)
- [Commits](https://github.com/Stebalien/tempfile/compare/v3.13.0...v3.14.0)

---
updated-dependencies:
- dependency-name: tempfile
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-11-08 09:48:48 +01:00
dependabot[bot]
17118a9b9f
Bump anyhow from 1.0.91 to 1.0.92 (#2417)
Bumps [anyhow](https://github.com/dtolnay/anyhow) from 1.0.91 to 1.0.92.
- [Release notes](https://github.com/dtolnay/anyhow/releases)
- [Commits](https://github.com/dtolnay/anyhow/compare/1.0.91...1.0.92)

---
updated-dependencies:
- dependency-name: anyhow
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-11-04 10:16:34 +01:00
dependabot[bot]
6352cfe5bf
Bump thiserror from 1.0.66 to 1.0.67 (#2416)
Bumps [thiserror](https://github.com/dtolnay/thiserror) from 1.0.66 to 1.0.67.
- [Release notes](https://github.com/dtolnay/thiserror/releases)
- [Commits](https://github.com/dtolnay/thiserror/compare/1.0.66...1.0.67)

---
updated-dependencies:
- dependency-name: thiserror
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-11-04 10:16:22 +01:00
dependabot[bot]
4e40b56587
Bump which from 6.0.3 to 7.0.0 (#2418)
Bumps [which](https://github.com/harryfei/which-rs) from 6.0.3 to 7.0.0.
- [Release notes](https://github.com/harryfei/which-rs/releases)
- [Changelog](https://github.com/harryfei/which-rs/blob/master/CHANGELOG.md)
- [Commits](https://github.com/harryfei/which-rs/compare/6.0.3...7.0.0)

---
updated-dependencies:
- dependency-name: which
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-11-04 10:16:07 +01:00
Nicholas R. Smith
94924db632
Support "Copy Path" operation in WSL (#2413) 2024-11-02 18:54:33 +01:00
dependabot[bot]
8db448cab3
Bump thiserror from 1.0.65 to 1.0.66 (#2415)
Bumps [thiserror](https://github.com/dtolnay/thiserror) from 1.0.65 to 1.0.66.
- [Release notes](https://github.com/dtolnay/thiserror/releases)
- [Commits](https://github.com/dtolnay/thiserror/compare/1.0.65...1.0.66)

---
updated-dependencies:
- dependency-name: thiserror
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-11-01 10:46:20 +01:00
Christoph Rüßler
9c4bd29fc8
Improve test for env variables (#2409)
* Add test for AsyncLog respecting GIT_DIR

* Mark test as serial

* Mark additional test as serial

* Unwrap result to get more info in error case

---------

Co-authored-by: extrawurst <776816+extrawurst@users.noreply.github.com>
2024-10-29 09:42:25 +01:00
dependabot[bot]
1943988f7e
Bump serde from 1.0.213 to 1.0.214 (#2410)
Bumps [serde](https://github.com/serde-rs/serde) from 1.0.213 to 1.0.214.
- [Release notes](https://github.com/serde-rs/serde/releases)
- [Commits](https://github.com/serde-rs/serde/compare/v1.0.213...v1.0.214)

---
updated-dependencies:
- dependency-name: serde
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-10-29 09:41:51 +01:00
extrawurst
603116f491
ratatui update (#2403) 2024-10-23 09:34:24 +02:00
dependabot[bot]
a923896a62
Bump serde from 1.0.210 to 1.0.213 (#2399)
Bumps [serde](https://github.com/serde-rs/serde) from 1.0.210 to 1.0.213.
- [Release notes](https://github.com/serde-rs/serde/releases)
- [Commits](https://github.com/serde-rs/serde/compare/v1.0.210...v1.0.213)

---
updated-dependencies:
- dependency-name: serde
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-10-23 08:23:13 +02:00
dependabot[bot]
a226611eae
Bump anyhow from 1.0.90 to 1.0.91 (#2401)
Bumps [anyhow](https://github.com/dtolnay/anyhow) from 1.0.90 to 1.0.91.
- [Release notes](https://github.com/dtolnay/anyhow/releases)
- [Commits](https://github.com/dtolnay/anyhow/compare/1.0.90...1.0.91)

---
updated-dependencies:
- dependency-name: anyhow
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-10-23 08:22:36 +02:00
dependabot[bot]
d85c6a7eac
Bump thiserror from 1.0.64 to 1.0.65 (#2400)
Bumps [thiserror](https://github.com/dtolnay/thiserror) from 1.0.64 to 1.0.65.
- [Release notes](https://github.com/dtolnay/thiserror/releases)
- [Commits](https://github.com/dtolnay/thiserror/compare/1.0.64...1.0.65)

---
updated-dependencies:
- dependency-name: thiserror
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-10-23 08:22:24 +02:00
Antonio Yang
b4c0244a2a
Bump struct-patch from 0.4.1 to 0.8.6 (#2386) 2024-10-21 09:20:17 +02:00
dependabot[bot]
1346e5594c
Bump anyhow from 1.0.89 to 1.0.90 (#2394)
Bumps [anyhow](https://github.com/dtolnay/anyhow) from 1.0.89 to 1.0.90.
- [Release notes](https://github.com/dtolnay/anyhow/releases)
- [Commits](https://github.com/dtolnay/anyhow/compare/1.0.89...1.0.90)

---
updated-dependencies:
- dependency-name: anyhow
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-10-21 09:19:31 +02:00
extrawurst
1866bf5f89 spaces to tabs 2024-10-18 18:14:21 +02:00
extrawurst
e7be093c39 fix changelog 2024-10-18 18:08:36 +02:00
wugeer
fb1ba7c0b9
feat: help popup display viewing progress (#2388) 2024-10-18 18:07:35 +02:00
extrawurst
ba9adf40f4 fix yanked crate 2024-10-18 18:02:51 +02:00
extrawurst
d7765c4239 fix doc comments to please new rust version lints 2024-10-18 18:02:02 +02:00
extrawurst
c4f517acb5 Revert "Add test for AsyncLog respecting GIT_DIR (#2387)"
This reverts commit 9c433b4de3.
2024-10-17 11:28:40 +02:00
dependabot[bot]
3643e80d5c
Bump openssl-sys from 0.9.103 to 0.9.104 (#2391)
Bumps [openssl-sys](https://github.com/sfackler/rust-openssl) from 0.9.103 to 0.9.104.
- [Release notes](https://github.com/sfackler/rust-openssl/releases)
- [Commits](https://github.com/sfackler/rust-openssl/compare/openssl-sys-v0.9.103...openssl-sys-v0.9.104)

---
updated-dependencies:
- dependency-name: openssl-sys
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-10-16 11:20:13 +02:00
dependabot[bot]
15e444ee3a
Bump ssh-key from 0.6.6 to 0.6.7 (#2390)
Bumps [ssh-key](https://github.com/RustCrypto/SSH) from 0.6.6 to 0.6.7.
- [Commits](https://github.com/RustCrypto/SSH/compare/ssh-key/v0.6.6...ssh-key/v0.6.7)

---
updated-dependencies:
- dependency-name: ssh-key
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-10-16 11:19:36 +02:00
Christoph Rüßler
9c433b4de3
Add test for AsyncLog respecting GIT_DIR (#2387) 2024-10-15 20:20:03 +02:00
dependabot[bot]
90a226927b
Bump once_cell from 1.20.1 to 1.20.2 (#2383)
Bumps [once_cell](https://github.com/matklad/once_cell) from 1.20.1 to 1.20.2.
- [Changelog](https://github.com/matklad/once_cell/blob/master/CHANGELOG.md)
- [Commits](https://github.com/matklad/once_cell/compare/v1.20.1...v1.20.2)

---
updated-dependencies:
- dependency-name: once_cell
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-10-07 11:24:49 +02:00
extrawurst
f52586885e git2-hooks version bump 2024-09-30 11:24:06 +02:00
Isaac Corbrey
d98171b2aa
Set CREATE_NO_WINDOW flag when executing Git hooks (#2371) 2024-09-30 11:18:24 +02:00
dependabot[bot]
2a590fd8cc
Bump once_cell from 1.19.0 to 1.20.1 (#2375) 2024-09-30 09:47:30 +02:00
dependabot[bot]
569ed66fc2
Bump tempfile from 3.12.0 to 3.13.0 (#2373) 2024-09-30 09:47:14 +02:00
extrawurst
efa8450bcc bump filetreelist 2024-09-28 12:52:54 +03:00
Fabio Valentini
2c1fce4b48
filetreelist: exclude demo.gif from published crates (#2369)
This reduces download size for the crate from ~900 kB to ~10 kB.
2024-09-28 12:52:12 +03:00
dependabot[bot]
6b588b4d45
Bump thiserror from 1.0.63 to 1.0.64 (#2367) 2024-09-24 10:10:23 +03:00
extrawurst
11a0b9b0b7 fix create-remote 2024-09-18 23:47:12 +02:00
extrawurst
7a51dbec4d some comments 2024-09-18 16:52:25 +02:00
extrawurst
4bb9fc8790 cleanup changelog 2024-09-18 16:47:17 +02:00
Robin
d4f9400e04
Remotes popup (#2350)
Co-authored-by: extrawurst <mail@rusticorn.com>
Co-authored-by: extrawurst <776816+extrawurst@users.noreply.github.com>
2024-09-18 16:31:21 +02:00
extrawurst
2cbeeeda95 rename section for consistency 2024-09-18 16:01:31 +02:00
dependabot[bot]
dd1efee5cf Bump unicode-segmentation from 1.11.0 to 1.12.0
Bumps [unicode-segmentation](https://github.com/unicode-rs/unicode-segmentation) from 1.11.0 to 1.12.0.
- [Commits](https://github.com/unicode-rs/unicode-segmentation/compare/v1.11.0...v1.12.0)

---
updated-dependencies:
- dependency-name: unicode-segmentation
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-09-18 13:38:42 +02:00
dependabot[bot]
02fa24e2f8 Bump pretty_assertions from 1.4.0 to 1.4.1
Bumps [pretty_assertions](https://github.com/rust-pretty-assertions/rust-pretty-assertions) from 1.4.0 to 1.4.1.
- [Release notes](https://github.com/rust-pretty-assertions/rust-pretty-assertions/releases)
- [Changelog](https://github.com/rust-pretty-assertions/rust-pretty-assertions/blob/main/CHANGELOG.md)
- [Commits](https://github.com/rust-pretty-assertions/rust-pretty-assertions/compare/v1.4.0...v1.4.1)

---
updated-dependencies:
- dependency-name: pretty_assertions
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-09-18 13:38:30 +02:00
extrawurst
9dd3cf30e1 fix crashes for multiline textboxes
for zero width or height terminals
2024-09-17 20:48:04 +02:00
dependabot[bot]
ab75103d00 Bump serde from 1.0.204 to 1.0.210
Bumps [serde](https://github.com/serde-rs/serde) from 1.0.204 to 1.0.210.
- [Release notes](https://github.com/serde-rs/serde/releases)
- [Commits](https://github.com/serde-rs/serde/compare/v1.0.204...v1.0.210)

---
updated-dependencies:
- dependency-name: serde
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-09-17 13:14:04 +02:00
extrawurst
b5d527bd97 update crossterm and ratatui 2024-09-17 13:13:09 +02:00
extrawurst
3a86cb5ca7 fix warning of yanked release 2024-09-17 12:34:10 +02:00
dependabot[bot]
7a37730076 Bump indexmap from 2.2.6 to 2.5.0
Bumps [indexmap](https://github.com/indexmap-rs/indexmap) from 2.2.6 to 2.5.0.
- [Changelog](https://github.com/indexmap-rs/indexmap/blob/master/RELEASES.md)
- [Commits](https://github.com/indexmap-rs/indexmap/compare/2.2.6...2.5.0)

---
updated-dependencies:
- dependency-name: indexmap
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-09-17 12:24:35 +02:00
extrawurst
f27b58114a fix warning 2024-09-17 12:02:25 +02:00
dependabot[bot]
314b1bd867 Bump tui-textarea from 0.5.2 to 0.5.3
Bumps [tui-textarea](https://github.com/rhysd/tui-textarea) from 0.5.2 to 0.5.3.
- [Release notes](https://github.com/rhysd/tui-textarea/releases)
- [Changelog](https://github.com/rhysd/tui-textarea/blob/main/CHANGELOG.md)
- [Commits](https://github.com/rhysd/tui-textarea/compare/v0.5.2...v0.5.3)

---
updated-dependencies:
- dependency-name: tui-textarea
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-09-17 11:59:31 +02:00
dependabot[bot]
1580defa29 Bump which from 6.0.2 to 6.0.3
Bumps [which](https://github.com/harryfei/which-rs) from 6.0.2 to 6.0.3.
- [Release notes](https://github.com/harryfei/which-rs/releases)
- [Changelog](https://github.com/harryfei/which-rs/blob/master/CHANGELOG.md)
- [Commits](https://github.com/harryfei/which-rs/compare/6.0.2...6.0.3)

---
updated-dependencies:
- dependency-name: which
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-09-17 11:58:51 +02:00
dependabot[bot]
ea94d3a731 Bump once_cell from 1.19.0 to 1.20.0
Bumps [once_cell](https://github.com/matklad/once_cell) from 1.19.0 to 1.20.0.
- [Changelog](https://github.com/matklad/once_cell/blob/master/CHANGELOG.md)
- [Commits](https://github.com/matklad/once_cell/compare/v1.19.0...v1.20.0)

---
updated-dependencies:
- dependency-name: once_cell
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-09-17 11:55:11 +02:00
dependabot[bot]
b958eaeddd Bump anyhow from 1.0.86 to 1.0.89
Bumps [anyhow](https://github.com/dtolnay/anyhow) from 1.0.86 to 1.0.89.
- [Release notes](https://github.com/dtolnay/anyhow/releases)
- [Commits](https://github.com/dtolnay/anyhow/compare/1.0.86...1.0.89)

---
updated-dependencies:
- dependency-name: anyhow
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-09-17 11:55:00 +02:00
Christoph Rüßler
2b8ef40289
Fix log crashing in subdirectories (#2301)
* Fix log crashing in subdirectories
Replace `gix::open` by `gix::discover`. `gix::open` errors when the
passed directory is not a git repository.
* Add test for AsyncLog in sub-directory
* Respect env variables when discovering repo

---------

Co-authored-by: extrawurst <776816+extrawurst@users.noreply.github.com>
2024-09-17 11:37:52 +02:00
extrawurst
0f5cf893f7 fix fmt 2024-09-11 22:24:32 +02:00
extrawurst
134ef734a4 clippy fix 2024-09-11 21:30:34 +02:00
extrawurst
0a5e2ef1ed gix update 2024-09-11 21:27:14 +02:00
extrawurst
7ad8265c54 git2-hooks version bump 2024-08-24 12:20:17 +08:00
Yerke Tulibergenov
4cb9500cd9 Use default shell instead of bash on Unix-like OS 2024-08-23 18:57:46 +08:00
dependabot[bot]
63a91fd281 Bump tempfile from 3.10.1 to 3.12.0
Bumps [tempfile](https://github.com/Stebalien/tempfile) from 3.10.1 to 3.12.0.
- [Changelog](https://github.com/Stebalien/tempfile/blob/master/CHANGELOG.md)
- [Commits](https://github.com/Stebalien/tempfile/commits)

---
updated-dependencies:
- dependency-name: tempfile
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-08-08 01:12:38 +08:00
extrawurst
6c9bb9f616 update binary sizes (closing #2330) 2024-08-08 01:11:51 +08:00
dependabot[bot]
61e52d9bfa Bump which from 6.0.1 to 6.0.2
Bumps [which](https://github.com/harryfei/which-rs) from 6.0.1 to 6.0.2.
- [Release notes](https://github.com/harryfei/which-rs/releases)
- [Changelog](https://github.com/harryfei/which-rs/blob/master/CHANGELOG.md)
- [Commits](https://github.com/harryfei/which-rs/compare/6.0.1...6.0.2)

---
updated-dependencies:
- dependency-name: which
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-08-02 00:38:10 +08:00
dependabot[bot]
0289c82e4e Bump tui-textarea from 0.5.1 to 0.5.2
Bumps [tui-textarea](https://github.com/rhysd/tui-textarea) from 0.5.1 to 0.5.2.
- [Release notes](https://github.com/rhysd/tui-textarea/releases)
- [Changelog](https://github.com/rhysd/tui-textarea/blob/main/CHANGELOG.md)
- [Commits](https://github.com/rhysd/tui-textarea/compare/v0.5.1...v0.5.2)

---
updated-dependencies:
- dependency-name: tui-textarea
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-08-02 00:34:02 +08:00
dependabot[bot]
4ef633a75a Bump env_logger from 0.11.4 to 0.11.5
Bumps [env_logger](https://github.com/rust-cli/env_logger) from 0.11.4 to 0.11.5.
- [Release notes](https://github.com/rust-cli/env_logger/releases)
- [Changelog](https://github.com/rust-cli/env_logger/blob/main/CHANGELOG.md)
- [Commits](https://github.com/rust-cli/env_logger/compare/v0.11.4...v0.11.5)

---
updated-dependencies:
- dependency-name: env_logger
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-07-26 14:35:24 +07:00
dependabot[bot]
9ecc207270 Bump clap from 4.5.10 to 4.5.11
Bumps [clap](https://github.com/clap-rs/clap) from 4.5.10 to 4.5.11.
- [Release notes](https://github.com/clap-rs/clap/releases)
- [Changelog](https://github.com/clap-rs/clap/blob/master/CHANGELOG.md)
- [Commits](https://github.com/clap-rs/clap/compare/clap_complete-v4.5.10...clap_complete-v4.5.11)

---
updated-dependencies:
- dependency-name: clap
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-07-26 14:35:07 +07:00
extrawurst
b89672b134 rust 1.80 clippy fixes 2024-07-26 13:24:52 +07:00
extrawurst
ce923e6fe4
supply libgit2 with flag to respect env variables (#2299)
closes #2298
2024-07-25 11:16:01 +07:00
dependabot[bot]
7fec560942 Bump clap from 4.5.9 to 4.5.10
Bumps [clap](https://github.com/clap-rs/clap) from 4.5.9 to 4.5.10.
- [Release notes](https://github.com/clap-rs/clap/releases)
- [Changelog](https://github.com/clap-rs/clap/blob/master/CHANGELOG.md)
- [Commits](https://github.com/clap-rs/clap/compare/clap_complete-v4.5.9...v4.5.10)

---
updated-dependencies:
- dependency-name: clap
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-07-25 10:53:06 +07:00
dependabot[bot]
32fbea2870 Bump env_logger from 0.11.3 to 0.11.4
Bumps [env_logger](https://github.com/rust-cli/env_logger) from 0.11.3 to 0.11.4.
- [Release notes](https://github.com/rust-cli/env_logger/releases)
- [Changelog](https://github.com/rust-cli/env_logger/blob/main/CHANGELOG.md)
- [Commits](https://github.com/rust-cli/env_logger/compare/v0.11.3...v0.11.4)

---
updated-dependencies:
- dependency-name: env_logger
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-07-25 10:52:53 +07:00
dependabot[bot]
8e00264943 Bump gix from 0.63.0 to 0.64.0
Bumps [gix](https://github.com/Byron/gitoxide) from 0.63.0 to 0.64.0.
- [Release notes](https://github.com/Byron/gitoxide/releases)
- [Changelog](https://github.com/Byron/gitoxide/blob/main/CHANGELOG.md)
- [Commits](https://github.com/Byron/gitoxide/compare/gix-v0.63.0...gix-v0.64.0)

---
updated-dependencies:
- dependency-name: gix
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-07-25 10:52:29 +07:00
dependabot[bot]
0cb5b781a2 Bump openssl-sys from 0.9.102 to 0.9.103
Bumps [openssl-sys](https://github.com/sfackler/rust-openssl) from 0.9.102 to 0.9.103.
- [Release notes](https://github.com/sfackler/rust-openssl/releases)
- [Commits](https://github.com/sfackler/rust-openssl/compare/openssl-sys-v0.9.102...openssl-sys-v0.9.103)

---
updated-dependencies:
- dependency-name: openssl-sys
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-07-22 12:23:44 +07:00
dependabot[bot]
341c796ff8 Bump gix-path from 0.10.8 to 0.10.9
Bumps [gix-path](https://github.com/Byron/gitoxide) from 0.10.8 to 0.10.9.
- [Release notes](https://github.com/Byron/gitoxide/releases)
- [Changelog](https://github.com/Byron/gitoxide/blob/main/CHANGELOG.md)
- [Commits](https://github.com/Byron/gitoxide/compare/gix-path-v0.10.8...gix-path-v0.10.9)

---
updated-dependencies:
- dependency-name: gix-path
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-07-19 12:06:29 +07:00
dependabot[bot]
ef45704a3c Bump thiserror from 1.0.62 to 1.0.63
Bumps [thiserror](https://github.com/dtolnay/thiserror) from 1.0.62 to 1.0.63.
- [Release notes](https://github.com/dtolnay/thiserror/releases)
- [Commits](https://github.com/dtolnay/thiserror/compare/1.0.62...1.0.63)

---
updated-dependencies:
- dependency-name: thiserror
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-07-19 11:52:41 +07:00
dependabot[bot]
d7aa8c8924 Bump tui-textarea from 0.5.0 to 0.5.1
Bumps [tui-textarea](https://github.com/rhysd/tui-textarea) from 0.5.0 to 0.5.1.
- [Release notes](https://github.com/rhysd/tui-textarea/releases)
- [Changelog](https://github.com/rhysd/tui-textarea/blob/main/CHANGELOG.md)
- [Commits](https://github.com/rhysd/tui-textarea/compare/v0.5.0...v0.5.1)

---
updated-dependencies:
- dependency-name: tui-textarea
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-07-16 12:57:26 +07:00
dependabot[bot]
bf4079a8a7 Bump thiserror from 1.0.61 to 1.0.62
Bumps [thiserror](https://github.com/dtolnay/thiserror) from 1.0.61 to 1.0.62.
- [Release notes](https://github.com/dtolnay/thiserror/releases)
- [Commits](https://github.com/dtolnay/thiserror/compare/1.0.61...1.0.62)

---
updated-dependencies:
- dependency-name: thiserror
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-07-14 12:11:36 +07:00
extrawurst
d3786992ba cargo update 2024-07-09 09:06:56 +02:00
extrawurst
51ce196d45 missing lock file changes 2024-07-09 09:04:18 +02:00
extrawurst
017ca45aed cargo updates 2024-07-08 23:40:44 +02:00
Christoph Rüßler
d30de223fe
Add LogWalkerWithoutFilter, using gitoxide (#2275)
* Add LogWalkerWithoutFilter, using gitoxide
* Use feature revision for gix

---------

Co-authored-by: extrawurst <776816+extrawurst@users.noreply.github.com>
2024-07-08 09:25:44 +02:00
extrawurst
95bbbda9af ratatui update 2024-07-08 08:52:39 +02:00
dependabot[bot]
72b954a1a5 Bump serde from 1.0.202 to 1.0.204
Bumps [serde](https://github.com/serde-rs/serde) from 1.0.202 to 1.0.204.
- [Release notes](https://github.com/serde-rs/serde/releases)
- [Commits](https://github.com/serde-rs/serde/compare/v1.0.202...v1.0.204)

---
updated-dependencies:
- dependency-name: serde
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-07-08 08:30:37 +02:00
dependabot[bot]
6ec7eea02b Bump clap from 4.5.7 to 4.5.8
Bumps [clap](https://github.com/clap-rs/clap) from 4.5.7 to 4.5.8.
- [Release notes](https://github.com/clap-rs/clap/releases)
- [Changelog](https://github.com/clap-rs/clap/blob/master/CHANGELOG.md)
- [Commits](https://github.com/clap-rs/clap/compare/clap_complete-v4.5.7...v4.5.8)

---
updated-dependencies:
- dependency-name: clap
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-07-01 08:32:17 +02:00
dependabot[bot]
3229c5d5ed Bump log from 0.4.21 to 0.4.22
Bumps [log](https://github.com/rust-lang/log) from 0.4.21 to 0.4.22.
- [Release notes](https://github.com/rust-lang/log/releases)
- [Changelog](https://github.com/rust-lang/log/blob/master/CHANGELOG.md)
- [Commits](https://github.com/rust-lang/log/compare/0.4.21...0.4.22)

---
updated-dependencies:
- dependency-name: log
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-06-28 09:16:28 +02:00
dependabot[bot]
423b5ff3d1 Bump git2 from 0.18.3 to 0.19.0
Bumps [git2](https://github.com/rust-lang/git2-rs) from 0.18.3 to 0.19.0.
- [Changelog](https://github.com/rust-lang/git2-rs/blob/master/CHANGELOG.md)
- [Commits](https://github.com/rust-lang/git2-rs/compare/git2-0.18.3...git2-0.19.0)

---
updated-dependencies:
- dependency-name: git2
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-06-27 12:45:18 +02:00
dependabot[bot]
5d06aac1ad Bump bitflags from 2.5.0 to 2.6.0
Bumps [bitflags](https://github.com/bitflags/bitflags) from 2.5.0 to 2.6.0.
- [Release notes](https://github.com/bitflags/bitflags/releases)
- [Changelog](https://github.com/bitflags/bitflags/blob/main/CHANGELOG.md)
- [Commits](https://github.com/bitflags/bitflags/compare/2.5.0...2.6.0)

---
updated-dependencies:
- dependency-name: bitflags
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-06-25 17:24:57 +02:00
dependabot[bot]
9995a28e54 Bump curve25519-dalek from 4.1.2 to 4.1.3
Bumps [curve25519-dalek](https://github.com/dalek-cryptography/curve25519-dalek) from 4.1.2 to 4.1.3.
- [Release notes](https://github.com/dalek-cryptography/curve25519-dalek/releases)
- [Commits](https://github.com/dalek-cryptography/curve25519-dalek/compare/curve25519-4.1.2...curve25519-4.1.3)

---
updated-dependencies:
- dependency-name: curve25519-dalek
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-06-20 00:34:47 +02:00
extrawurst
038c4a50d1 fix new rust warnings 2024-06-14 21:40:46 +02:00
dependabot[bot]
65410e7293 Bump backtrace from 0.3.71 to 0.3.73
Bumps [backtrace](https://github.com/rust-lang/backtrace-rs) from 0.3.71 to 0.3.73.
- [Release notes](https://github.com/rust-lang/backtrace-rs/releases)
- [Commits](https://github.com/rust-lang/backtrace-rs/compare/0.3.71...0.3.73)

---
updated-dependencies:
- dependency-name: backtrace
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-06-13 23:23:58 +02:00
dependabot[bot]
f126f7a9f6 Bump clap from 4.5.6 to 4.5.7
Bumps [clap](https://github.com/clap-rs/clap) from 4.5.6 to 4.5.7.
- [Release notes](https://github.com/clap-rs/clap/releases)
- [Changelog](https://github.com/clap-rs/clap/blob/master/CHANGELOG.md)
- [Commits](https://github.com/clap-rs/clap/compare/v4.5.6...v4.5.7)

---
updated-dependencies:
- dependency-name: clap
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-06-11 10:39:28 +02:00
dependabot[bot]
cb1e8da619 Bump clap from 4.5.4 to 4.5.6
Bumps [clap](https://github.com/clap-rs/clap) from 4.5.4 to 4.5.6.
- [Release notes](https://github.com/clap-rs/clap/releases)
- [Changelog](https://github.com/clap-rs/clap/blob/master/CHANGELOG.md)
- [Commits](https://github.com/clap-rs/clap/compare/clap_complete-v4.5.4...v4.5.6)

---
updated-dependencies:
- dependency-name: clap
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-06-07 09:47:54 +02:00
dependabot[bot]
7f89962f72 Bump unicode-width from 0.1.12 to 0.1.13
Bumps [unicode-width](https://github.com/unicode-rs/unicode-width) from 0.1.12 to 0.1.13.
- [Commits](https://github.com/unicode-rs/unicode-width/compare/v0.1.12...v0.1.13)

---
updated-dependencies:
- dependency-name: unicode-width
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-06-05 08:18:12 +02:00
extrawurst
95e1d4d432 release prep 2024-06-02 14:49:32 +02:00
extrawurst
e818a6a35e clarification 2024-06-02 14:49:17 +02:00
extrawurst
7f161d61cd release prep 2024-06-02 14:24:02 +02:00
extrawurst
671c8416e2 support mac x86 release 2024-06-02 14:20:46 +02:00
extrawurst
23d6cbe0f2 use locked on release builds 2024-06-02 14:17:47 +02:00
extrawurst
43fac4f12a fix nightly 2024-06-02 14:08:23 +02:00
extrawurst
09747c2248 document new apple x86 nightly 2024-06-02 14:01:13 +02:00
extrawurst
a89044fd94
Build apple x86 (#2253) 2024-06-02 13:54:32 +02:00
extrawurst
baac3d01be rename for explicity 2024-05-30 16:03:16 +02:00
Caleb Maclennan
d1ebc56bfe
Allow builds from 'git archive' generated tarballs (#2187)
Co-authored-by: extrawurst <776816+extrawurst@users.noreply.github.com>
2024-05-30 15:59:46 +02:00
extrawurst
a085add733 fix typo 2024-05-23 11:19:21 +02:00
extrawurst
ff59eb6c10 unittest more of the color formats
* to ensure noticing if breaking changes happen
* document breaking change in changelog
2024-05-22 17:41:35 +02:00
extrawurst
659ee74b04 cargo update 2024-05-22 11:27:31 +02:00
dependabot[bot]
d3186b5d10 ---
updated-dependencies:
- dependency-name: ratatui
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-05-21 08:34:19 +02:00
extrawurst
b925155b7f upgrade yanked libc 2024-05-20 17:17:19 +02:00
extrawurst
6ad80ea3a9 update lockfile 2024-05-18 11:39:27 +02:00
extrawurst
dd255837ab release prep 2024-05-18 11:38:55 +02:00
extrawurst
53e9c8d1ef version bump for rc 2024-05-16 19:17:47 +02:00
extrawurst
93fee307d6 version bump 2024-05-16 19:14:32 +02:00
extrawurst
9be28c9af9
Cargo update (#2230) 2024-05-16 18:23:50 +02:00
Bernhard M. Wiedemann
f56844501e
Allow to override build date with SOURCE_DATE_EPOCH (#2202)
to make builds reproducible.
See https://reproducible-builds.org/ for why this is good
and https://reproducible-builds.org/specs/source-date-epoch/
for the definition of this variable.

This patch was done while working on reproducible builds for openSUSE.

---------

Co-authored-by: extrawurst <mail@rusticorn.com>
2024-05-16 17:17:58 +02:00
extrawurst
3dca5feeec
ratatui 0.25 update (#2101)
fixes #2098
2024-05-16 17:07:30 +02:00
Bryan Behrenshausen
fa2515343b
add : to signoff trailer (#2197)
fixes #2196
2024-05-16 13:57:51 +02:00
Christoph Rüßler
a92be3be9d
Get default fetch remote from configuration (#2204)
fixes #1093
2024-05-16 12:03:55 +02:00
dependabot[bot]
7651fdba89 Bump anyhow from 1.0.82 to 1.0.83
Bumps [anyhow](https://github.com/dtolnay/anyhow) from 1.0.82 to 1.0.83.
- [Release notes](https://github.com/dtolnay/anyhow/releases)
- [Commits](https://github.com/dtolnay/anyhow/compare/1.0.82...1.0.83)

---
updated-dependencies:
- dependency-name: anyhow
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-05-16 12:02:56 +02:00
dependabot[bot]
58f5326fb8 Bump serde from 1.0.199 to 1.0.202
Bumps [serde](https://github.com/serde-rs/serde) from 1.0.199 to 1.0.202.
- [Release notes](https://github.com/serde-rs/serde/releases)
- [Commits](https://github.com/serde-rs/serde/compare/v1.0.199...v1.0.202)

---
updated-dependencies:
- dependency-name: serde
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-05-16 12:02:46 +02:00
extrawurst
98e0379736 fix cargo deny deprecation warnings 2024-05-16 12:02:16 +02:00
extrawurst
4fe52dc117 correct fix 2024-05-16 12:01:22 +02:00
extrawurst
b5b2eb6007 fix cargo config warning 2024-05-16 12:01:22 +02:00
dependabot[bot]
1a582dacc4 Bump thiserror from 1.0.58 to 1.0.60
Bumps [thiserror](https://github.com/dtolnay/thiserror) from 1.0.58 to 1.0.60.
- [Release notes](https://github.com/dtolnay/thiserror/releases)
- [Commits](https://github.com/dtolnay/thiserror/compare/1.0.58...1.0.60)

---
updated-dependencies:
- dependency-name: thiserror
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-05-16 11:46:32 +02:00
dependabot[bot]
be7daa60c5 Bump unicode-width from 0.1.11 to 0.1.12
Bumps [unicode-width](https://github.com/unicode-rs/unicode-width) from 0.1.11 to 0.1.12.
- [Commits](https://github.com/unicode-rs/unicode-width/compare/v0.1.11...v0.1.12)

---
updated-dependencies:
- dependency-name: unicode-width
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-05-16 11:20:53 +02:00
extrawurst
6e12c1e974 fix clippy and nightly build 2024-05-16 11:11:05 +02:00
dependabot[bot]
c57543b4f8 Bump two-face from 0.3.0 to 0.4.0
Bumps [two-face](https://github.com/CosmicHorrorDev/two-face) from 0.3.0 to 0.4.0.
- [Release notes](https://github.com/CosmicHorrorDev/two-face/releases)
- [Changelog](https://github.com/CosmicHorrorDev/two-face/blob/main/CHANGELOG.md)
- [Commits](https://github.com/CosmicHorrorDev/two-face/compare/v0.3.0...v0.4.0)

---
updated-dependencies:
- dependency-name: two-face
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-04-29 19:42:29 +02:00
dependabot[bot]
5ad54ee675 Bump unicode-truncate from 0.2.0 to 1.0.0
Bumps [unicode-truncate](https://github.com/Aetf/unicode-truncate) from 0.2.0 to 1.0.0.
- [Release notes](https://github.com/Aetf/unicode-truncate/releases)
- [Changelog](https://github.com/Aetf/unicode-truncate/blob/master/CHANGELOG.md)
- [Commits](https://github.com/Aetf/unicode-truncate/compare/v0.2.0...v1.0.0)

---
updated-dependencies:
- dependency-name: unicode-truncate
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-04-29 13:32:22 +02:00
dependabot[bot]
c2d23da543 Bump serial_test from 3.0.0 to 3.1.1
Bumps [serial_test](https://github.com/palfrey/serial_test) from 3.0.0 to 3.1.1.
- [Release notes](https://github.com/palfrey/serial_test/releases)
- [Commits](https://github.com/palfrey/serial_test/compare/v3.0.0...v3.1.1)

---
updated-dependencies:
- dependency-name: serial_test
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-04-29 13:32:12 +02:00
dependabot[bot]
a61677b9e6 Bump serde from 1.0.198 to 1.0.199
Bumps [serde](https://github.com/serde-rs/serde) from 1.0.198 to 1.0.199.
- [Release notes](https://github.com/serde-rs/serde/releases)
- [Commits](https://github.com/serde-rs/serde/compare/v1.0.198...v1.0.199)

---
updated-dependencies:
- dependency-name: serde
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-04-29 13:31:38 +02:00
dependabot[bot]
64a1e3866e Bump serde from 1.0.197 to 1.0.198
Bumps [serde](https://github.com/serde-rs/serde) from 1.0.197 to 1.0.198.
- [Release notes](https://github.com/serde-rs/serde/releases)
- [Commits](https://github.com/serde-rs/serde/compare/v1.0.197...v1.0.198)

---
updated-dependencies:
- dependency-name: serde
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-04-21 18:07:21 +02:00
dependabot[bot]
920c28cfd7 Bump chrono from 0.4.37 to 0.4.38
Bumps [chrono](https://github.com/chronotope/chrono) from 0.4.37 to 0.4.38.
- [Release notes](https://github.com/chronotope/chrono/releases)
- [Changelog](https://github.com/chronotope/chrono/blob/main/CHANGELOG.md)
- [Commits](https://github.com/chronotope/chrono/compare/v0.4.37...v0.4.38)

---
updated-dependencies:
- dependency-name: chrono
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-04-16 08:22:12 +02:00
dependabot[bot]
d6ab75399b Bump ssh-key from 0.6.5 to 0.6.6
Bumps [ssh-key](https://github.com/RustCrypto/SSH) from 0.6.5 to 0.6.6.
- [Commits](https://github.com/RustCrypto/SSH/compare/ssh-key/v0.6.5...ssh-key/v0.6.6)

---
updated-dependencies:
- dependency-name: ssh-key
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-04-15 08:10:11 +02:00
extrawurst
4fc55a2263 match release notes 2024-04-14 22:23:44 +02:00
extrawurst
7b437d030f comment on 0.26.1 windows only 2024-04-14 22:12:02 +02:00
extrawurst
c55188e462 prepare for release 2024-04-14 22:08:55 +02:00
extrawurst
3de9953dde allow manual cd 2024-04-14 21:43:56 +02:00
extrawurst
218d739b03 fix windows release 2024-04-14 21:42:59 +02:00
extrawurst
4bf1290610 add a release image 2024-04-14 21:33:50 +02:00
extrawurst
a72c62bd82 upgrade like in f27ca92 2024-04-14 21:01:05 +02:00
extrawurst
56e2da3027 remove itch, add discord 2024-04-14 20:57:39 +02:00
extrawurst
f14bf271c7 additional info 2024-04-14 20:56:49 +02:00
extrawurst
f27ca92f10 upgrade ci action 2024-04-14 20:35:42 +02:00
extrawurst
09a2f70c89
Release prep (#2183) 2024-04-14 20:31:05 +02:00
Christoph Rüßler
b08eddb45b
Get default push remote from configuration (#2156) 2024-04-14 12:12:09 +02:00
dependabot[bot]
fe05f93a74 Bump anyhow from 1.0.81 to 1.0.82
Bumps [anyhow](https://github.com/dtolnay/anyhow) from 1.0.81 to 1.0.82.
- [Release notes](https://github.com/dtolnay/anyhow/releases)
- [Commits](https://github.com/dtolnay/anyhow/compare/1.0.81...1.0.82)

---
updated-dependencies:
- dependency-name: anyhow
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-04-11 08:11:20 +02:00
extrawurst
665e99779e update changelog and readme 2024-04-10 11:56:55 +02:00
Antonio Yang
945ec45027
sign with ssh key in disc (#2175) 2024-04-10 11:53:23 +02:00
extrawurst
5d68ea0e39
Reset branch in branch popup (#2171)
allow triggering branch reset from branch popup. closes #2170
2024-04-07 18:12:30 +02:00
extrawurst
5053850433
Update NIGHTLIES.md
Fix links for windows
2024-03-31 12:46:15 +01:00
extrawurst
f91621be09 make necesaary sudo usage more obvious to user 2024-03-31 11:36:34 +01:00
dependabot[bot]
21491bcbca Bump openssl-sys from 0.9.101 to 0.9.102
Bumps [openssl-sys](https://github.com/sfackler/rust-openssl) from 0.9.101 to 0.9.102.
- [Release notes](https://github.com/sfackler/rust-openssl/releases)
- [Commits](https://github.com/sfackler/rust-openssl/compare/openssl-sys-v0.9.101...openssl-sys-v0.9.102)

---
updated-dependencies:
- dependency-name: openssl-sys
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-03-29 13:50:13 +00:00
dependabot[bot]
188d00159b Bump chrono from 0.4.35 to 0.4.37
Bumps [chrono](https://github.com/chronotope/chrono) from 0.4.35 to 0.4.37.
- [Release notes](https://github.com/chronotope/chrono/releases)
- [Changelog](https://github.com/chronotope/chrono/blob/main/CHANGELOG.md)
- [Commits](https://github.com/chronotope/chrono/compare/v0.4.35...v0.4.37)

---
updated-dependencies:
- dependency-name: chrono
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-03-28 08:36:00 +00:00
extrawurst
29fb29638f fix win msi naming 2024-03-27 18:00:47 +00:00
extrawurst
48b6c07843 some more changelog entries 2024-03-27 17:38:38 +00:00
extrawurst
44e1c56a1c
provide nightly builds (#2164)
closes #2083
2024-03-27 17:28:31 +00:00
extrawurst
2ec6d632ed encode nightly version, commit, date into binary 2024-03-27 14:24:05 +00:00
extrawurst
0966953050 fix changelog 2024-03-27 14:00:47 +00:00
Martí Homs Soler
3b7b443e2a
syntect additional file type support (#2160)
closes #2005

Co-authored-by: MHS <mhs@histolution.com>
2024-03-27 13:57:37 +00:00
extrawurst
5fafe784d1 rename artifacts for clarity (#2148) 2024-03-27 13:46:54 +00:00
extrawurst
4cc6b459cc Revert "encode nightly version, commit, date into binary"
This reverts commit 89e9f855cb.
2024-03-27 13:02:31 +00:00
extrawurst
89e9f855cb encode nightly version, commit, date into binary 2024-03-27 12:49:53 +00:00
Concelare
92e0face1e
add tests for commitChar filtering (#2151)
adds missing tests for b15c8643c8 (see #2145)
2024-03-26 15:50:13 +00:00
dependabot[bot]
8272b20913 Bump clap from 4.5.3 to 4.5.4
Bumps [clap](https://github.com/clap-rs/clap) from 4.5.3 to 4.5.4.
- [Release notes](https://github.com/clap-rs/clap/releases)
- [Changelog](https://github.com/clap-rs/clap/blob/master/CHANGELOG.md)
- [Commits](https://github.com/clap-rs/clap/compare/v4.5.3...v4.5.4)

---
updated-dependencies:
- dependency-name: clap
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-03-26 15:43:46 +00:00
dependabot[bot]
ba26a1a664 Bump indexmap from 2.2.5 to 2.2.6
Bumps [indexmap](https://github.com/indexmap-rs/indexmap) from 2.2.5 to 2.2.6.
- [Changelog](https://github.com/indexmap-rs/indexmap/blob/master/RELEASES.md)
- [Commits](https://github.com/indexmap-rs/indexmap/compare/2.2.5...2.2.6)

---
updated-dependencies:
- dependency-name: indexmap
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-03-25 01:35:12 -07:00
dependabot[bot]
bb2640e893 Bump which from 6.0.0 to 6.0.1
Bumps [which](https://github.com/harryfei/which-rs) from 6.0.0 to 6.0.1.
- [Release notes](https://github.com/harryfei/which-rs/releases)
- [Changelog](https://github.com/harryfei/which-rs/blob/master/CHANGELOG.md)
- [Commits](https://github.com/harryfei/which-rs/compare/6.0.0...6.0.1)

---
updated-dependencies:
- dependency-name: which
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-03-24 23:01:35 -07:00
dependabot[bot]
9d94b9f600 Bump rayon from 1.9.0 to 1.10.0
Bumps [rayon](https://github.com/rayon-rs/rayon) from 1.9.0 to 1.10.0.
- [Changelog](https://github.com/rayon-rs/rayon/blob/main/RELEASES.md)
- [Commits](https://github.com/rayon-rs/rayon/compare/rayon-core-v1.9.0...rayon-core-v1.10.0)

---
updated-dependencies:
- dependency-name: rayon
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-03-24 22:57:48 -07:00
ZingyAwesome
ef6abbba0f
Fix typo in commit.rs (#2150) 2024-03-24 15:14:07 -07:00
extrawurst
3342544dfb update readme now that gpgsign is there 2024-03-24 13:24:15 -07:00
extrawurst
6515831c95 more changelog details 2024-03-24 13:13:26 -07:00
extrawurst
93959aff5d fix changelog entry 2024-03-24 13:11:15 -07:00
Hendrik Maus
5b3e2c9ae3
Support git commit signing using OpenPGP (#1544)
* Support git commit signing using OpenPGP
* workaround for amending signed commits
* workaround for rewording signed commits
* support signing initial commit
* return both signature and signature_field value from sign

---------

Co-authored-by: Utkarsh Gupta <utkarshgupta137@gmail.com>
2024-03-24 13:08:28 -07:00
Michael
5131aba138
Make MsgPopup scrollable (#2120) 2024-03-24 12:57:59 -07:00
extrawurst
540a95c160 fix failing to commit since b15c864
(if it would not find core.commitComment config)
2024-03-24 12:54:00 -07:00
extrawurst
9c8c802b20 fix nightly clippy 2024-03-24 12:50:56 -07:00
extrawurst
76814a36e3 update yanked 2024-03-24 12:42:35 -07:00
Christoph Rüßler
7d8b7c9239
Add CONTRIBUTING.md (#2121) 2024-03-24 12:40:39 -07:00
Concelare
b15c8643c8
Git Config Commit Comments (#2145)
Closes #2136
2024-03-22 15:12:08 -07:00
extrawurst
3d06b54d2c update changelog for 0.25.2 2024-03-22 12:58:56 -07:00
extrawurst
f9ebba3043 fix another github action warning 2024-03-22 12:57:43 -07:00
extrawurst
205555916c upgrade version 2024-03-22 12:32:55 -07:00
extrawurst
3db4425b98 prepare release 2024-03-22 12:23:06 -07:00
extrawurst
a4e08834e2 preapre for release 2024-03-22 12:20:57 -07:00
extrawurst
512f2f439e changelog 2024-03-22 12:19:03 -07:00
extrawurst
bb92067b19
fixes tag annotation being broken in 0.25 (#2139)
fixes #2126
2024-03-22 12:18:05 -07:00
extrawurst
dddb35ac15 update changelog 2024-03-22 12:14:59 -07:00
martihomssoler
f40d368069 fix: 2114 2024-03-22 12:13:14 -07:00
dependabot[bot]
b7b7705df2 Bump backtrace from 0.3.69 to 0.3.70
Bumps [backtrace](https://github.com/rust-lang/backtrace-rs) from 0.3.69 to 0.3.70.
- [Release notes](https://github.com/rust-lang/backtrace-rs/releases)
- [Commits](https://github.com/rust-lang/backtrace-rs/compare/0.3.69...0.3.70)

---
updated-dependencies:
- dependency-name: backtrace
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-03-21 23:03:10 -07:00
Concelare
5dd1852dd3 Fixed:
- Cargo clippy errors

Updated:
- Ran cargo clippy --fix
- Ran cargo fmt
2024-03-21 23:01:02 -07:00
extrawurst
02dfae7bd4 cargo updates 2024-03-19 18:07:05 -07:00
extrawurst
96116ce405 cargo update 2024-03-19 18:03:30 -07:00
extrawurst
1a1c948020 todo 2024-03-19 17:56:42 -07:00
extrawurst
b43c4c05f6 update changelog 2024-03-19 17:43:38 -07:00
Trung Tran
99b9a38fb3 fix: index out of bounds when blaming a file ending with a blank line (#2130) 2024-03-20 01:37:59 +01:00
dependabot[bot]
baddec0e39 Bump bitflags from 2.4.2 to 2.5.0
Bumps [bitflags](https://github.com/bitflags/bitflags) from 2.4.2 to 2.5.0.
- [Release notes](https://github.com/bitflags/bitflags/releases)
- [Changelog](https://github.com/bitflags/bitflags/blob/main/CHANGELOG.md)
- [Commits](https://github.com/bitflags/bitflags/compare/2.4.2...2.5.0)

---
updated-dependencies:
- dependency-name: bitflags
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-03-19 08:52:05 +01:00
dependabot[bot]
0fba25506a Bump clap from 4.5.2 to 4.5.3
Bumps [clap](https://github.com/clap-rs/clap) from 4.5.2 to 4.5.3.
- [Release notes](https://github.com/clap-rs/clap/releases)
- [Changelog](https://github.com/clap-rs/clap/blob/master/CHANGELOG.md)
- [Commits](https://github.com/clap-rs/clap/compare/v4.5.2...v4.5.3)

---
updated-dependencies:
- dependency-name: clap
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-03-18 08:18:45 +01:00
extrawurst
0ab1ff8625 fix chrono deprecations 2024-03-13 11:00:31 +01:00
dependabot[bot]
2274d7c22f Bump thiserror from 1.0.57 to 1.0.58
Bumps [thiserror](https://github.com/dtolnay/thiserror) from 1.0.57 to 1.0.58.
- [Release notes](https://github.com/dtolnay/thiserror/releases)
- [Commits](https://github.com/dtolnay/thiserror/compare/1.0.57...1.0.58)

---
updated-dependencies:
- dependency-name: thiserror
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-03-12 09:58:29 +01:00
dependabot[bot]
43e44bf02a Bump anyhow from 1.0.80 to 1.0.81
Bumps [anyhow](https://github.com/dtolnay/anyhow) from 1.0.80 to 1.0.81.
- [Release notes](https://github.com/dtolnay/anyhow/releases)
- [Commits](https://github.com/dtolnay/anyhow/compare/1.0.80...1.0.81)

---
updated-dependencies:
- dependency-name: anyhow
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-03-12 09:58:13 +01:00
Charlie
21810a4df6 Fix typo in FAQ.md 2024-03-08 11:59:43 +01:00
Juan
43af49bab4
Remove unused clippy warning suppression (missing_const_for_fn) (#2115) 2024-03-07 20:08:28 +01:00
dependabot[bot]
f130cd5068 Bump clap from 4.5.1 to 4.5.2
Bumps [clap](https://github.com/clap-rs/clap) from 4.5.1 to 4.5.2.
- [Release notes](https://github.com/clap-rs/clap/releases)
- [Changelog](https://github.com/clap-rs/clap/blob/master/CHANGELOG.md)
- [Commits](https://github.com/clap-rs/clap/compare/clap_complete-v4.5.1...v4.5.2)

---
updated-dependencies:
- dependency-name: clap
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-03-07 09:47:52 +01:00
dependabot[bot]
ba5d89595e Bump chrono from 0.4.34 to 0.4.35
Bumps [chrono](https://github.com/chronotope/chrono) from 0.4.34 to 0.4.35.
- [Release notes](https://github.com/chronotope/chrono/releases)
- [Changelog](https://github.com/chronotope/chrono/blob/main/CHANGELOG.md)
- [Commits](https://github.com/chronotope/chrono/compare/v0.4.34...v0.4.35)

---
updated-dependencies:
- dependency-name: chrono
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-03-07 09:47:38 +01:00
dependabot[bot]
f68aba0ffa Bump env_logger from 0.11.2 to 0.11.3
Bumps [env_logger](https://github.com/rust-cli/env_logger) from 0.11.2 to 0.11.3.
- [Release notes](https://github.com/rust-cli/env_logger/releases)
- [Changelog](https://github.com/rust-cli/env_logger/blob/main/CHANGELOG.md)
- [Commits](https://github.com/rust-cli/env_logger/compare/v0.11.2...v0.11.3)

---
updated-dependencies:
- dependency-name: env_logger
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-03-06 08:41:56 +01:00
dependabot[bot]
7556282f17 Bump simplelog from 0.12.1 to 0.12.2
Bumps [simplelog](https://github.com/drakulix/simplelog.rs) from 0.12.1 to 0.12.2.
- [Changelog](https://github.com/Drakulix/simplelog.rs/blob/master/CHANGELOG.md)
- [Commits](https://github.com/drakulix/simplelog.rs/compare/v0.12.1...v0.12.2)

---
updated-dependencies:
- dependency-name: simplelog
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-03-05 07:28:21 +01:00
dependabot[bot]
7cda59d87e Bump mio from 0.8.10 to 0.8.11
Bumps [mio](https://github.com/tokio-rs/mio) from 0.8.10 to 0.8.11.
- [Release notes](https://github.com/tokio-rs/mio/releases)
- [Changelog](https://github.com/tokio-rs/mio/blob/master/CHANGELOG.md)
- [Commits](https://github.com/tokio-rs/mio/compare/v0.8.10...v0.8.11)

---
updated-dependencies:
- dependency-name: mio
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-03-04 23:30:26 +01:00
dependabot[bot]
3b7e1f921a Bump indexmap from 2.2.4 to 2.2.5
Bumps [indexmap](https://github.com/indexmap-rs/indexmap) from 2.2.4 to 2.2.5.
- [Changelog](https://github.com/indexmap-rs/indexmap/blob/master/RELEASES.md)
- [Commits](https://github.com/indexmap-rs/indexmap/compare/2.2.4...2.2.5)

---
updated-dependencies:
- dependency-name: indexmap
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-03-04 08:49:19 +01:00
dependabot[bot]
3397ab5c75 Bump log from 0.4.20 to 0.4.21
Bumps [log](https://github.com/rust-lang/log) from 0.4.20 to 0.4.21.
- [Release notes](https://github.com/rust-lang/log/releases)
- [Changelog](https://github.com/rust-lang/log/blob/master/CHANGELOG.md)
- [Commits](https://github.com/rust-lang/log/compare/0.4.20...0.4.21)

---
updated-dependencies:
- dependency-name: log
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-02-29 07:57:15 +01:00
dependabot[bot]
e3b036daa5 Bump indexmap from 2.2.3 to 2.2.4
Bumps [indexmap](https://github.com/indexmap-rs/indexmap) from 2.2.3 to 2.2.4.
- [Changelog](https://github.com/indexmap-rs/indexmap/blob/master/RELEASES.md)
- [Commits](https://github.com/indexmap-rs/indexmap/compare/2.2.3...2.2.4)

---
updated-dependencies:
- dependency-name: indexmap
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-02-29 07:56:54 +01:00
dependabot[bot]
04d3587be2 Bump crossbeam-channel from 0.5.11 to 0.5.12
Bumps [crossbeam-channel](https://github.com/crossbeam-rs/crossbeam) from 0.5.11 to 0.5.12.
- [Release notes](https://github.com/crossbeam-rs/crossbeam/releases)
- [Changelog](https://github.com/crossbeam-rs/crossbeam/blob/master/CHANGELOG.md)
- [Commits](https://github.com/crossbeam-rs/crossbeam/compare/crossbeam-channel-0.5.11...crossbeam-channel-0.5.12)

---
updated-dependencies:
- dependency-name: crossbeam-channel
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-02-29 07:56:30 +01:00
dependabot[bot]
7de1adc476 Bump rayon from 1.8.1 to 1.9.0
Bumps [rayon](https://github.com/rayon-rs/rayon) from 1.8.1 to 1.9.0.
- [Changelog](https://github.com/rayon-rs/rayon/blob/main/RELEASES.md)
- [Commits](https://github.com/rayon-rs/rayon/compare/rayon-core-v1.8.1...rayon-core-v1.9.0)

---
updated-dependencies:
- dependency-name: rayon
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-02-28 14:00:23 +01:00
dependabot[bot]
aa54d6f8a6 Bump tempfile from 3.10.0 to 3.10.1
Bumps [tempfile](https://github.com/Stebalien/tempfile) from 3.10.0 to 3.10.1.
- [Changelog](https://github.com/Stebalien/tempfile/blob/master/CHANGELOG.md)
- [Commits](https://github.com/Stebalien/tempfile/compare/v3.10.0...v3.10.1)

---
updated-dependencies:
- dependency-name: tempfile
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-02-27 08:31:35 +01:00
extrawurst
11401872b5 git2-hooks wnaring fixes
and more clippy checks
2024-02-23 18:38:02 +01:00
extrawurst
e65d25da60 another ci update 2024-02-23 18:01:53 +01:00
extrawurst
8008e04c6b prep for release 2024-02-23 17:40:57 +01:00
extrawurst
b39a24db06 update changelog 2024-02-23 17:37:46 +01:00
extrawurst
f4d4a5e086 lock in release build 2024-02-23 17:33:52 +01:00
extrawurst
fd79b2d5e3 update ci to msrv 2024-02-23 17:29:51 +01:00
extrawurst
598cc778d4 update openssl-sys fixing always rebuilding 2024-02-22 00:23:20 +01:00
Joshix-1
d6818ecc1a Fix CHANGELOG.md
uppsi
2024-02-21 22:14:41 +01:00
Anas
c629070ea5
fix: fix typos (#2092) 2024-02-21 21:27:33 +01:00
extrawurst
5cff3221a1 pin ratatui release 2024-02-21 17:30:23 +01:00
extrawurst
456d50919c honor contrubtors in readme 2024-02-21 17:30:12 +01:00
extrawurst
4da1cc4473 make cargo install command more clear
see https://github.com/extrawurst/gitui/issues/2079#issuecomment-1957170943
2024-02-21 17:29:51 +01:00
Mo
8876c1d0f6 Remove references to bit flags 2024-02-21 16:31:56 +01:00
extrawurst
2a2f914839 bump dependency that got yanked
fixes #2087
2024-02-21 16:04:19 +01:00
extrawurst
7bf65f4835 documentation of breaking changes to key bindings 2024-02-21 15:49:18 +01:00
extrawurst
3f69f5fe62
Action updates (#2085) 2024-02-21 13:03:53 +01:00
extrawurst
9f0ef851b2
Prep release 25 (#2082) 2024-02-21 11:27:45 +01:00
Sainath Singineedi
fa051eff5a
feat: add branch name validation on renaming (#2081)
Closes #2062 

---------

Co-authored-by: extrawurst <776816+extrawurst@users.noreply.github.com>
2024-02-21 00:09:53 +01:00
extrawurst
a50a478c1a do not panic if invalid repo
fixes #2064
2024-02-20 22:14:48 +01:00
extrawurst
2bbaa6f3a1 fix a bunch more typos 2024-02-20 19:51:05 +01:00
extrawurst
5dad9f4adb
cancel commit search (#2078)
closes #1860

Co-authored-by: StemCll <lydjotj6f@mozmail.com>
2024-02-20 19:07:25 +01:00
extrawurst
762b889b48
better theme file handling (#2077)
* better theme file handling
* print all possible err of loading theme

closes #2007
2024-02-20 18:52:35 +01:00
extrawurst
2b39c6465a ratatui 0.24 update 2024-02-20 15:41:31 +01:00
extrawurst
45c34e5675
fix infinite spinner (#2075)
af9da95 introduced pending work to always be true
2024-02-20 11:56:37 +01:00
dependabot[bot]
1a6f82b71b Bump anyhow from 1.0.79 to 1.0.80
Bumps [anyhow](https://github.com/dtolnay/anyhow) from 1.0.79 to 1.0.80.
- [Release notes](https://github.com/dtolnay/anyhow/releases)
- [Commits](https://github.com/dtolnay/anyhow/compare/1.0.79...1.0.80)

---
updated-dependencies:
- dependency-name: anyhow
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-02-20 10:13:52 +01:00
dependabot[bot]
720e5debb8 Bump serde from 1.0.196 to 1.0.197
Bumps [serde](https://github.com/serde-rs/serde) from 1.0.196 to 1.0.197.
- [Release notes](https://github.com/serde-rs/serde/releases)
- [Commits](https://github.com/serde-rs/serde/compare/v1.0.196...v1.0.197)

---
updated-dependencies:
- dependency-name: serde
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-02-20 10:13:37 +01:00
dependabot[bot]
8fb6f23ff3 Bump openssl-sys from 0.9.99 to 0.9.100
Bumps [openssl-sys](https://github.com/sfackler/rust-openssl) from 0.9.99 to 0.9.100.
- [Release notes](https://github.com/sfackler/rust-openssl/releases)
- [Commits](https://github.com/sfackler/rust-openssl/compare/openssl-sys-v0.9.99...openssl-sys-v0.9.100)

---
updated-dependencies:
- dependency-name: openssl-sys
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-02-20 10:12:33 +01:00
extrawurst
e2590f79bd
better default for single line textinput size (#2070) 2024-02-20 09:43:15 +01:00
extrawurst
286283d3ed cleanup license checks 2024-02-19 22:37:18 +01:00
extrawurst
90a109b1dd allow fetch in both tabs of branch list 2024-02-19 22:16:42 +01:00
extrawurst
8b665a43fc update changelog with image of syntax in blame 2024-02-19 21:44:18 +01:00
extrawurst
f0f90a88f1 print theme path in error message too 2024-02-19 21:35:26 +01:00
extrawurst
1cb821cd51 fix ci 2024-02-19 21:32:08 +01:00
extrawurst
cae864a188 fix nigtly 2024-02-19 21:24:09 +01:00
extrawurst
e22cc70a7d
moving popups in its own module (#2066) 2024-02-19 17:18:30 +01:00
Trung Tran
af9da95178 add syntax highlighting for blame view (#745) 2024-02-19 17:15:03 +01:00
extrawurst
ab95b98ef8
allow pushing to empty repo (#2063)
closes #1919
2024-02-19 15:46:39 +01:00
extrawurst
be10d9096a print theme loading errors into log (#2056) 2024-02-19 12:52:17 +01:00
extrawurst
acf4661c1e fix nighty and raise msrv 2024-02-19 11:36:38 +01:00
extrawurst
e7bc4084da update docs to new bitflags serialization 2024-02-19 11:34:18 +01:00
extrawurst
f1da79b4b2 fix panics not shown on cli (closes #2049) 2024-02-18 14:01:44 +01:00
extrawurst
f4a7034a9a allow customizing color of focused title bar 2024-02-18 13:54:55 +01:00
extrawurst
e1f243d8fb wording 2024-02-18 12:48:11 +01:00
extrawurst
825935ba4f
Multiline TextEdit cleanups (#2053)
* change commit default binding
* animated gif of multiline text edit
* update changelog
* fix style of default text
* textinput should not need to know about its users
* fix branch create popup
2024-02-18 12:44:44 +01:00
pm100
b9a2e131f2
tui textarea (#2051) 2024-02-18 10:24:18 +01:00
extrawurst
3439862854 cargo update 2024-02-18 10:18:19 +01:00
extrawurst
89ce540653 fix all places of single line textinput 2024-02-15 21:47:03 +01:00
extrawurst
84d855d242
move audit checks to cargo-deny (#2048)
* move audit checks to cargo-deny
* remove separate sec audit step using old auditing
2024-02-14 11:32:16 +01:00
dependabot[bot]
1b8f9772f4 Bump env_logger from 0.11.1 to 0.11.2
Bumps [env_logger](https://github.com/rust-cli/env_logger) from 0.11.1 to 0.11.2.
- [Release notes](https://github.com/rust-cli/env_logger/releases)
- [Changelog](https://github.com/rust-cli/env_logger/blob/main/CHANGELOG.md)
- [Commits](https://github.com/rust-cli/env_logger/compare/v0.11.1...v0.11.2)

---
updated-dependencies:
- dependency-name: env_logger
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-02-14 11:31:56 +01:00
Joshix-1
7335cd1c5d
fix sorting of commits in diff view (#1747) 2024-02-12 20:33:46 +01:00
extrawurst
1fa6f9b725 update license 2024-02-12 17:31:03 +01:00
extrawurst
4c33ee4859
New Logo with outline (#2044) 2024-02-12 17:28:01 +01:00
dependabot[bot]
61ee3fd695 updates and fixes 2024-02-12 12:55:10 +01:00
Alexandru Macovei
0383f9517b (refactor) shorten component creation by grouping common items in an Environment 2024-02-12 12:55:10 +01:00
extrawurst
5259fd90b3 dump out release bin size 2024-02-12 12:03:31 +01:00
extrawurst
8bdba9ce7c cargo update 2024-02-12 12:00:24 +01:00
extrawurst
673edd8f2d
cargo updates (#1856)
closes #1781
2024-02-12 11:53:22 +01:00
dependabot[bot]
d5f88df88a Bump clap from 4.4.18 to 4.5.0
Bumps [clap](https://github.com/clap-rs/clap) from 4.4.18 to 4.5.0.
- [Release notes](https://github.com/clap-rs/clap/releases)
- [Changelog](https://github.com/clap-rs/clap/blob/master/CHANGELOG.md)
- [Commits](https://github.com/clap-rs/clap/compare/v4.4.18...clap_complete-v4.5.0)

---
updated-dependencies:
- dependency-name: clap
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-02-11 18:45:55 +01:00
dependabot[bot]
0a3736e765 Bump unicode-segmentation from 1.10.1 to 1.11.0
Bumps [unicode-segmentation](https://github.com/unicode-rs/unicode-segmentation) from 1.10.1 to 1.11.0.
- [Commits](https://github.com/unicode-rs/unicode-segmentation/compare/v1.10.1...v1.11.0)

---
updated-dependencies:
- dependency-name: unicode-segmentation
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-02-08 22:48:02 +00:00
dependabot[bot]
961b731c59 Bump itertools from 0.12.0 to 0.12.1
Bumps [itertools](https://github.com/rust-itertools/itertools) from 0.12.0 to 0.12.1.
- [Changelog](https://github.com/rust-itertools/itertools/blob/master/CHANGELOG.md)
- [Commits](https://github.com/rust-itertools/itertools/compare/v0.12.0...v0.12.1)

---
updated-dependencies:
- dependency-name: itertools
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-01-30 08:17:15 +01:00
dependabot[bot]
c30eb7c0a8 Bump chrono from 0.4.32 to 0.4.33
Bumps [chrono](https://github.com/chronotope/chrono) from 0.4.32 to 0.4.33.
- [Release notes](https://github.com/chronotope/chrono/releases)
- [Changelog](https://github.com/chronotope/chrono/blob/main/CHANGELOG.md)
- [Commits](https://github.com/chronotope/chrono/compare/v0.4.32...v0.4.33)

---
updated-dependencies:
- dependency-name: chrono
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-01-26 09:42:26 +01:00
extrawurst
4f89ad5be4 strip debug symbols in release 2024-01-25 16:04:18 +01:00
extrawurst
1487fe3433 remove obsolete donation methods 2024-01-24 10:25:48 +01:00
dependabot[bot]
b0922080e3 Bump openssl-sys from 0.9.98 to 0.9.99
Bumps [openssl-sys](https://github.com/sfackler/rust-openssl) from 0.9.98 to 0.9.99.
- [Release notes](https://github.com/sfackler/rust-openssl/releases)
- [Commits](https://github.com/sfackler/rust-openssl/compare/openssl-sys-v0.9.98...openssl-sys-v0.9.99)

---
updated-dependencies:
- dependency-name: openssl-sys
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-01-23 08:45:04 +01:00
dependabot[bot]
9b00036937 Bump chrono from 0.4.31 to 0.4.32
Bumps [chrono](https://github.com/chronotope/chrono) from 0.4.31 to 0.4.32.
- [Release notes](https://github.com/chronotope/chrono/releases)
- [Changelog](https://github.com/chronotope/chrono/blob/main/CHANGELOG.md)
- [Commits](https://github.com/chronotope/chrono/compare/v0.4.31...v0.4.32)

---
updated-dependencies:
- dependency-name: chrono
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-01-23 08:44:33 +01:00
dependabot[bot]
21ce2015a0 Bump anyhow from 1.0.78 to 1.0.79
Bumps [anyhow](https://github.com/dtolnay/anyhow) from 1.0.78 to 1.0.79.
- [Release notes](https://github.com/dtolnay/anyhow/releases)
- [Commits](https://github.com/dtolnay/anyhow/compare/1.0.78...1.0.79)

---
updated-dependencies:
- dependency-name: anyhow
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-01-22 15:33:31 +01:00
dependabot[bot]
4fec18fa4e Bump clap from 4.4.14 to 4.4.18
Bumps [clap](https://github.com/clap-rs/clap) from 4.4.14 to 4.4.18.
- [Release notes](https://github.com/clap-rs/clap/releases)
- [Changelog](https://github.com/clap-rs/clap/blob/master/CHANGELOG.md)
- [Commits](https://github.com/clap-rs/clap/compare/v4.4.14...v4.4.18)

---
updated-dependencies:
- dependency-name: clap
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-01-22 15:33:09 +01:00
dependabot[bot]
f21c05bd00 Bump env_logger from 0.10.1 to 0.10.2
Bumps [env_logger](https://github.com/rust-cli/env_logger) from 0.10.1 to 0.10.2.
- [Release notes](https://github.com/rust-cli/env_logger/releases)
- [Changelog](https://github.com/rust-cli/env_logger/blob/main/CHANGELOG.md)
- [Commits](https://github.com/rust-cli/env_logger/compare/v0.10.1...v0.10.2)

---
updated-dependencies:
- dependency-name: env_logger
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-01-22 14:49:04 +01:00
dependabot[bot]
f6497140fa Bump rayon from 1.8.0 to 1.8.1
Bumps [rayon](https://github.com/rayon-rs/rayon) from 1.8.0 to 1.8.1.
- [Changelog](https://github.com/rayon-rs/rayon/blob/master/RELEASES.md)
- [Commits](https://github.com/rayon-rs/rayon/compare/rayon-core-v1.8.0...rayon-core-v1.8.1)

---
updated-dependencies:
- dependency-name: rayon
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-01-22 14:48:48 +01:00
dependabot[bot]
e34a4f82ea Bump rayon-core from 1.12.0 to 1.12.1
Bumps [rayon-core](https://github.com/rayon-rs/rayon) from 1.12.0 to 1.12.1.
- [Changelog](https://github.com/rayon-rs/rayon/blob/master/RELEASES.md)
- [Commits](https://github.com/rayon-rs/rayon/compare/rayon-core-v1.12.0...rayon-core-v1.12.1)

---
updated-dependencies:
- dependency-name: rayon-core
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-01-22 14:48:31 +01:00
dependabot[bot]
0beff5c569 Bump crossbeam-channel from 0.5.10 to 0.5.11
Bumps [crossbeam-channel](https://github.com/crossbeam-rs/crossbeam) from 0.5.10 to 0.5.11.
- [Release notes](https://github.com/crossbeam-rs/crossbeam/releases)
- [Changelog](https://github.com/crossbeam-rs/crossbeam/blob/master/CHANGELOG.md)
- [Commits](https://github.com/crossbeam-rs/crossbeam/compare/crossbeam-channel-0.5.10...crossbeam-channel-0.5.11)

---
updated-dependencies:
- dependency-name: crossbeam-channel
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-01-09 07:16:08 +01:00
dependabot[bot]
975e881d83 Bump clap from 4.4.11 to 4.4.14
Bumps [clap](https://github.com/clap-rs/clap) from 4.4.11 to 4.4.14.
- [Release notes](https://github.com/clap-rs/clap/releases)
- [Changelog](https://github.com/clap-rs/clap/blob/master/CHANGELOG.md)
- [Commits](https://github.com/clap-rs/clap/compare/v4.4.11...v4.4.14)

---
updated-dependencies:
- dependency-name: clap
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-01-09 07:15:43 +01:00
dependabot[bot]
307d38ccc9 Bump anyhow from 1.0.77 to 1.0.78
Bumps [anyhow](https://github.com/dtolnay/anyhow) from 1.0.77 to 1.0.78.
- [Release notes](https://github.com/dtolnay/anyhow/releases)
- [Commits](https://github.com/dtolnay/anyhow/compare/1.0.77...1.0.78)

---
updated-dependencies:
- dependency-name: anyhow
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-01-01 10:59:58 +01:00
dependabot[bot]
32d0a5c516 Bump anyhow from 1.0.76 to 1.0.77
Bumps [anyhow](https://github.com/dtolnay/anyhow) from 1.0.76 to 1.0.77.
- [Release notes](https://github.com/dtolnay/anyhow/releases)
- [Commits](https://github.com/dtolnay/anyhow/compare/1.0.76...1.0.77)

---
updated-dependencies:
- dependency-name: anyhow
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-12-27 08:38:05 +01:00
dependabot[bot]
d9ccd9e5d3 Bump openssl-sys from 0.9.97 to 0.9.98
Bumps [openssl-sys](https://github.com/sfackler/rust-openssl) from 0.9.97 to 0.9.98.
- [Release notes](https://github.com/sfackler/rust-openssl/releases)
- [Commits](https://github.com/sfackler/rust-openssl/compare/openssl-sys-v0.9.97...openssl-sys-v0.9.98)

---
updated-dependencies:
- dependency-name: openssl-sys
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-12-25 15:23:06 +01:00
dependabot[bot]
8940c472ca Bump crossbeam-channel from 0.5.9 to 0.5.10
Bumps [crossbeam-channel](https://github.com/crossbeam-rs/crossbeam) from 0.5.9 to 0.5.10.
- [Release notes](https://github.com/crossbeam-rs/crossbeam/releases)
- [Changelog](https://github.com/crossbeam-rs/crossbeam/blob/master/CHANGELOG.md)
- [Commits](https://github.com/crossbeam-rs/crossbeam/compare/crossbeam-channel-0.5.9...crossbeam-channel-0.5.10)

---
updated-dependencies:
- dependency-name: crossbeam-channel
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-12-25 15:22:34 +01:00
extrawurst
99c705eaa7 fix stashing tab empty (closes #1986) 2023-12-22 17:22:55 +01:00
dependabot[bot]
55d316fad9 Bump anyhow from 1.0.75 to 1.0.76
Bumps [anyhow](https://github.com/dtolnay/anyhow) from 1.0.75 to 1.0.76.
- [Release notes](https://github.com/dtolnay/anyhow/releases)
- [Commits](https://github.com/dtolnay/anyhow/compare/1.0.75...1.0.76)

---
updated-dependencies:
- dependency-name: anyhow
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-12-21 09:42:53 +01:00
extrawurst
e7c61ffc89
Support prepare commit hook (#1978) 2023-12-16 00:07:54 +01:00
dependabot[bot]
7b7c5c4131 Bump crossbeam-channel from 0.5.8 to 0.5.9
Bumps [crossbeam-channel](https://github.com/crossbeam-rs/crossbeam) from 0.5.8 to 0.5.9.
- [Release notes](https://github.com/crossbeam-rs/crossbeam/releases)
- [Changelog](https://github.com/crossbeam-rs/crossbeam/blob/master/CHANGELOG.md)
- [Commits](https://github.com/crossbeam-rs/crossbeam/compare/crossbeam-channel-0.5.8...crossbeam-channel-0.5.9)

---
updated-dependencies:
- dependency-name: crossbeam-channel
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-12-14 08:45:24 +01:00
extrawurst
521ab91309
git2-hooks: allows customizing what places to look for hooks (#1975)
* allows customizing what places to look for hooks
2023-12-08 14:33:22 +01:00
dependabot[bot]
fd400cfcc7 Bump once_cell from 1.18.0 to 1.19.0
Bumps [once_cell](https://github.com/matklad/once_cell) from 1.18.0 to 1.19.0.
- [Changelog](https://github.com/matklad/once_cell/blob/master/CHANGELOG.md)
- [Commits](https://github.com/matklad/once_cell/compare/v1.18.0...v1.19.0)

---
updated-dependencies:
- dependency-name: once_cell
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-12-08 09:13:36 +01:00
extrawurst
a6416b914d
Cleanup hooks (#1972)
* cleanup errors
* cleaner repo structure
* added docs
2023-12-07 17:22:07 +01:00
extrawurst
d4dd58f6ca
move git hooks support into separate crate (#1971)
* unique error type name
* git2 dependency future 
* return hook out/err separately
2023-12-07 16:28:52 +01:00
dependabot[bot]
fabed3238b Bump openssl-sys from 0.9.96 to 0.9.97
Bumps [openssl-sys](https://github.com/sfackler/rust-openssl) from 0.9.96 to 0.9.97.
- [Release notes](https://github.com/sfackler/rust-openssl/releases)
- [Commits](https://github.com/sfackler/rust-openssl/compare/openssl-sys-v0.9.96...openssl-sys-v0.9.97)

---
updated-dependencies:
- dependency-name: openssl-sys
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-12-05 09:26:04 +01:00
dependabot[bot]
53834f2415 Bump clap from 4.4.10 to 4.4.11
Bumps [clap](https://github.com/clap-rs/clap) from 4.4.10 to 4.4.11.
- [Release notes](https://github.com/clap-rs/clap/releases)
- [Changelog](https://github.com/clap-rs/clap/blob/master/CHANGELOG.md)
- [Commits](https://github.com/clap-rs/clap/compare/v4.4.10...v4.4.11)

---
updated-dependencies:
- dependency-name: clap
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-12-05 09:25:43 +01:00
extrawurst
83df6ddbc8 test that proved hook has access to PATH (#1967) 2023-12-02 13:21:33 +01:00
dependabot[bot]
91242e574a Bump clap from 4.4.9 to 4.4.10
Bumps [clap](https://github.com/clap-rs/clap) from 4.4.9 to 4.4.10.
- [Release notes](https://github.com/clap-rs/clap/releases)
- [Changelog](https://github.com/clap-rs/clap/blob/master/CHANGELOG.md)
- [Commits](https://github.com/clap-rs/clap/compare/v4.4.9...v4.4.10)

---
updated-dependencies:
- dependency-name: clap
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-11-29 09:02:30 +01:00
extrawurst
c39eb93736 fix ci 2023-11-28 09:24:58 +01:00
dependabot[bot]
408e2caca4 Bump clap from 4.4.8 to 4.4.9
Bumps [clap](https://github.com/clap-rs/clap) from 4.4.8 to 4.4.9.
- [Release notes](https://github.com/clap-rs/clap/releases)
- [Changelog](https://github.com/clap-rs/clap/blob/master/CHANGELOG.md)
- [Commits](https://github.com/clap-rs/clap/compare/v4.4.8...v4.4.9)

---
updated-dependencies:
- dependency-name: clap
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-11-28 08:09:43 +01:00
extrawurst
bcf9bac934 simplify implementation 2023-11-27 16:21:04 +01:00
dependabot[bot]
d576405223 Bump url from 2.4.1 to 2.5.0
Bumps [url](https://github.com/servo/rust-url) from 2.4.1 to 2.5.0.
- [Release notes](https://github.com/servo/rust-url/releases)
- [Commits](https://github.com/servo/rust-url/compare/v2.4.1...v2.5.0)

---
updated-dependencies:
- dependency-name: url
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-11-23 09:27:05 +01:00
dependabot[bot]
77d1660066 Bump openssl-sys from 0.9.95 to 0.9.96
Bumps [openssl-sys](https://github.com/sfackler/rust-openssl) from 0.9.95 to 0.9.96.
- [Release notes](https://github.com/sfackler/rust-openssl/releases)
- [Commits](https://github.com/sfackler/rust-openssl/compare/openssl-sys-v0.9.95...openssl-sys-v0.9.96)

---
updated-dependencies:
- dependency-name: openssl-sys
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-11-23 09:26:30 +01:00
Felix Yan
4a0930ae02 Update Arch Linux package URL in README.md 2023-11-21 10:46:20 +01:00
dependabot[bot]
25cdc198d2 Bump itertools from 0.11.0 to 0.12.0
Bumps [itertools](https://github.com/rust-itertools/itertools) from 0.11.0 to 0.12.0.
- [Changelog](https://github.com/rust-itertools/itertools/blob/master/CHANGELOG.md)
- [Commits](https://github.com/rust-itertools/itertools/compare/v0.11.0...v0.12.0)

---
updated-dependencies:
- dependency-name: itertools
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-11-15 09:07:18 +01:00
dependabot[bot]
9850d02f7d Bump env_logger from 0.10.0 to 0.10.1
Bumps [env_logger](https://github.com/rust-cli/env_logger) from 0.10.0 to 0.10.1.
- [Release notes](https://github.com/rust-cli/env_logger/releases)
- [Changelog](https://github.com/rust-cli/env_logger/blob/main/CHANGELOG.md)
- [Commits](https://github.com/rust-cli/env_logger/compare/v0.10.0...v0.10.1)

---
updated-dependencies:
- dependency-name: env_logger
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-11-14 16:34:30 +01:00
dependabot[bot]
6d91a43004 Bump clap from 4.4.7 to 4.4.8
Bumps [clap](https://github.com/clap-rs/clap) from 4.4.7 to 4.4.8.
- [Release notes](https://github.com/clap-rs/clap/releases)
- [Changelog](https://github.com/clap-rs/clap/blob/master/CHANGELOG.md)
- [Commits](https://github.com/clap-rs/clap/compare/v4.4.7...v4.4.8)

---
updated-dependencies:
- dependency-name: clap
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-11-14 16:34:06 +01:00
dependabot[bot]
e20cf918ed Bump openssl-sys from 0.9.94 to 0.9.95
Bumps [openssl-sys](https://github.com/sfackler/rust-openssl) from 0.9.94 to 0.9.95.
- [Release notes](https://github.com/sfackler/rust-openssl/releases)
- [Commits](https://github.com/sfackler/rust-openssl/compare/openssl-sys-v0.9.94...openssl-sys-v0.9.95)

---
updated-dependencies:
- dependency-name: openssl-sys
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-11-06 16:46:20 +01:00
extrawurst
985acf23ef fix nighyly clippy 2023-11-02 07:50:41 +01:00
dependabot[bot]
b051b3c00c Bump openssl-sys from 0.9.93 to 0.9.94
Bumps [openssl-sys](https://github.com/sfackler/rust-openssl) from 0.9.93 to 0.9.94.
- [Release notes](https://github.com/sfackler/rust-openssl/releases)
- [Commits](https://github.com/sfackler/rust-openssl/compare/openssl-sys-v0.9.93...openssl-sys-v0.9.94)

---
updated-dependencies:
- dependency-name: openssl-sys
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-11-02 07:39:10 +01:00
dependabot[bot]
187fbbdcf5 Bump clap from 4.4.6 to 4.4.7
Bumps [clap](https://github.com/clap-rs/clap) from 4.4.6 to 4.4.7.
- [Release notes](https://github.com/clap-rs/clap/releases)
- [Changelog](https://github.com/clap-rs/clap/blob/master/CHANGELOG.md)
- [Commits](https://github.com/clap-rs/clap/compare/v4.4.6...v4.4.7)

---
updated-dependencies:
- dependency-name: clap
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-10-25 09:51:20 +02:00
Shun Sakai
501833cd90 Update README
Update the installation instructions on openSUSE.
2023-10-19 14:17:53 +02:00
dependabot[bot]
91ad02c105 Bump rustix from 0.36.8 to 0.36.16
Bumps [rustix](https://github.com/bytecodealliance/rustix) from 0.36.8 to 0.36.16.
- [Release notes](https://github.com/bytecodealliance/rustix/releases)
- [Commits](https://github.com/bytecodealliance/rustix/compare/v0.36.8...v0.36.16)

---
updated-dependencies:
- dependency-name: rustix
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-10-19 14:06:14 +02:00
extrawurst
d285440f2b Revert "Bump indexmap from 1.9.3 to 2.0.1"
This reverts commit 81a69f845e.
2023-10-17 08:43:41 +02:00
extrawurst
4abe5edf06 Revert "Bump indexmap from 2.0.1 to 2.0.2"
This reverts commit b7772e63e4.
2023-10-17 08:42:36 +02:00
Adrian Wannenmacher
2be0e73d5b
Prevent unsigned tagging (#1915)
* prevent creation of tags when tag-signing is configured

Co-authored-by: extrawurst <776816+extrawurst@users.noreply.github.com>
2023-10-17 08:40:20 +02:00
Sainath Singineedi
0e2b3db1d9
Add confirmation dialog for undo commit (#1909) 2023-10-17 07:59:59 +02:00
Maurice Wangleng Tan
2fd957e2c8
Allow customizing line break visualization (#1904) 2023-10-16 18:00:48 +02:00
Aditya Pillai
09907f3873 fix typo with sign off commit 2023-10-16 17:45:40 +02:00
dependabot[bot]
b7772e63e4 Bump indexmap from 2.0.1 to 2.0.2
Bumps [indexmap](https://github.com/bluss/indexmap) from 2.0.1 to 2.0.2.
- [Changelog](https://github.com/bluss/indexmap/blob/master/RELEASES.md)
- [Commits](https://github.com/bluss/indexmap/compare/2.0.1...2.0.2)

---
updated-dependencies:
- dependency-name: indexmap
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-10-02 08:40:36 +02:00
dependabot[bot]
579f5e1e37 Bump clap from 4.4.5 to 4.4.6
Bumps [clap](https://github.com/clap-rs/clap) from 4.4.5 to 4.4.6.
- [Release notes](https://github.com/clap-rs/clap/releases)
- [Changelog](https://github.com/clap-rs/clap/blob/master/CHANGELOG.md)
- [Commits](https://github.com/clap-rs/clap/compare/v4.4.5...v4.4.6)

---
updated-dependencies:
- dependency-name: clap
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-09-29 11:33:37 +02:00
dependabot[bot]
81a69f845e Bump indexmap from 1.9.3 to 2.0.1
Bumps [indexmap](https://github.com/bluss/indexmap) from 1.9.3 to 2.0.1.
- [Changelog](https://github.com/bluss/indexmap/blob/master/RELEASES.md)
- [Commits](https://github.com/bluss/indexmap/commits)

---
updated-dependencies:
- dependency-name: indexmap
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-09-28 09:33:45 +02:00
dependabot[bot]
9bdd5cc856 Bump clap from 4.4.4 to 4.4.5
Bumps [clap](https://github.com/clap-rs/clap) from 4.4.4 to 4.4.5.
- [Release notes](https://github.com/clap-rs/clap/releases)
- [Changelog](https://github.com/clap-rs/clap/blob/master/CHANGELOG.md)
- [Commits](https://github.com/clap-rs/clap/compare/v4.4.4...v4.4.5)

---
updated-dependencies:
- dependency-name: clap
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-09-27 10:17:37 +02:00
dependabot[bot]
0bd5b9958e Bump rayon from 1.7.0 to 1.8.0
Bumps [rayon](https://github.com/rayon-rs/rayon) from 1.7.0 to 1.8.0.
- [Changelog](https://github.com/rayon-rs/rayon/blob/master/RELEASES.md)
- [Commits](https://github.com/rayon-rs/rayon/compare/rayon-core-v1.7.0...rayon-core-v1.8.0)

---
updated-dependencies:
- dependency-name: rayon
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-09-21 12:34:32 +02:00
dependabot[bot]
52cb83f714 Bump unicode-width from 0.1.10 to 0.1.11
Bumps [unicode-width](https://github.com/unicode-rs/unicode-width) from 0.1.10 to 0.1.11.
- [Commits](https://github.com/unicode-rs/unicode-width/compare/v0.1.10...v0.1.11)

---
updated-dependencies:
- dependency-name: unicode-width
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-09-20 11:39:36 +02:00
dependabot[bot]
ca15fcbe4c Bump gh-emoji from 1.0.7 to 1.0.8
Bumps [gh-emoji](https://github.com/kornelski/gh-emoji) from 1.0.7 to 1.0.8.
- [Commits](https://github.com/kornelski/gh-emoji/commits)

---
updated-dependencies:
- dependency-name: gh-emoji
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-09-20 11:37:36 +02:00
extrawurst
b2e7420349
Update README.md 2023-09-19 19:52:30 +02:00
dependabot[bot]
9bd483fea5 Bump clap from 4.4.3 to 4.4.4
Bumps [clap](https://github.com/clap-rs/clap) from 4.4.3 to 4.4.4.
- [Release notes](https://github.com/clap-rs/clap/releases)
- [Changelog](https://github.com/clap-rs/clap/blob/master/CHANGELOG.md)
- [Commits](https://github.com/clap-rs/clap/compare/v4.4.3...v4.4.4)

---
updated-dependencies:
- dependency-name: clap
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-09-19 19:44:40 +02:00
Charles Johnson
bd96c85f5a Update FAQ.md 2023-09-19 19:44:19 +02:00
dependabot[bot]
4eea01a9fe Bump chrono from 0.4.30 to 0.4.31
Bumps [chrono](https://github.com/chronotope/chrono) from 0.4.30 to 0.4.31.
- [Release notes](https://github.com/chronotope/chrono/releases)
- [Changelog](https://github.com/chronotope/chrono/blob/main/CHANGELOG.md)
- [Commits](https://github.com/chronotope/chrono/compare/v0.4.30...v0.4.31)

---
updated-dependencies:
- dependency-name: chrono
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-09-18 08:07:45 +02:00
dependabot[bot]
8775fec9fa Bump clap from 4.4.2 to 4.4.3
Bumps [clap](https://github.com/clap-rs/clap) from 4.4.2 to 4.4.3.
- [Release notes](https://github.com/clap-rs/clap/releases)
- [Changelog](https://github.com/clap-rs/clap/blob/master/CHANGELOG.md)
- [Commits](https://github.com/clap-rs/clap/compare/v4.4.2...v4.4.3)

---
updated-dependencies:
- dependency-name: clap
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-09-13 14:38:55 +02:00
Christoph Rüßler
aa7aa7a5c1
Fix file history for all sizes (#1738) 2023-09-09 11:29:04 +02:00
extrawurst
d0f15d54ab fix github action warnings using set-output
see https://github.blog/changelog/2022-10-11-github-actions-deprecating-save-state-and-set-output-commands/
2023-09-09 11:20:19 +02:00
extrawurst
8768611876 prepare release 2023-09-09 10:59:21 +02:00
extrawurst
015d409540 cleanup according to toolchain action docs 2023-09-08 23:22:30 +02:00
dependabot[bot]
5b81d72014 Bump chrono from 0.4.29 to 0.4.30
Bumps [chrono](https://github.com/chronotope/chrono) from 0.4.29 to 0.4.30.
- [Release notes](https://github.com/chronotope/chrono/releases)
- [Changelog](https://github.com/chronotope/chrono/blob/main/CHANGELOG.md)
- [Commits](https://github.com/chronotope/chrono/compare/v0.4.29...v0.4.30)

---
updated-dependencies:
- dependency-name: chrono
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-09-08 08:37:44 +02:00
dependabot[bot]
6bda5c1f21 Bump chrono from 0.4.28 to 0.4.29
Bumps [chrono](https://github.com/chronotope/chrono) from 0.4.28 to 0.4.29.
- [Release notes](https://github.com/chronotope/chrono/releases)
- [Changelog](https://github.com/chronotope/chrono/blob/main/CHANGELOG.md)
- [Commits](https://github.com/chronotope/chrono/compare/v0.4.28...v0.4.29)

---
updated-dependencies:
- dependency-name: chrono
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-09-06 09:40:34 +02:00
extrawurst
7c0d010233 make commit lookup in log faster
* makes hopping to next highlighted commit loopfree (closes #1876)
* makes general commit find faster
2023-09-05 09:07:21 +02:00
dependabot[bot]
bf2c1e3295 Bump openssl-sys from 0.9.92 to 0.9.93
Bumps [openssl-sys](https://github.com/sfackler/rust-openssl) from 0.9.92 to 0.9.93.
- [Release notes](https://github.com/sfackler/rust-openssl/releases)
- [Commits](https://github.com/sfackler/rust-openssl/compare/openssl-sys-v0.9.92...openssl-sys-v0.9.93)

---
updated-dependencies:
- dependency-name: openssl-sys
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-09-05 07:53:28 +02:00
extrawurst
253a18f62a search message body/summary separately
closes #1875
2023-09-04 22:24:20 +02:00
extrawurst
0e1d83fb02
Parallelize log search (#1874) 2023-09-04 20:55:17 +02:00
extrawurst
5808515853 fix warnings 2023-09-04 09:48:37 +02:00
extrawurst
ecb793206d seems useless, lets remove 2023-09-04 09:46:08 +02:00
extrawurst
19051cfec0 update chrono 2023-09-04 09:40:29 +02:00
extrawurst
0a8cd66bbd update clap 2023-09-04 09:38:55 +02:00
extrawurst
11b9e8193c prepare for release 2023-09-03 18:52:33 +02:00
extrawurst
6356f64b1d fix #1866 2023-09-03 18:41:00 +02:00
extrawurst
42043bda6f make update_progress simpler 2023-09-03 18:34:09 +02:00
extrawurst
52dfefe624 Revert "parallelize log search"
This reverts commit ebe41e8a75.
2023-09-03 18:30:34 +02:00
extrawurst
ebe41e8a75 parallelize log search
* will consume all cores now and got faster in all my benchmarks
* setting progress via asyncjob now makes sure to only set it if it has changed and return whether that is the case to simplify sending progress notifications only in case progress actually changed
2023-08-31 12:09:13 +02:00
extrawurst
5be397b335
stash list does not update after pop/drop (#1865)
* move to stashlist after stashing
* move to status after stash popping
2023-08-31 10:41:52 +02:00
extrawurst
16c97edb4d fix log not updating after branch switch
closes #1862
2023-08-31 10:02:59 +02:00
extrawurst
082e308efb fix manual brew pipeline 2023-08-30 16:49:22 +02:00
extrawurst
bc1cea3dc8 new homebrew formular bumping ci action 2023-08-30 16:37:18 +02:00
extrawurst
4fd056c38b fix homebrew action problem 2023-08-30 16:23:35 +02:00
extrawurst
85ab9d47ad prepare release 2023-08-30 15:55:26 +02:00
extrawurst
f2f51cde82 package status badge 2023-08-30 15:53:05 +02:00
extrawurst
bd139ccc59 fix broken reloading of revlog on new search results 2023-08-30 11:54:30 +02:00
extrawurst
e0bc51822f some cleanup 2023-08-30 09:57:25 +02:00
Tony
ea9314e242
Speedup CI (#1852) 2023-08-30 09:53:20 +02:00
extrawurst
0c591dfea4
Fix branch switch with slash (#1857) 2023-08-29 16:39:58 +02:00
extrawurst
c38b1d1e1c do not fetch commit_info if batch is the same 2023-08-29 13:40:28 +02:00
dependabot[bot]
f639f4a0d0 Bump url from 2.4.0 to 2.4.1
Bumps [url](https://github.com/servo/rust-url) from 2.4.0 to 2.4.1.
- [Release notes](https://github.com/servo/rust-url/releases)
- [Commits](https://github.com/servo/rust-url/compare/v2.4.0...v2.4.1)

---
updated-dependencies:
- dependency-name: url
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-08-29 07:36:22 +02:00
extrawurst
b36ffc9481 added changelog 2023-08-28 12:40:54 +02:00
extrawurst
0376ed3a26
Fix slowness in big repo revlog tab (#1849) 2023-08-28 12:35:01 +02:00
dependabot[bot]
8c177151d1 Bump openssl-sys from 0.9.91 to 0.9.92
Bumps [openssl-sys](https://github.com/sfackler/rust-openssl) from 0.9.91 to 0.9.92.
- [Release notes](https://github.com/sfackler/rust-openssl/releases)
- [Commits](https://github.com/sfackler/rust-openssl/compare/openssl-sys-v0.9.91...openssl-sys-v0.9.92)

---
updated-dependencies:
- dependency-name: openssl-sys
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-08-28 08:43:19 +02:00
extrawurst
15e9222f51
refactoring commit filter (#1843) 2023-08-27 16:38:20 +02:00
extrawurst
86c4f7ff1c use new CD 2023-08-27 16:36:29 +02:00
extrawurst
7eaa04169f fix changelog times 2023-08-27 16:12:28 +02:00
extrawurst
6ce3485cf0 version bump 2023-08-27 15:31:53 +02:00
extrawurst
33ac5b97ae prep for release 2023-08-27 15:30:15 +02:00
extrawurst
9a7c2199a7
make commit filtering an async job (#1842) 2023-08-27 15:14:10 +02:00
extrawurst
2675934027
Index of search result (#1840) 2023-08-27 11:25:16 +02:00
Ammar Abou Zor
c68fa3e87b
Jump to commit via sha (#1818) 2023-08-27 09:46:41 +02:00
extrawurst
005047f015 cleanup state in search popup more 2023-08-26 23:55:51 +02:00
extrawurst
0fdec134c5
Fix: search in log (#1838) 2023-08-26 20:34:37 +02:00
extrawurst
5b2b8c7e0a cleanup and improvements
inspired by #1411
2023-08-26 14:37:15 +02:00
Ammar Abou Zor
6339a1f33c
Copy full Commit Hash by default (#1836) 2023-08-26 14:26:51 +02:00
dependabot[bot]
c84a973d5d Bump bytesize from 1.2.0 to 1.3.0
Bumps [bytesize](https://github.com/hyunsik/bytesize) from 1.2.0 to 1.3.0.
- [Release notes](https://github.com/hyunsik/bytesize/releases)
- [Commits](https://github.com/hyunsik/bytesize/compare/v1.2.0...v1.3.0)

---
updated-dependencies:
- dependency-name: bytesize
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-08-26 13:54:24 +02:00
extrawurst
11c65f633f fix new fmt check 2023-08-26 13:52:47 +02:00
extrawurst
2377924f9c use popup specific up/down cmds (fixes #1831) 2023-08-26 13:45:15 +02:00
extrawurst
7558d25e25 cleanup 2023-08-23 19:40:24 +02:00
extrawurst
a5b898c718 fix potential panic 2023-08-22 13:19:17 +02:00
extrawurst
9e69f251a8 do not show spacing selection in non-highlight 2023-08-22 13:19:06 +02:00
extrawurst
30211b2a6b allow visualizing enable state in text
this allows us to show whether the text inpout is currently selected in search popup
2023-08-22 13:01:50 +02:00
Ammar Abou Zor
306ff91afa
Search popup: allow navigation with non arrow keys (#1816) 2023-08-22 12:47:18 +02:00
extrawurst
ab51490648 cleanup 2023-08-21 13:23:52 +02:00
extrawurst
b4a77c9262 update readme 2023-08-21 13:14:42 +02:00
extrawurst
77bb69a162 fix ci 2023-08-21 12:54:56 +02:00
extrawurst
0cf041bb98 fix backgrounding search thread 2023-08-21 12:19:07 +02:00
extrawurst
cde1c7f883
Search commits by author (#1822) 2023-08-21 12:11:53 +02:00
extrawurst
d253022f13 add missing command for new commit search in log 2023-08-21 11:29:02 +02:00
ImgBotApp
4a6131fcd9 [ImgBot] Optimize images
/assets/diff-empty-line.png -- 219.77kb -> 135.70kb (38.26%)

Signed-off-by: ImgBotApp <ImgBotHelp@gmail.com>
2023-08-19 01:54:41 +02:00
extrawurst
3c5131ad27
commit log filtering (#1800) 2023-08-18 17:19:18 +02:00
extrawurst
b50d44a4b8 fix changelog 2023-08-18 14:32:44 +02:00
lightsnowball
514e8f0175
Modify checkout implementation making branch switching more similar to CLI git behaviour (#1809) 2023-08-18 14:31:51 +02:00
extrawurst
074820e63b fix: missing entry in ToC 2023-08-18 14:23:50 +02:00
dependabot[bot]
ecf6fe9369 Bump anyhow from 1.0.74 to 1.0.75
Bumps [anyhow](https://github.com/dtolnay/anyhow) from 1.0.74 to 1.0.75.
- [Release notes](https://github.com/dtolnay/anyhow/releases)
- [Commits](https://github.com/dtolnay/anyhow/compare/1.0.74...1.0.75)

---
updated-dependencies:
- dependency-name: anyhow
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-08-17 09:55:39 +02:00
dependabot[bot]
77d1ef6874 Bump anyhow from 1.0.72 to 1.0.74
Bumps [anyhow](https://github.com/dtolnay/anyhow) from 1.0.72 to 1.0.74.
- [Release notes](https://github.com/dtolnay/anyhow/releases)
- [Commits](https://github.com/dtolnay/anyhow/compare/1.0.72...1.0.74)

---
updated-dependencies:
- dependency-name: anyhow
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-08-16 12:47:05 +02:00
extrawurst
809281f1ab clippy fix 2023-08-16 12:43:41 +02:00
dependabot[bot]
347e6e43e7 Bump log from 0.4.19 to 0.4.20
Bumps [log](https://github.com/rust-lang/log) from 0.4.19 to 0.4.20.
- [Release notes](https://github.com/rust-lang/log/releases)
- [Changelog](https://github.com/rust-lang/log/blob/master/CHANGELOG.md)
- [Commits](https://github.com/rust-lang/log/compare/0.4.19...0.4.20)

---
updated-dependencies:
- dependency-name: log
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-08-14 09:17:22 +02:00
extrawurst
8437b76f99 fix nightly clippy 2023-08-12 18:50:19 +02:00
extrawurst
798f990cd3 changelog and notes for #1506 2023-08-12 18:45:16 +02:00
Kieran Siek
bf31f20657
Fix external editor delay. (#1579)
The default polling rate of 1 second causes a 1 second delay when
queuing the event to launch the external editor, causing latency.

However, a slower polling helps reduce CPU usage, so let's
have a short polling duration as long as there are input events, and
slow poll otherwise.

Since the external editor among other components (not tested) is always
launched in response to an input event, we reduce the latency to ~100ms,
which is the fast poll duration.

Co-authored-by: extrawurst <776816+extrawurst@users.noreply.github.com>
2023-08-12 18:28:59 +02:00
extrawurst
6ec647710d visualize empty line in diff better (closes #1359) 2023-08-11 16:44:50 +02:00
extrawurst
9eb8d470a4 optimize logfilter to not check stashes
logfilter using `get_commit_diff` on each entry lead to a lot of unneeded calls to `get_stashes` and `is_stash_commit` which should be not even needed for file history log entries. this is not happening now anymore
2023-08-11 11:38:21 +02:00
Niklas Dießner
e90656d61c
add anaconda install instructions (#1802) 2023-08-10 23:34:27 +02:00
extrawurst
7400d5bc68 more logging/diagnostics when repo cant be opened 2023-08-10 15:39:39 +02:00
extrawurst
53988ba4e0
fix hunk edits with non standard diff options (#1803) 2023-08-10 14:48:36 +02:00
extrawurst
495d4d5da7 do shell expansion for commit.template
more error logging around commit-template loading
2023-08-09 19:02:24 +02:00
Niklas Dießner
403c5aabd9
Switch to bwrap (#1792)
* switch from textwrap to bwrap
2023-08-09 16:00:35 +02:00
extrawurst
a828bd2cea show dependency stats on readme 2023-08-07 14:02:25 +02:00
dependabot[bot]
5aeb9c4c9c Bump openssl-sys from 0.9.90 to 0.9.91
Bumps [openssl-sys](https://github.com/sfackler/rust-openssl) from 0.9.90 to 0.9.91.
- [Release notes](https://github.com/sfackler/rust-openssl/releases)
- [Commits](https://github.com/sfackler/rust-openssl/compare/openssl-sys-v0.9.90...openssl-sys-v0.9.91)

---
updated-dependencies:
- dependency-name: openssl-sys
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-08-07 11:54:06 +02:00
extrawurst
38bf345ebf new single issue for commit filtering 2023-08-05 15:52:49 +02:00
extrawurst
9e9e743515 do not ignore pprof anymore 2023-08-01 10:30:11 +02:00
extrawurst
965f0765aa upgrade syntect and regex (see #1781) 2023-08-01 10:23:38 +02:00
extrawurst
104078f15f cargo install locked 2023-08-01 10:09:15 +02:00
extrawurst
9d37e36314 licence update 2023-07-30 11:53:34 +02:00
extrawurst
a87f66c131
switch back to dirs (#1783)
* switch back to `dirs`
2023-07-30 10:44:49 +02:00
Niklas Dießner
104e5bf62e
1751 follow symlink for keybinding config (#1767) 2023-07-29 23:04:53 +02:00
extrawurst
e661ee6e3e simplify deny config 2023-07-29 22:56:56 +02:00
domtac
dba5206e46
Feat 1757 add signoff option (#1758) 2023-07-29 22:11:36 +02:00
dependabot[bot]
77f672570f Bump pprof from 0.12.0 to 0.12.1
Bumps [pprof](https://github.com/tikv/pprof-rs) from 0.12.0 to 0.12.1.
- [Changelog](https://github.com/tikv/pprof-rs/blob/master/CHANGELOG.md)
- [Commits](https://github.com/tikv/pprof-rs/commits)

---
updated-dependencies:
- dependency-name: pprof
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-07-27 08:15:09 +02:00
extrawurst
b935df59a1 fix nightly build 2023-07-18 09:45:05 +02:00
dependabot[bot]
46a039f88f Bump scopeguard from 1.1.0 to 1.2.0
Bumps [scopeguard](https://github.com/bluss/scopeguard) from 1.1.0 to 1.2.0.
- [Commits](https://github.com/bluss/scopeguard/compare/v1.1.0...v1.2.0)

---
updated-dependencies:
- dependency-name: scopeguard
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-07-18 09:30:41 +02:00
dependabot[bot]
023f09b327 Bump anyhow from 1.0.71 to 1.0.72
Bumps [anyhow](https://github.com/dtolnay/anyhow) from 1.0.71 to 1.0.72.
- [Release notes](https://github.com/dtolnay/anyhow/releases)
- [Commits](https://github.com/dtolnay/anyhow/compare/1.0.71...1.0.72)

---
updated-dependencies:
- dependency-name: anyhow
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-07-17 09:24:11 +02:00
extrawurst
c1e209581b use udeps git in ci 2023-07-09 18:12:11 +02:00
extrawurst
bfa9f7ef01 upgrade from yanked crate 2023-07-09 17:58:25 +02:00
Ammar Abou Zor
4682a1b75b
Make fuzzy find popup scrollable (#1734)
* Added: Make fuzzy find popup scrollable
* Fuzzy_find: Add scrollbar to matches list
* Update CHANGELOG
2023-07-08 12:29:47 +02:00
extrawurst
bdba065909 cargo update
cargo update

cargo update

cargo update

cargo update

cargo update

cargo update

cargo update

cargo update

cargo update

cargo update

cargo update

cargo update

cargo update

cargo update

cargo update

cargo update

cargo update

cargo update

cargo update
2023-07-07 15:58:59 +02:00
extrawurst
cb9cf3ad0c fix nightly builds
* update proc-macro2
* fix new warnings
2023-07-07 15:46:32 +02:00
dependabot[bot]
05ce018e06 Bump pprof from 0.11.1 to 0.12.0
Bumps [pprof](https://github.com/tikv/pprof-rs) from 0.11.1 to 0.12.0.
- [Changelog](https://github.com/tikv/pprof-rs/blob/master/CHANGELOG.md)
- [Commits](https://github.com/tikv/pprof-rs/commits)

---
updated-dependencies:
- dependency-name: pprof
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-07-07 15:16:13 +02:00
dependabot[bot]
c2435c2752 Bump pretty_assertions from 1.3.0 to 1.4.0
Bumps [pretty_assertions](https://github.com/rust-pretty-assertions/rust-pretty-assertions) from 1.3.0 to 1.4.0.
- [Release notes](https://github.com/rust-pretty-assertions/rust-pretty-assertions/releases)
- [Changelog](https://github.com/rust-pretty-assertions/rust-pretty-assertions/blob/main/CHANGELOG.md)
- [Commits](https://github.com/rust-pretty-assertions/rust-pretty-assertions/compare/v1.3.0...v1.4.0)

---
updated-dependencies:
- dependency-name: pretty_assertions
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-07-07 11:11:00 +02:00
UG
b4450f9bc3
fixed wrong fuzzy-find highlight in long str (#1731)
* fixed wrong highlight in long str
* support multibyte characters
2023-07-06 21:15:08 +02:00
extrawurst
5c98e2fe76 print theme file path in log with mesages 2023-06-25 14:14:57 +02:00
extrawurst
c119e6ae18 fix changelog 2023-06-25 14:11:31 +02:00
Christoph Rüßler
3c9c266c01
Simplify theme overrides (#1652)
* Simplify theme overrides

Theme overrides are now loaded the same way key overrides are loaded.
The config file, `theme.ron`, does not have to contain a complete theme
anymore. Instead, it is possible to specify only the values that are
supposed to override their corresponding default values.

* Document breaking change in changelog
* Test that override differs from default
* Convert existing theme to patch
2023-06-25 14:09:40 +02:00
dependabot[bot]
999912c347 Bump itertools from 0.10.5 to 0.11.0
Bumps [itertools](https://github.com/rust-itertools/itertools) from 0.10.5 to 0.11.0.
- [Changelog](https://github.com/rust-itertools/itertools/blob/master/CHANGELOG.md)
- [Commits](https://github.com/rust-itertools/itertools/compare/v0.10.5...v0.11.0)

---
updated-dependencies:
- dependency-name: itertools
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-06-25 10:49:48 +02:00
dependabot[bot]
7ebb3c0ef6 Bump openssl-sys from 0.9.88 to 0.9.90
Bumps [openssl-sys](https://github.com/sfackler/rust-openssl) from 0.9.88 to 0.9.90.
- [Release notes](https://github.com/sfackler/rust-openssl/releases)
- [Commits](https://github.com/sfackler/rust-openssl/compare/openssl-sys-v0.9.88...openssl-sys-v0.9.90)

---
updated-dependencies:
- dependency-name: openssl-sys
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-06-21 09:38:48 +02:00
extrawurst
022b389146 fix char_count with utf8 chars (fixes #1726) 2023-06-20 18:04:27 +02:00
UG
e90e8dc536
FuzzyFindPopup for general use (#1672)
* replace BranchFindPopup with FuzzyFindPopup
* replace FileFindPopup with FuzzyFindPopup
2023-06-20 12:57:36 +02:00
extrawurst
be801a336f fix changelog 2023-06-19 17:53:04 +02:00
hamflx
bfcf33fce4
feat: support 'n'/'p' key to move to the next/prev hunk. (#1723)
* feat: support 'n'/'p' key to move to the next/prev hunk.
* feat: auto scroll next/prev hunk into visible area.
* add unittest for VerticalScroll::move_area_to_visible.
2023-06-19 17:52:05 +02:00
extrawurst
197fc6fdf1 bump gitui version 2023-06-19 16:00:54 +02:00
extrawurst
40cd72abfc prepare for release 2023-06-19 16:00:01 +02:00
extrawurst
0ca82aba46 bump versions 2023-06-19 15:58:19 +02:00
kamillo
58e72cd22b
Add support for options handling in log and stashes views #1661 (#1675) 2023-06-19 15:40:26 +02:00
extrawurst
4f3be697d5 upgrade ratatui 2023-06-19 15:05:17 +02:00
dependabot[bot]
195a6f2c90 Bump log from 0.4.18 to 0.4.19
Bumps [log](https://github.com/rust-lang/log) from 0.4.18 to 0.4.19.
- [Release notes](https://github.com/rust-lang/log/releases)
- [Changelog](https://github.com/rust-lang/log/blob/master/CHANGELOG.md)
- [Commits](https://github.com/rust-lang/log/compare/0.4.18...0.4.19)

---
updated-dependencies:
- dependency-name: log
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-06-19 13:49:04 +02:00
dependabot[bot]
ff48840f23 Bump openssl-sys from 0.9.87 to 0.9.88
Bumps [openssl-sys](https://github.com/sfackler/rust-openssl) from 0.9.87 to 0.9.88.
- [Release notes](https://github.com/sfackler/rust-openssl/releases)
- [Commits](https://github.com/sfackler/rust-openssl/compare/openssl-sys-v0.9.87...openssl-sys-v0.9.88)

---
updated-dependencies:
- dependency-name: openssl-sys
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-06-08 14:42:17 +02:00
dependabot[bot]
4f10dfb185 Bump url from 2.3.1 to 2.4.0
Bumps [url](https://github.com/servo/rust-url) from 2.3.1 to 2.4.0.
- [Release notes](https://github.com/servo/rust-url/releases)
- [Commits](https://github.com/servo/rust-url/compare/v2.3.1...v2.4.0)

---
updated-dependencies:
- dependency-name: url
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-06-08 14:41:59 +02:00
extrawurst
7d5b7d2c38 clippy nightly fixes 2023-06-07 12:50:27 +02:00
dependabot[bot]
022281d414 Bump log from 0.4.17 to 0.4.18
Bumps [log](https://github.com/rust-lang/log) from 0.4.17 to 0.4.18.
- [Release notes](https://github.com/rust-lang/log/releases)
- [Changelog](https://github.com/rust-lang/log/blob/master/CHANGELOG.md)
- [Commits](https://github.com/rust-lang/log/compare/0.4.17...0.4.18)

---
updated-dependencies:
- dependency-name: log
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-06-07 12:38:46 +02:00
dependabot[bot]
bc99a92ec8 Bump git2 from 0.17.1 to 0.17.2
Bumps [git2](https://github.com/rust-lang/git2-rs) from 0.17.1 to 0.17.2.
- [Changelog](https://github.com/rust-lang/git2-rs/blob/master/CHANGELOG.md)
- [Commits](https://github.com/rust-lang/git2-rs/compare/0.17.1...0.17.2)

---
updated-dependencies:
- dependency-name: git2
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-06-07 12:38:28 +02:00
dependabot[bot]
1a96855c63 Bump once_cell from 1.17.1 to 1.18.0
Bumps [once_cell](https://github.com/matklad/once_cell) from 1.17.1 to 1.18.0.
- [Changelog](https://github.com/matklad/once_cell/blob/master/CHANGELOG.md)
- [Commits](https://github.com/matklad/once_cell/compare/v1.17.1...v1.18.0)

---
updated-dependencies:
- dependency-name: once_cell
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-06-07 12:37:32 +02:00
dependabot[bot]
036cc16417 Bump chrono from 0.4.24 to 0.4.26
Bumps [chrono](https://github.com/chronotope/chrono) from 0.4.24 to 0.4.26.
- [Release notes](https://github.com/chronotope/chrono/releases)
- [Changelog](https://github.com/chronotope/chrono/blob/main/CHANGELOG.md)
- [Commits](https://github.com/chronotope/chrono/compare/v0.4.24...v0.4.26)

---
updated-dependencies:
- dependency-name: chrono
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-06-07 12:37:06 +02:00
pm100
2b3bc622d6 too many things skipped with !verify 2023-05-21 22:40:15 -05:00
pm100
cdaf3b8b84 missed readme and changelog updates 2023-05-17 08:45:12 -06:00
pm100
aa4266cd1f fix double key input on windows due to crossterm 0.26 2023-05-14 14:01:25 -06:00
pm100
d6f33532bb bump msrv to 1.65 2023-05-14 13:59:48 -06:00
pm100
8fa96fd598 add perl and python to build requirments 2023-05-14 13:49:44 -06:00
Christoph Rüßler
49cd7ea2db Fix file history for sizes <= 1200 entries 2023-05-04 15:49:21 +02:00
dependabot[bot]
9b09617f85 Bump anyhow from 1.0.70 to 1.0.71
Bumps [anyhow](https://github.com/dtolnay/anyhow) from 1.0.70 to 1.0.71.
- [Release notes](https://github.com/dtolnay/anyhow/releases)
- [Commits](https://github.com/dtolnay/anyhow/compare/1.0.70...1.0.71)

---
updated-dependencies:
- dependency-name: anyhow
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-05-02 08:44:35 +02:00
Christoph Rüßler
370aff5fcc
Default to tick-based updates (#1657)
* Default to tick-based updates

This commit reintroduces code that was previously removed in favor of a
notify-based update trigger. It turned out that notify-based updates can
cause issues in larger repositories, so tick-based updates seemed like a
safer default.

https://github.com/extrawurst/gitui/issues/1444
https://github.com/extrawurst/gitui/pull/1310

* Add FAQ entry for --watcher

* Remove --poll
2023-04-29 17:03:43 +02:00
dependabot[bot]
836e03c01c Bump openssl-sys from 0.9.86 to 0.9.87
Bumps [openssl-sys](https://github.com/sfackler/rust-openssl) from 0.9.86 to 0.9.87.
- [Release notes](https://github.com/sfackler/rust-openssl/releases)
- [Commits](https://github.com/sfackler/rust-openssl/compare/openssl-sys-v0.9.86...openssl-sys-v0.9.87)

---
updated-dependencies:
- dependency-name: openssl-sys
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-04-26 23:35:00 +02:00
extrawurst
48d42abd38 changelog entry and gif for #1350 2023-04-22 08:12:53 +02:00
extrawurst
379bf265fe fix ratatui building with optimization 2023-04-22 08:05:48 +02:00
UG
3a6f292bf5
add fuzzy finder in branch list (#1658)
* add branch_find_popup
* capital F for fetch in branchlist, f for find
* add command info of return

closes #1350
2023-04-21 23:03:35 +02:00
dependabot[bot]
a921ab543b Bump openssl-sys from 0.9.85 to 0.9.86
Bumps [openssl-sys](https://github.com/sfackler/rust-openssl) from 0.9.85 to 0.9.86.
- [Release notes](https://github.com/sfackler/rust-openssl/releases)
- [Commits](https://github.com/sfackler/rust-openssl/compare/openssl-sys-v0.9.85...openssl-sys-v0.9.86)

---
updated-dependencies:
- dependency-name: openssl-sys
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-04-21 11:16:28 +02:00
extrawurst
904885e001 tui -> ratatui
tui is not maintained anymore and it seems that ratatui is the successor: https://github.com/fdehau/tui-rs/issues/654
2023-04-19 14:46:09 +02:00
extrawurst
19b820bb35 cleanup 2023-04-19 14:30:43 +02:00
dependabot[bot]
8d6e30d2a6 Bump git2 from 0.17.0 to 0.17.1
Bumps [git2](https://github.com/rust-lang/git2-rs) from 0.17.0 to 0.17.1.
- [Release notes](https://github.com/rust-lang/git2-rs/releases)
- [Changelog](https://github.com/rust-lang/git2-rs/blob/master/CHANGELOG.md)
- [Commits](https://github.com/rust-lang/git2-rs/compare/git2-curl-0.17.0...0.17.1)

---
updated-dependencies:
- dependency-name: git2
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-04-17 10:40:45 +02:00
Christoph Rüßler
3e0ec29d0d Add entry to changelog 2023-04-12 11:53:08 +02:00
Christoph Rüßler
2554f04ace Don’t show upstream if commit is local branch head 2023-04-12 11:53:08 +02:00
Christoph Rüßler
3af256c75a Show remote branches in revlog 2023-04-12 11:53:08 +02:00
dependabot[bot]
b15d24caf8 Bump shellexpand from 3.0.0 to 3.1.0
Bumps [shellexpand](https://gitlab.com/ijackson/rust-shellexpand) from 3.0.0 to 3.1.0.
- [Release notes](https://gitlab.com/ijackson/rust-shellexpand/tags)
- [Commits](https://gitlab.com/ijackson/rust-shellexpand/compare/shellexpand-3.0.0...shellexpand-3.1.0)

---
updated-dependencies:
- dependency-name: shellexpand
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-04-12 07:59:37 +02:00
dependabot[bot]
b2d123b519 Bump clap from 4.1.8 to 4.1.14
Bumps [clap](https://github.com/clap-rs/clap) from 4.1.8 to 4.1.14.
- [Release notes](https://github.com/clap-rs/clap/releases)
- [Changelog](https://github.com/clap-rs/clap/blob/master/CHANGELOG.md)
- [Commits](https://github.com/clap-rs/clap/compare/v4.1.8...v4.1.14)

---
updated-dependencies:
- dependency-name: clap
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-04-12 07:59:17 +02:00
dependabot[bot]
060ea8f0c6 Bump openssl-sys from 0.9.84 to 0.9.85
Bumps [openssl-sys](https://github.com/sfackler/rust-openssl) from 0.9.84 to 0.9.85.
- [Release notes](https://github.com/sfackler/rust-openssl/releases)
- [Commits](https://github.com/sfackler/rust-openssl/compare/openssl-sys-v0.9.84...openssl-sys-v0.9.85)

---
updated-dependencies:
- dependency-name: openssl-sys
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-04-12 07:57:22 +02:00
dependabot[bot]
2611f5a13e Bump crossbeam-channel from 0.5.7 to 0.5.8
Bumps [crossbeam-channel](https://github.com/crossbeam-rs/crossbeam) from 0.5.7 to 0.5.8.
- [Release notes](https://github.com/crossbeam-rs/crossbeam/releases)
- [Changelog](https://github.com/crossbeam-rs/crossbeam/blob/master/CHANGELOG.md)
- [Commits](https://github.com/crossbeam-rs/crossbeam/compare/crossbeam-channel-0.5.7...crossbeam-channel-0.5.8)

---
updated-dependencies:
- dependency-name: crossbeam-channel
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-04-12 07:57:02 +02:00
pm100
12fdfeafa8 better binary explanations 2023-04-12 06:45:36 +02:00
pm100
c1e3e978a2 fix test failure on nightly due to cef81dc 2023-04-12 06:39:53 +02:00
extrawurst
76258fbb93 bump git2-rs 2023-04-03 12:17:55 +02:00
dependabot[bot]
5ac6144c35 Bump openssl-sys from 0.9.82 to 0.9.84
Bumps [openssl-sys](https://github.com/sfackler/rust-openssl) from 0.9.82 to 0.9.84.
- [Release notes](https://github.com/sfackler/rust-openssl/releases)
- [Commits](https://github.com/sfackler/rust-openssl/compare/openssl-sys-v0.9.82...openssl-sys-v0.9.84)

---
updated-dependencies:
- dependency-name: openssl-sys
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-04-03 09:15:19 +02:00
Stefan Krieger
75abd0f283 Add openSUSE (TW) to 'Installation' section
Since gitui is now available in the official software repository for openSUSE Tumbleweed it would make sense to add it to the install instructions.
2023-03-30 08:24:09 +02:00
dependabot[bot]
9782eb7b1f Bump anyhow from 1.0.69 to 1.0.70
Bumps [anyhow](https://github.com/dtolnay/anyhow) from 1.0.69 to 1.0.70.
- [Release notes](https://github.com/dtolnay/anyhow/releases)
- [Commits](https://github.com/dtolnay/anyhow/compare/1.0.69...1.0.70)

---
updated-dependencies:
- dependency-name: anyhow
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-03-20 18:16:21 +01:00
dependabot[bot]
00182d1c5e Bump openssl-sys from 0.9.81 to 0.9.82
Bumps [openssl-sys](https://github.com/sfackler/rust-openssl) from 0.9.81 to 0.9.82.
- [Release notes](https://github.com/sfackler/rust-openssl/releases)
- [Commits](https://github.com/sfackler/rust-openssl/compare/openssl-sys-v0.9.81...openssl-sys-v0.9.82)

---
updated-dependencies:
- dependency-name: openssl-sys
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-03-20 18:15:49 +01:00
dependabot[bot]
cdae2b067a Bump openssl-sys from 0.9.80 to 0.9.81
Bumps [openssl-sys](https://github.com/sfackler/rust-openssl) from 0.9.80 to 0.9.81.
- [Release notes](https://github.com/sfackler/rust-openssl/releases)
- [Commits](https://github.com/sfackler/rust-openssl/compare/openssl-sys-v0.9.80...openssl-sys-v0.9.81)

---
updated-dependencies:
- dependency-name: openssl-sys
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-03-15 16:28:18 +01:00
dependabot[bot]
0052cab3df Bump serde from 1.0.155 to 1.0.156
Bumps [serde](https://github.com/serde-rs/serde) from 1.0.155 to 1.0.156.
- [Release notes](https://github.com/serde-rs/serde/releases)
- [Commits](https://github.com/serde-rs/serde/compare/v1.0.155...v1.0.156)

---
updated-dependencies:
- dependency-name: serde
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-03-15 16:27:59 +01:00
dependabot[bot]
1a298a4e72 Bump serde from 1.0.152 to 1.0.155
Bumps [serde](https://github.com/serde-rs/serde) from 1.0.152 to 1.0.155.
- [Release notes](https://github.com/serde-rs/serde/releases)
- [Commits](https://github.com/serde-rs/serde/compare/v1.0.152...v1.0.155)

---
updated-dependencies:
- dependency-name: serde
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-03-13 10:04:17 +01:00
dependabot[bot]
6abfacb5fd Bump chrono from 0.4.23 to 0.4.24
Bumps [chrono](https://github.com/chronotope/chrono) from 0.4.23 to 0.4.24.
- [Release notes](https://github.com/chronotope/chrono/releases)
- [Changelog](https://github.com/chronotope/chrono/blob/main/CHANGELOG.md)
- [Commits](https://github.com/chronotope/chrono/compare/v0.4.23...v0.4.24)

---
updated-dependencies:
- dependency-name: chrono
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-03-13 10:03:50 +01:00
Remo Senekowitsch
63f230f0d1 fix freeze on copy with wl-copy 2023-03-07 18:50:09 +01:00
dependabot[bot]
3ceeb33c25 Bump rayon-core from 1.10.2 to 1.11.0
Bumps [rayon-core](https://github.com/rayon-rs/rayon) from 1.10.2 to 1.11.0.
- [Release notes](https://github.com/rayon-rs/rayon/releases)
- [Changelog](https://github.com/rayon-rs/rayon/blob/master/RELEASES.md)
- [Commits](https://github.com/rayon-rs/rayon/compare/rayon-core-v1.10.2...rayon-core-v1.11.0)

---
updated-dependencies:
- dependency-name: rayon-core
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-03-06 07:19:28 +01:00
dependabot[bot]
73db2cfd03 Bump thiserror from 1.0.38 to 1.0.39
Bumps [thiserror](https://github.com/dtolnay/thiserror) from 1.0.38 to 1.0.39.
- [Release notes](https://github.com/dtolnay/thiserror/releases)
- [Commits](https://github.com/dtolnay/thiserror/compare/1.0.38...1.0.39)

---
updated-dependencies:
- dependency-name: thiserror
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-03-06 07:19:01 +01:00
dependabot[bot]
88a3efbe06 Bump struct-patch from 0.2.0 to 0.2.3
Bumps [struct-patch](https://github.com/yanganto/struct-patch) from 0.2.0 to 0.2.3.
- [Release notes](https://github.com/yanganto/struct-patch/releases)
- [Commits](https://github.com/yanganto/struct-patch/compare/v0.2.0...v0.2.3)

---
updated-dependencies:
- dependency-name: struct-patch
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-03-06 07:18:30 +01:00
dependabot[bot]
6c38c65716 Bump pprof from 0.11.0 to 0.11.1
Bumps [pprof](https://github.com/tikv/pprof-rs) from 0.11.0 to 0.11.1.
- [Release notes](https://github.com/tikv/pprof-rs/releases)
- [Changelog](https://github.com/tikv/pprof-rs/blob/master/CHANGELOG.md)
- [Commits](https://github.com/tikv/pprof-rs/compare/v0.11.0...v0.11.1)

---
updated-dependencies:
- dependency-name: pprof
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-03-05 12:44:56 +01:00
Louis Bourque
45bb8a71b5 Fix freeze on copy when xclip is installed on Linux 2023-03-05 12:44:01 +01:00
Antonio Yang
f8e1c26309
Fix dep struct patch (#1577) 2023-03-04 15:51:09 +01:00
dependabot[bot]
5df48021d8 Bump simplelog from 0.12.0 to 0.12.1
Bumps [simplelog](https://github.com/drakulix/simplelog.rs) from 0.12.0 to 0.12.1.
- [Release notes](https://github.com/drakulix/simplelog.rs/releases)
- [Changelog](https://github.com/Drakulix/simplelog.rs/blob/master/CHANGELOG.md)
- [Commits](https://github.com/drakulix/simplelog.rs/compare/v0.12.0...v0.12.1)

---
updated-dependencies:
- dependency-name: simplelog
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-03-03 15:47:49 +01:00
extrawurst
eba650a4c8 cleanup 2023-03-03 15:47:02 +01:00
extrawurst
017823da49 try fixing CI 2023-03-03 15:43:32 +01:00
extrawurst
01f6580125 Revert "Refactor key_list and remove key_list_file (#1511)"
This reverts commit 0fb1856d18.
2023-03-01 16:10:45 +01:00
extrawurst
8f29cb2e81
Cargo install in CI (#1576)
* run cargo install in CI
* also run cargo install in musl env
2023-03-01 15:44:51 +01:00
extrawurst
670677044f pin dep 2023-03-01 14:30:34 +01:00
extrawurst
2fa4c7932c fix race issue in revlog message fetching
sometimes messages appear empty because getting the revlog is so fast (empty repo) that no draw happened yet and so we do not know yet what size the view will have.
fixes #1473
2023-03-01 14:22:42 +01:00
dependabot[bot]
e1a40dd224 Bump crossbeam-channel from 0.5.6 to 0.5.7
Bumps [crossbeam-channel](https://github.com/crossbeam-rs/crossbeam) from 0.5.6 to 0.5.7.
- [Release notes](https://github.com/crossbeam-rs/crossbeam/releases)
- [Changelog](https://github.com/crossbeam-rs/crossbeam/blob/master/CHANGELOG.md)
- [Commits](https://github.com/crossbeam-rs/crossbeam/compare/crossbeam-channel-0.5.6...crossbeam-channel-0.5.7)

---
updated-dependencies:
- dependency-name: crossbeam-channel
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-03-01 13:20:33 +01:00
dependabot[bot]
35e1a0b735 Bump clap from 4.1.7 to 4.1.8
Bumps [clap](https://github.com/clap-rs/clap) from 4.1.7 to 4.1.8.
- [Release notes](https://github.com/clap-rs/clap/releases)
- [Changelog](https://github.com/clap-rs/clap/blob/master/CHANGELOG.md)
- [Commits](https://github.com/clap-rs/clap/compare/v4.1.7...v4.1.8)

---
updated-dependencies:
- dependency-name: clap
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-03-01 13:20:14 +01:00
dependabot[bot]
258299265a Bump clap from 4.1.6 to 4.1.7
Bumps [clap](https://github.com/clap-rs/clap) from 4.1.6 to 4.1.7.
- [Release notes](https://github.com/clap-rs/clap/releases)
- [Changelog](https://github.com/clap-rs/clap/blob/master/CHANGELOG.md)
- [Commits](https://github.com/clap-rs/clap/compare/v4.1.6...v4.1.7)

---
updated-dependencies:
- dependency-name: clap
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-02-28 19:54:52 +01:00
extrawurst
c3e318fdd5 fix clippy nightly 2023-02-27 23:26:14 +01:00
dependabot[bot]
000deb2cf8 Bump bytesize from 1.1.0 to 1.2.0
Bumps [bytesize](https://github.com/hyunsik/bytesize) from 1.1.0 to 1.2.0.
- [Release notes](https://github.com/hyunsik/bytesize/releases)
- [Commits](https://github.com/hyunsik/bytesize/compare/v1.1.0...v1.2.0)

---
updated-dependencies:
- dependency-name: bytesize
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-02-27 10:33:41 +01:00
dependabot[bot]
fa551878a8 Bump tempfile from 3.3.0 to 3.4.0
Bumps [tempfile](https://github.com/Stebalien/tempfile) from 3.3.0 to 3.4.0.
- [Release notes](https://github.com/Stebalien/tempfile/releases)
- [Changelog](https://github.com/Stebalien/tempfile/blob/master/NEWS)
- [Commits](https://github.com/Stebalien/tempfile/commits)

---
updated-dependencies:
- dependency-name: tempfile
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-02-27 10:32:55 +01:00
extrawurst
10bf4e310d reword gif 2023-02-19 11:44:25 +00:00
extrawurst
9d83ce358e
Reword commit (#1553)
* reuse commit popup for reword
* switch to status after reword
* show command
* prepopulate with old msg
* changelog

Closes #829
2023-02-18 20:47:24 +00:00
dependabot[bot]
ab01fc7e35 Bump clap from 4.1.4 to 4.1.6
Bumps [clap](https://github.com/clap-rs/clap) from 4.1.4 to 4.1.6.
- [Release notes](https://github.com/clap-rs/clap/releases)
- [Changelog](https://github.com/clap-rs/clap/blob/master/CHANGELOG.md)
- [Commits](https://github.com/clap-rs/clap/compare/v4.1.4...v4.1.6)

---
updated-dependencies:
- dependency-name: clap
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-02-18 13:27:23 +00:00
extrawurst
9d256510ac print message of where log will be written
closes #1472
2023-02-17 16:30:43 +00:00
extrawurst
ba9b6d6b6a fix crash on entering submodule #1510
also do not allow opening submodule without workdir
2023-02-17 11:19:16 +00:00
extrawurst
8f612c5cb4 cleanup 2023-02-17 10:21:07 +00:00
Antonio Yang
0fb1856d18
Refactor key_list and remove key_list_file (#1511)
* refactor key_list and rm redundant key_list_file
* remove redudent key_list_file.rs
* fix testcase conflict
2023-02-15 07:10:47 +00:00
dependabot[bot]
51d7616828
Bump once_cell from 1.17.0 to 1.17.1 (#1546)
Bumps [once_cell](https://github.com/matklad/once_cell) from 1.17.0 to 1.17.1.
- [Release notes](https://github.com/matklad/once_cell/releases)
- [Changelog](https://github.com/matklad/once_cell/blob/master/CHANGELOG.md)
- [Commits](https://github.com/matklad/once_cell/compare/v1.17.0...v1.17.1)

---
updated-dependencies:
- dependency-name: once_cell
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-02-15 07:10:00 +00:00
extrawurst
d4d6fd28eb
Unittest keylist (#1545)
* fix warning
* add unittest to keylist
2023-02-15 00:20:45 +00:00
Antonio Yang
aefc18d819
Copy file path (#1516)
* copy file path to click board
* update change log
* Add copy path info to command bar
2023-02-12 10:06:56 +00:00
extrawurst
0fca8befc8 cleanup 2023-02-12 10:06:18 +00:00
extrawurst
8f7f35b8a9 remove focus key bindings
merge them into `move_XYZ` keys
2023-02-11 10:51:51 +00:00
extrawurst
201561bc7a allow duplicates right now 2023-02-11 10:14:37 +00:00
dependabot[bot]
f0b491d0ec Bump openssl-src from 111.24.0+1.1.1s to 111.25.0+1.1.1t
Bumps [openssl-src](https://github.com/alexcrichton/openssl-src-rs) from 111.24.0+1.1.1s to 111.25.0+1.1.1t.
- [Release notes](https://github.com/alexcrichton/openssl-src-rs/releases)
- [Commits](https://github.com/alexcrichton/openssl-src-rs/commits)

---
updated-dependencies:
- dependency-name: openssl-src
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-02-09 00:08:45 +00:00
dependabot[bot]
06289afe6e Bump anyhow from 1.0.68 to 1.0.69
Bumps [anyhow](https://github.com/dtolnay/anyhow) from 1.0.68 to 1.0.69.
- [Release notes](https://github.com/dtolnay/anyhow/releases)
- [Commits](https://github.com/dtolnay/anyhow/compare/1.0.68...1.0.69)

---
updated-dependencies:
- dependency-name: anyhow
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-02-06 07:06:10 +00:00
extrawurst
7f5c6ceeef
Check udeps (#1535) 2023-02-05 10:32:25 +01:00
extrawurst
bff1c33625 wording 2023-02-04 19:43:43 +01:00
extrawurst
8ab62244ce
support reset from log view (#1534) 2023-02-04 16:15:26 +01:00
extrawurst
1a0167e7f8 mention contributor in changelog 2023-02-04 07:01:48 +01:00
Andrey Krupskiy
57a5322fa7
Checkout commit (#1499)
* Add keybind to checkout commit in log view
* Extract commit checkout into method
* add quckbar hint for checkout commit
* add a smoke test
* update changelog
* show an error in popup

---------

Co-authored-by: Omnikar <omnikar5@gmail.com>
Co-authored-by: extrawurst <776816+extrawurst@users.noreply.github.com>
2023-02-04 07:00:19 +01:00
hamflx
5411397f9a
Fix commit hooks error and "no such file" error on Windows with wsl2 installed. (#1532)
* use git bash instead of wsl bash
* add unittest for some simple shell commands.
* fix incompatible unitest with macOS
2023-02-02 10:33:57 +01:00
dependabot[bot]
0e0cdb327b Bump unicode-segmentation from 1.10.0 to 1.10.1
Bumps [unicode-segmentation](https://github.com/unicode-rs/unicode-segmentation) from 1.10.0 to 1.10.1.
- [Release notes](https://github.com/unicode-rs/unicode-segmentation/releases)
- [Commits](https://github.com/unicode-rs/unicode-segmentation/compare/v1.10.0...v1.10.1)

---
updated-dependencies:
- dependency-name: unicode-segmentation
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-02-01 10:03:05 +01:00
extrawurst
54fdf3d51c cargo updates 2023-01-29 11:23:54 +01:00
extrawurst
32f6134309 log errors in key bindings parsing (#1491) 2023-01-29 11:15:19 +01:00
extrawurst
b424b9beeb changelog 2023-01-29 09:34:25 +01:00
extrawurst
ce70d5ef12 bump msrv 1.64 2023-01-29 09:28:09 +01:00
extrawurst
48eed0562a additional documentation for stash_open (#1525) 2023-01-28 14:44:36 +01:00
extrawurst
c663e6b0a9 cargo update 2023-01-27 00:38:48 +01:00
dependabot[bot]
582b660bf7 Bump rayon-core from 1.10.1 to 1.10.2
Bumps [rayon-core](https://github.com/rayon-rs/rayon) from 1.10.1 to 1.10.2.
- [Release notes](https://github.com/rayon-rs/rayon/releases)
- [Changelog](https://github.com/rayon-rs/rayon/blob/master/RELEASES.md)
- [Commits](https://github.com/rayon-rs/rayon/compare/rayon-core-v1.10.1...rayon-core-v1.10.2)

---
updated-dependencies:
- dependency-name: rayon-core
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-01-23 09:40:34 +01:00
dependabot[bot]
1a833fcec3 Bump which from 4.3.0 to 4.4.0
Bumps [which](https://github.com/harryfei/which-rs) from 4.3.0 to 4.4.0.
- [Release notes](https://github.com/harryfei/which-rs/releases)
- [Commits](https://github.com/harryfei/which-rs/compare/4.3.0...4.4.0)

---
updated-dependencies:
- dependency-name: which
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-01-23 09:40:09 +01:00
dependabot[bot]
8088aa9743 Bump git2 from 0.16.0 to 0.16.1
Bumps [git2](https://github.com/rust-lang/git2-rs) from 0.16.0 to 0.16.1.
- [Release notes](https://github.com/rust-lang/git2-rs/releases)
- [Commits](https://github.com/rust-lang/git2-rs/compare/git2-curl-0.16.0...0.16.1)

---
updated-dependencies:
- dependency-name: git2
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-01-21 08:56:12 +01:00
dependabot[bot]
a5128b94dd Bump serial_test from 0.10.0 to 1.0.0
Bumps [serial_test](https://github.com/palfrey/serial_test) from 0.10.0 to 1.0.0.
- [Release notes](https://github.com/palfrey/serial_test/releases)
- [Commits](https://github.com/palfrey/serial_test/compare/v0.10.0...v1.0.0)

---
updated-dependencies:
- dependency-name: serial_test
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-01-18 11:59:27 +01:00
extrawurst
ad45102e3b add proper changelog for 1dc097e 2023-01-13 14:52:59 +01:00
Alen Šiljak
1dc097ebd8
Status view fetch (#1483)
* adding the command to Status view
* cleanup and fix warnings

Co-authored-by: extrawurst <mail@rusticorn.com>
2023-01-13 14:51:34 +01:00
extrawurst
b8a436fdeb clippy fix 2023-01-13 14:13:40 +01:00
extrawurst
ad42ad9269 added relevant changlog for 5ca712f 2023-01-13 14:08:07 +01:00
Dave
5ca712ff80
Add no-verify commit command (#1375)
* add no-verify option on commit action
* make verify a bool flag on commit component
2023-01-13 14:05:51 +01:00
dependabot[bot]
c7356ae3de Bump git2 from 0.15.0 to 0.16.0
Bumps [git2](https://github.com/rust-lang/git2-rs) from 0.15.0 to 0.16.0.
- [Release notes](https://github.com/rust-lang/git2-rs/releases)
- [Commits](https://github.com/rust-lang/git2-rs/compare/git2-curl-0.15.0...git2-curl-0.16.0)

---
updated-dependencies:
- dependency-name: git2
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-01-11 09:09:00 +01:00
Christoph Rüßler
9fa5fddd93
Allow to scroll diffs horizontally (#1327) 2023-01-08 12:47:37 +01:00
extrawurst
f29178d1b3 cleanup unused deps 2023-01-06 11:48:17 +01:00
extrawurst
3ff5b51fe9 cargo update 2023-01-06 11:07:07 +01:00
extrawurst
00040a39cb upgrade dep 2023-01-06 11:06:25 +01:00
extrawurst
71ba561221 bump version 2023-01-06 11:05:24 +01:00
extrawurst
0b1a0a148c cargo update 2022-12-30 09:44:29 +01:00
extrawurst
c6dec379f2 clippy nightly fixes 2022-12-30 09:42:50 +01:00
extrawurst
e5c62d01e1 remove duplicate edit cmd from bar (#1489) 2022-12-30 00:15:13 +01:00
dependabot[bot]
761cb10405 Bump serde from 1.0.151 to 1.0.152
Bumps [serde](https://github.com/serde-rs/serde) from 1.0.151 to 1.0.152.
- [Release notes](https://github.com/serde-rs/serde/releases)
- [Commits](https://github.com/serde-rs/serde/compare/v1.0.151...v1.0.152)

---
updated-dependencies:
- dependency-name: serde
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-12-27 13:48:33 +01:00
dependabot[bot]
fce6c51bea Bump clap from 4.0.30 to 4.0.32
Bumps [clap](https://github.com/clap-rs/clap) from 4.0.30 to 4.0.32.
- [Release notes](https://github.com/clap-rs/clap/releases)
- [Changelog](https://github.com/clap-rs/clap/blob/master/CHANGELOG.md)
- [Commits](https://github.com/clap-rs/clap/compare/v4.0.30...v4.0.32)

---
updated-dependencies:
- dependency-name: clap
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-12-25 14:28:22 +01:00
dependabot[bot]
01188e214e Bump easy-cast from 0.5.1 to 0.5.2
Bumps [easy-cast](https://github.com/kas-gui/easy-cast) from 0.5.1 to 0.5.2.
- [Release notes](https://github.com/kas-gui/easy-cast/releases)
- [Changelog](https://github.com/kas-gui/easy-cast/blob/master/CHANGELOG.md)
- [Commits](https://github.com/kas-gui/easy-cast/compare/0.5.1...0.5.2)

---
updated-dependencies:
- dependency-name: easy-cast
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-12-23 12:06:41 +01:00
dependabot[bot]
ece5a86855 Bump clap from 4.0.29 to 4.0.30
Bumps [clap](https://github.com/clap-rs/clap) from 4.0.29 to 4.0.30.
- [Release notes](https://github.com/clap-rs/clap/releases)
- [Changelog](https://github.com/clap-rs/clap/blob/master/CHANGELOG.md)
- [Commits](https://github.com/clap-rs/clap/compare/v4.0.29...v4.0.30)

---
updated-dependencies:
- dependency-name: clap
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-12-22 09:46:35 +01:00
extrawurst
669ea61019 followup 9ca6068 2022-12-21 08:17:33 +01:00
extrawurst
03dd0de07a upgrade so we fix the remaining linting issue 2022-12-20 17:21:46 +01:00
extrawurst
81645a61d4 cargo update 2022-12-20 15:22:45 +01:00
extrawurst
438fa8ec19 cargo update 2022-12-20 15:15:14 +01:00
extrawurst
9ca6068a17 fix crash in small window and branches
fixes #1470
2022-12-18 20:07:26 +01:00
dependabot[bot]
b59526c86c Bump serde from 1.0.149 to 1.0.150
Bumps [serde](https://github.com/serde-rs/serde) from 1.0.149 to 1.0.150.
- [Release notes](https://github.com/serde-rs/serde/releases)
- [Commits](https://github.com/serde-rs/serde/compare/v1.0.149...v1.0.150)

---
updated-dependencies:
- dependency-name: serde
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-12-12 03:25:29 +01:00
extrawurst
55a006a4a0 update env_logger 2022-12-10 11:32:43 +01:00
extrawurst
15500246ca cargo update 2022-12-10 11:31:10 +01:00
extrawurst
289eddd9be fix nightly clippy 2022-12-10 11:27:59 +01:00
extrawurst
7dcf93e0b2 do not show edit item cmd in commit detail: msg
fixes #1461
2022-12-10 11:25:11 +01:00
extrawurst
e8b7097845 fix regular app exectuation after 045e9e5 2022-12-10 01:57:04 +01:00
extrawurst
fa302717e8 add changelog for 045e9e5 2022-12-10 01:23:58 +01:00
extrawurst
045e9e5f09 bugreport arg does not require param
fixes #1466
2022-12-10 01:22:34 +01:00
dependabot[bot]
b9b01cd32a Bump openssl-sys from 0.9.78 to 0.9.79
Bumps [openssl-sys](https://github.com/sfackler/rust-openssl) from 0.9.78 to 0.9.79.
- [Release notes](https://github.com/sfackler/rust-openssl/releases)
- [Commits](https://github.com/sfackler/rust-openssl/compare/openssl-sys-v0.9.78...openssl-sys-v0.9.79)

---
updated-dependencies:
- dependency-name: openssl-sys
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-12-07 09:19:03 +01:00
dependabot[bot]
c1305f47b2 Bump serde from 1.0.147 to 1.0.148
Bumps [serde](https://github.com/serde-rs/serde) from 1.0.147 to 1.0.148.
- [Release notes](https://github.com/serde-rs/serde/releases)
- [Commits](https://github.com/serde-rs/serde/compare/v1.0.147...v1.0.148)

---
updated-dependencies:
- dependency-name: serde
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-12-02 08:08:04 +01:00
dependabot[bot]
71ea7fb89d Bump shellexpand from 2.1.2 to 3.0.0
Bumps [shellexpand](https://gitlab.com/ijackson/rust-shellexpand) from 2.1.2 to 3.0.0.
- [Release notes](https://gitlab.com/ijackson/rust-shellexpand/tags)
- [Commits](https://gitlab.com/ijackson/rust-shellexpand/compare/shellexpand/2.1.2...shellexpand-3.0.0)

---
updated-dependencies:
- dependency-name: shellexpand
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-12-02 07:35:48 +01:00
extrawurst
9210d8ca87 fix key binding used in comand bar for stash_open
this fixes #1454
2022-11-28 01:14:03 +01:00
dependabot[bot]
e902f4ce3e Bump openssl-sys from 0.9.77 to 0.9.78
Bumps [openssl-sys](https://github.com/sfackler/rust-openssl) from 0.9.77 to 0.9.78.
- [Release notes](https://github.com/sfackler/rust-openssl/releases)
- [Commits](https://github.com/sfackler/rust-openssl/compare/openssl-sys-v0.9.77...openssl-sys-v0.9.78)

---
updated-dependencies:
- dependency-name: openssl-sys
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-11-27 21:05:11 +01:00
extrawurst
c24db1189e more funding options 2022-11-26 22:02:03 +01:00
extrawurst
6c4ee56eed remove debug sleep (fixes #1451) 2022-11-25 21:16:47 +01:00
extrawurst
ca19d42948 follow up to 9b46bb6 (see #1420)
add standard "Changs to be commited" text on top of changes.
2022-11-23 15:31:37 +01:00
bc-universe
9b46bb63f9 Display commit changes (#1420)
When using an external editor to edit the commit message, the changes are now displayed
2022-11-23 15:26:26 +01:00
extrawurst
8da9cfc21d improve help on where to use amend command 2022-11-23 15:14:29 +01:00
extrawurst
8d2de65868 fix clippy nightly 2022-11-23 15:14:12 +01:00
extrawurst
b987598c7b fix next commit msg from history ordering
and also disable this command if no history is present

this fixes #1445
2022-11-22 12:18:36 +01:00
extrawurst
234e7cb3fc more changelog notes 2022-11-22 11:26:28 +01:00
extrawurst
0b0f550572 fix cargo publish 2022-11-22 11:14:22 +01:00
extrawurst
83d6941c7d cargo update 2022-11-22 11:09:19 +01:00
extrawurst
4ef9659138 prep release 2022-11-22 11:07:41 +01:00
extrawurst
bea70306e2 fix up for 8e8c5fa never showing file tree 2022-11-22 10:55:25 +01:00
extrawurst
8e8c5fad55 make fetching tree files async 2022-11-21 20:16:48 +01:00
extrawurst
bc15b5d550 better link 2022-11-21 18:31:32 +01:00
extrawurst
fbcf908881 update changelog 2022-11-21 18:26:49 +01:00
extrawurst
289f4f3630 cleanup logging 2022-11-21 18:17:28 +01:00
extrawurst
bbcadcb5d1 threaded watcher creation and arg for config
new argument `polling` allows to force watcher to use polling instead of file system events.
this should address the issue in #1436 and maybe even #1437
2022-11-21 18:09:08 +01:00
extrawurst
8cdb02349f scope time repo watcher 2022-11-21 17:04:54 +01:00
extrawurst
92f63d107c support fetching branch_infos async 2022-11-21 16:32:17 +01:00
extrawurst
3fee481e8d fix status_tree not showing while first status loading 2022-11-21 14:45:03 +01:00
extrawurst
3667db37e1 trace app start duration 2022-11-21 14:30:17 +01:00
extrawurst
faf912393b add liberapay 2022-11-20 22:22:44 +01:00
extrawurst
b495425b16 fix changelog 2022-11-19 18:19:21 +01:00
extrawurst
5495e9bec3 fix building asyncgit alone 2022-11-19 17:48:56 +01:00
extrawurst
425e9f091c bump versions (prepare for release) 2022-11-19 17:44:05 +01:00
extrawurst
7efa225f31 fix lints 2022-11-19 17:39:18 +01:00
extrawurst
5c191b9cf3 upgrade textwrap 2022-11-19 17:39:18 +01:00
dependabot[bot]
b693d33c32 Bump clap from 4.0.25 to 4.0.26
Bumps [clap](https://github.com/clap-rs/clap) from 4.0.25 to 4.0.26.
- [Release notes](https://github.com/clap-rs/clap/releases)
- [Changelog](https://github.com/clap-rs/clap/blob/master/CHANGELOG.md)
- [Commits](https://github.com/clap-rs/clap/compare/v4.0.25...v4.0.26)

---
updated-dependencies:
- dependency-name: clap
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-11-19 17:11:30 +01:00
extrawurst
9cfad7b1d0 fix win ci 2022-11-19 17:11:05 +01:00
dependabot[bot]
c6e7a55fbf Bump clap from 4.0.23 to 4.0.25
Bumps [clap](https://github.com/clap-rs/clap) from 4.0.23 to 4.0.25.
- [Release notes](https://github.com/clap-rs/clap/releases)
- [Changelog](https://github.com/clap-rs/clap/blob/master/CHANGELOG.md)
- [Commits](https://github.com/clap-rs/clap/compare/v4.0.23...v4.0.25)

---
updated-dependencies:
- dependency-name: clap
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-11-16 07:19:06 +01:00
extrawurst
c67c2cf622 cargo sort 2022-11-14 16:08:42 +01:00
extrawurst
c03a220990 nightly clippy fix 2022-11-14 15:04:48 +01:00
extrawurst
6b9a91f17d
Edit file from file tree (#1430)
* allow edit file from any StatusTreeComponent
2022-11-14 15:01:34 +01:00
extrawurst
b6ed33037e upgrade clap 2022-11-14 14:57:30 +01:00
extrawurst
fbab49b858 changelog updates 2022-11-14 14:12:16 +01:00
Alexandru Macovei
0f9389c284
display commit description in addition to hash in file view (#1380) 2022-11-14 14:09:15 +01:00
extrawurst
45d850e8ca fix new clippy warnings 2022-11-14 14:06:43 +01:00
dependabot[bot]
5430a4d21d Bump chrono from 0.4.22 to 0.4.23
Bumps [chrono](https://github.com/chronotope/chrono) from 0.4.22 to 0.4.23.
- [Release notes](https://github.com/chronotope/chrono/releases)
- [Changelog](https://github.com/chronotope/chrono/blob/main/CHANGELOG.md)
- [Commits](https://github.com/chronotope/chrono/compare/v0.4.22...v0.4.23)

---
updated-dependencies:
- dependency-name: chrono
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-11-14 13:52:23 +01:00
dependabot[bot]
5876d617ff Bump pprof from 0.10.1 to 0.11.0
Bumps [pprof](https://github.com/tikv/pprof-rs) from 0.10.1 to 0.11.0.
- [Release notes](https://github.com/tikv/pprof-rs/releases)
- [Changelog](https://github.com/tikv/pprof-rs/blob/master/CHANGELOG.md)
- [Commits](https://github.com/tikv/pprof-rs/commits)

---
updated-dependencies:
- dependency-name: pprof
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-11-08 09:43:58 +01:00
dependabot[bot]
dbe5a37372 Bump env_logger from 0.9.1 to 0.9.3
Bumps [env_logger](https://github.com/env-logger-rs/env_logger) from 0.9.1 to 0.9.3.
- [Release notes](https://github.com/env-logger-rs/env_logger/releases)
- [Changelog](https://github.com/env-logger-rs/env_logger/blob/main/CHANGELOG.md)
- [Commits](https://github.com/env-logger-rs/env_logger/compare/v0.9.1...v0.9.3)

---
updated-dependencies:
- dependency-name: env_logger
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-11-08 09:43:31 +01:00
dependabot[bot]
2579d6bd6a Bump once_cell from 1.15.0 to 1.16.0
Bumps [once_cell](https://github.com/matklad/once_cell) from 1.15.0 to 1.16.0.
- [Release notes](https://github.com/matklad/once_cell/releases)
- [Changelog](https://github.com/matklad/once_cell/blob/master/CHANGELOG.md)
- [Commits](https://github.com/matklad/once_cell/compare/v1.15.0...v1.16.0)

---
updated-dependencies:
- dependency-name: once_cell
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-11-07 08:29:17 +01:00
Alexandru Macovei
e371153034
Report failure to copy to clipboard (#1410)
* (refactor) move copy_commit_hash from revlog into commitlist, and make fewer functions public in commitlist
* (refactor) reduce duplication in commit copying code; use already-stored commits instead of looking up items
* (clipboard) actually check subprocess exit status, and report failure instead of ignoring it
* (commitlist) display popup with copy failure instead of exiting the application on error
2022-11-05 16:51:03 +01:00
Artur
a172b18428
Add Linux targets for ARM, ARMv7 and AARCH64 (#1419) 2022-11-05 16:46:11 +01:00
extrawurst
a4943224bd fix nightly clippy 2022-11-05 16:33:48 +01:00
Sergio Alejandro Ribera Costa
282e578ac3
Add notification when correctly copying hash commit (#1376) 2022-10-26 14:35:31 +02:00
extrawurst
c705712a05 fix rust clippy 2022-10-26 14:34:33 +02:00
Sergio Alejandro Ribera Costa
e5acee1a0c
Generate rust toolchain file (#1401)
* Generate rust toolchain file
* Make track "stable" channel
* CD workflow override rust toolchain file
2022-10-25 09:17:37 +02:00
extrawurst
fa2ad81bec feature gif 2022-10-24 16:33:58 +02:00
extrawurst
98f6bd4e77 Cargo updates 2022-10-24 11:50:29 +02:00
extrawurst
d92d43afef Revert "Generate rust toolchain file - to develop (and build) (#1397)"
This reverts commit c7e54fa17d.
2022-10-20 16:51:36 +02:00
Tim Abell
5b69f7713f Add link from themes.md to referenced code block
Just for convenience
2022-10-20 16:30:15 +02:00
Alexandru Macovei
9c2d8c0e0d
Display current repository path in the top-right corner (#1387) 2022-10-20 16:23:58 +02:00
Sergio Alejandro Ribera Costa
c7e54fa17d
Generate rust toolchain file - to develop (and build) (#1397)
* Generate rust toolchain file
* Make track "stable" channel
2022-10-20 16:22:19 +02:00
extrawurst
ac67e53b12 dup current branch name in revlog 2022-10-19 14:03:04 +02:00
extrawurst
6dfe5ea62a make more easy to revisit 2022-10-19 13:50:11 +02:00
Alexandru Macovei
6b5745f6c2
Fix Clippy Lints (#1390)
* apply latest nigtly clippy lints
* temporarily disable const fn lints due to nigh false positive count on nightly
2022-10-19 13:45:12 +02:00
Ivan Zvonimir Horvat
e2a0f3800f make: add clean cmd 2022-10-18 13:57:02 +02:00
dependabot[bot]
5b7686bf92 Bump easy-cast from 0.5.0 to 0.5.1
Bumps [easy-cast](https://github.com/kas-gui/easy-cast) from 0.5.0 to 0.5.1.
- [Release notes](https://github.com/kas-gui/easy-cast/releases)
- [Changelog](https://github.com/kas-gui/easy-cast/blob/master/CHANGELOG.md)
- [Commits](https://github.com/kas-gui/easy-cast/compare/0.5.0...0.5.1)

---
updated-dependencies:
- dependency-name: easy-cast
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-10-18 13:48:36 +02:00
extrawurst
f04af21382 align branch name parenth usage
and added changelog github user name
2022-10-18 13:44:27 +02:00
Alexandru Macovei
216fad3140
Display tags and branches in the revlog (#1371)
* give tags a more distinctive appearance in the revlog
* store branches on commitlist, and display branch labels on head commits
2022-10-18 13:37:20 +02:00
extrawurst
8604b331ae
selected items should have dedicated fg color (#1366)
* selected items should have dedicated fg color
2022-09-30 20:19:37 +02:00
dependabot[bot]
eeb502d0ff Bump thiserror from 1.0.35 to 1.0.37
Bumps [thiserror](https://github.com/dtolnay/thiserror) from 1.0.35 to 1.0.37.
- [Release notes](https://github.com/dtolnay/thiserror/releases)
- [Commits](https://github.com/dtolnay/thiserror/compare/1.0.35...1.0.37)

---
updated-dependencies:
- dependency-name: thiserror
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-09-29 16:48:07 +02:00
extrawurst
049272ea2b fix ci 2022-09-27 14:05:35 +02:00
dependabot[bot]
11adcce84e Bump itertools from 0.10.4 to 0.10.5
Bumps [itertools](https://github.com/rust-itertools/itertools) from 0.10.4 to 0.10.5.
- [Release notes](https://github.com/rust-itertools/itertools/releases)
- [Changelog](https://github.com/rust-itertools/itertools/blob/master/CHANGELOG.md)
- [Commits](https://github.com/rust-itertools/itertools/commits)

---
updated-dependencies:
- dependency-name: itertools
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-09-21 08:20:41 +02:00
dependabot[bot]
5cc4c63066 Bump once_cell from 1.14.0 to 1.15.0
Bumps [once_cell](https://github.com/matklad/once_cell) from 1.14.0 to 1.15.0.
- [Release notes](https://github.com/matklad/once_cell/releases)
- [Changelog](https://github.com/matklad/once_cell/blob/master/CHANGELOG.md)
- [Commits](https://github.com/matklad/once_cell/compare/v1.14.0...v1.15.0)

---
updated-dependencies:
- dependency-name: once_cell
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-09-21 08:19:14 +02:00
extrawurst
df43118e55 clarify further 2022-09-20 10:07:30 +02:00
extrawurst
aeb64e1175
Commit msg history (#1346) 2022-09-20 10:07:05 +02:00
extrawurst
ff0c123293 cleanup 2022-09-20 10:01:53 +02:00
extrawurst
16a537c102
Persist all options (#1342)
closes #1340
2022-09-20 08:23:04 +02:00
Emil Jaszczuk
e0fa63c6c9
Allow copying multiple commits (#1288) 2022-09-19 10:54:29 +02:00
extrawurst
9534e4c2f9
persist current tab as options (#1339) 2022-09-18 18:05:29 +02:00
extrawurst
cb01fda516 some logfile cleanup 2022-09-18 15:24:45 +02:00
Jakub Jirutka
f69460cccf
Allow to build without vendored openssl, allow to build syntect with regex-onig (#1323)
* allow to build syntect with regex-onig

Syntect supports two regex engines:

* regex-fancy: a pure-rust regex engine based on the fancy-regex
* regex-onig: a regex engine based on the oniguruma C library

From the syntect's Readme:

> The advantage of fancy-regex is that it does not require the onig
> crate which requires building and linking the Oniguruma C library.
> Many users experience difficulty building the onig crate, especially
> on Windows and Webassembly.

> As far as our tests can tell this new engine is just as correct, but
> it hasn't been tested as extensively in production. It also currently
> seems to be about half the speed of the default Oniguruma engine

Oniguruma engine is faster than the fancy-regex engine and the syntect
project chose the latter as the default only to avoid difficulties with
linking Oniguruma (C library) on some platforms. This is not an issue
for linux distributions - linking against system-provided shared
library is preferred to bundled libraries.

Moreover, gitui built with Oniguruma instead of fancy-regex is by 25%
smaller.

This commit adds two cargo features, regex-fancy and regex-onig, to
enable respective syntect features. The former is enabled by default.

* allow to build without vendored openssl

Vendoring (bundling) openssl library is very bad for security and
Linux distributions forbid it. The aim of this change is to simplify
packaging gitui in linux distros.

Co-authored-by: extrawurst <776816+extrawurst@users.noreply.github.com>
2022-09-18 15:02:01 +02:00
Volker Schwaberow
f2b2665c78 Addresses TODO with macro log_eprintln! 2022-09-18 14:58:39 +02:00
extrawurst
5906a2789d cargo update 2022-09-18 14:57:26 +02:00
dependabot[bot]
882b890738 Bump itertools from 0.10.3 to 0.10.4
Bumps [itertools](https://github.com/rust-itertools/itertools) from 0.10.3 to 0.10.4.
- [Release notes](https://github.com/rust-itertools/itertools/releases)
- [Changelog](https://github.com/rust-itertools/itertools/blob/master/CHANGELOG.md)
- [Commits](https://github.com/rust-itertools/itertools/compare/v0.10.3...v0.10.4)

---
updated-dependencies:
- dependency-name: itertools
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-09-18 14:55:09 +02:00
Nitish Kumar
d1566861dd
fix: returns 0 exit code on error (#1322) 2022-09-18 14:54:45 +02:00
Sachin Joseph
ddf8c69253
Add a Readme entry for Winget-based installation (#1337) 2022-09-18 14:09:19 +02:00
Niko Heiskanen
0a970db24a
file blame at right revision from commit-details (#1324) 2022-09-18 14:06:46 +02:00
dependabot[bot]
30918d114c Bump anyhow from 1.0.63 to 1.0.65
Bumps [anyhow](https://github.com/dtolnay/anyhow) from 1.0.63 to 1.0.65.
- [Release notes](https://github.com/dtolnay/anyhow/releases)
- [Commits](https://github.com/dtolnay/anyhow/compare/1.0.63...1.0.65)

---
updated-dependencies:
- dependency-name: anyhow
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-09-18 12:41:54 +02:00
dependabot[bot]
e38118836e Bump unicode-width from 0.1.9 to 0.1.10
Bumps [unicode-width](https://github.com/unicode-rs/unicode-width) from 0.1.9 to 0.1.10.
- [Release notes](https://github.com/unicode-rs/unicode-width/releases)
- [Commits](https://github.com/unicode-rs/unicode-width/compare/v0.1.9...v0.1.10)

---
updated-dependencies:
- dependency-name: unicode-width
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-09-17 09:43:50 +02:00
dependabot[bot]
eeaed52cd5 Bump textwrap from 0.15.0 to 0.15.1
Bumps [textwrap](https://github.com/mgeisler/textwrap) from 0.15.0 to 0.15.1.
- [Release notes](https://github.com/mgeisler/textwrap/releases)
- [Changelog](https://github.com/mgeisler/textwrap/blob/master/CHANGELOG.md)
- [Commits](https://github.com/mgeisler/textwrap/compare/0.15.0...0.15.1)

---
updated-dependencies:
- dependency-name: textwrap
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-09-17 09:42:48 +02:00
dependabot[bot]
c99cdab614 Bump unicode-segmentation from 1.9.0 to 1.10.0
Bumps [unicode-segmentation](https://github.com/unicode-rs/unicode-segmentation) from 1.9.0 to 1.10.0.
- [Release notes](https://github.com/unicode-rs/unicode-segmentation/releases)
- [Commits](https://github.com/unicode-rs/unicode-segmentation/commits/v1.10.0)

---
updated-dependencies:
- dependency-name: unicode-segmentation
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-09-14 10:30:34 +02:00
dependabot[bot]
90c5d11e56 Bump thiserror from 1.0.34 to 1.0.35
Bumps [thiserror](https://github.com/dtolnay/thiserror) from 1.0.34 to 1.0.35.
- [Release notes](https://github.com/dtolnay/thiserror/releases)
- [Commits](https://github.com/dtolnay/thiserror/compare/1.0.34...1.0.35)

---
updated-dependencies:
- dependency-name: thiserror
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-09-14 10:29:45 +02:00
dependabot[bot]
89b499274e Bump clap from 3.2.20 to 3.2.21
Bumps [clap](https://github.com/clap-rs/clap) from 3.2.20 to 3.2.21.
- [Release notes](https://github.com/clap-rs/clap/releases)
- [Changelog](https://github.com/clap-rs/clap/blob/v3.2.21/CHANGELOG.md)
- [Commits](https://github.com/clap-rs/clap/compare/v3.2.20...v3.2.21)

---
updated-dependencies:
- dependency-name: clap
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-09-13 23:20:45 +02:00
dependabot[bot]
7ad7f4cbc4 Bump url from 2.3.0 to 2.3.1
Bumps [url](https://github.com/servo/rust-url) from 2.3.0 to 2.3.1.
- [Release notes](https://github.com/servo/rust-url/releases)
- [Commits](https://github.com/servo/rust-url/compare/v2.3.0...v2.3.1)

---
updated-dependencies:
- dependency-name: url
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-09-09 09:00:59 +02:00
Rodrigo Batista de Moraes
67fa456e08
Add word motions to text input (#1256) 2022-09-08 08:50:08 +02:00
dependabot[bot]
1020346217 Bump url from 2.2.2 to 2.3.0
Bumps [url](https://github.com/servo/rust-url) from 2.2.2 to 2.3.0.
- [Release notes](https://github.com/servo/rust-url/releases)
- [Commits](https://github.com/servo/rust-url/compare/v2.2.2...v2.3.0)

---
updated-dependencies:
- dependency-name: url
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-09-08 06:51:31 +02:00
dependabot[bot]
69d98b596e Bump thiserror from 1.0.33 to 1.0.34
Bumps [thiserror](https://github.com/dtolnay/thiserror) from 1.0.33 to 1.0.34.
- [Release notes](https://github.com/dtolnay/thiserror/releases)
- [Commits](https://github.com/dtolnay/thiserror/compare/1.0.33...1.0.34)

---
updated-dependencies:
- dependency-name: thiserror
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-09-06 11:46:19 +02:00
dependabot[bot]
1d9c1403b9 Bump notify-debouncer-mini from 0.2.0 to 0.2.1
Bumps [notify-debouncer-mini](https://github.com/notify-rs/notify) from 0.2.0 to 0.2.1.
- [Release notes](https://github.com/notify-rs/notify/releases)
- [Changelog](https://github.com/notify-rs/notify/blob/main/CHANGELOG.md)
- [Commits](https://github.com/notify-rs/notify/compare/debouncer-mini-0.2.0...debouncer-mini-0.2.1)

---
updated-dependencies:
- dependency-name: notify-debouncer-mini
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-09-05 18:03:13 +02:00
extrawurst
0ddec47611 cleanup 2022-09-03 10:22:24 +02:00
extrawurst
589536c2c3 cargo upgrades 2022-09-02 20:10:17 +02:00
extrawurst
609039eb5e update 2022-09-02 10:00:47 +02:00
extrawurst
cf9ce9d4ff
File watching using notify (#1310)
closes #1
2022-09-02 09:59:13 +02:00
dependabot[bot]
5f7213730b Bump which from 4.2.5 to 4.3.0
Bumps [which](https://github.com/harryfei/which-rs) from 4.2.5 to 4.3.0.
- [Release notes](https://github.com/harryfei/which-rs/releases)
- [Commits](https://github.com/harryfei/which-rs/compare/4.2.5...4.3.0)

---
updated-dependencies:
- dependency-name: which
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-09-02 09:18:01 +02:00
extrawurst
5101b39556 switch from lazy_static to once_cell 2022-09-02 09:17:34 +02:00
extrawurst
4249a278b6 nightly clippy fixes 2022-09-02 09:09:29 +02:00
dependabot[bot]
f308bddb77 Bump clap from 3.2.19 to 3.2.20
Bumps [clap](https://github.com/clap-rs/clap) from 3.2.19 to 3.2.20.
- [Release notes](https://github.com/clap-rs/clap/releases)
- [Changelog](https://github.com/clap-rs/clap/blob/v3.2.20/CHANGELOG.md)
- [Commits](https://github.com/clap-rs/clap/compare/v3.2.19...v3.2.20)

---
updated-dependencies:
- dependency-name: clap
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-09-02 08:58:34 +02:00
dependabot[bot]
862c6b160a Bump thiserror from 1.0.32 to 1.0.33
Bumps [thiserror](https://github.com/dtolnay/thiserror) from 1.0.32 to 1.0.33.
- [Release notes](https://github.com/dtolnay/thiserror/releases)
- [Commits](https://github.com/dtolnay/thiserror/compare/1.0.32...1.0.33)

---
updated-dependencies:
- dependency-name: thiserror
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-09-01 07:05:40 +02:00
extrawurst
cbb51bd535 submodules gif 2022-08-31 12:06:55 +02:00
extrawurst
77a20fb86f small visual fixes 2022-08-31 11:53:26 +02:00
extrawurst
b9c105605e fix submodules border style 2022-08-31 11:48:28 +02:00
extrawurst
4e0da37230
Support updating submodules (#1305) 2022-08-31 11:40:52 +02:00
extrawurst
986d34a5ac
support opening submodule (#1298) 2022-08-31 10:51:08 +02:00
dependabot[bot]
aa9ed3349f Bump anyhow from 1.0.62 to 1.0.63
Bumps [anyhow](https://github.com/dtolnay/anyhow) from 1.0.62 to 1.0.63.
- [Release notes](https://github.com/dtolnay/anyhow/releases)
- [Commits](https://github.com/dtolnay/anyhow/compare/1.0.62...1.0.63)

---
updated-dependencies:
- dependency-name: anyhow
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-08-31 08:18:28 +02:00
dependabot[bot]
79e42391ac Bump clap from 3.2.18 to 3.2.19
Bumps [clap](https://github.com/clap-rs/clap) from 3.2.18 to 3.2.19.
- [Release notes](https://github.com/clap-rs/clap/releases)
- [Changelog](https://github.com/clap-rs/clap/blob/v3.2.19/CHANGELOG.md)
- [Commits](https://github.com/clap-rs/clap/compare/v3.2.18...v3.2.19)

---
updated-dependencies:
- dependency-name: clap
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-08-31 08:18:09 +02:00
ImgBotApp
a79967fcd8 [ImgBot] Optimize images
/assets/submodules.png -- 152.13kb -> 126.56kb (16.81%)

Signed-off-by: ImgBotApp <ImgBotHelp@gmail.com>
2022-08-30 22:55:02 +02:00
extrawurst
65b121179d shorter changelog entry 2022-08-30 14:09:24 +02:00
extrawurst
69d30a2cd0 make cmdbar_bg style default to blue again 2022-08-30 14:05:10 +02:00
Luigi Clemente
bacf81f6d6
Improve UI selection and command bar (#1299)
* Added new color for commands bar
* Made commit list item and file tree item fill the entire row
2022-08-30 14:03:35 +02:00
extrawurst
f4f560ce5f cargo update 2022-08-30 13:48:23 +02:00
extrawurst
8c32ec53ee pretty_assertions migrated away from ansi-termi
this closes #1290
2022-08-30 13:46:06 +02:00
dependabot[bot]
0879c2ff9e Bump pprof from 0.10.0 to 0.10.1
Bumps [pprof](https://github.com/tikv/pprof-rs) from 0.10.0 to 0.10.1.
- [Release notes](https://github.com/tikv/pprof-rs/releases)
- [Changelog](https://github.com/tikv/pprof-rs/blob/master/CHANGELOG.md)
- [Commits](https://github.com/tikv/pprof-rs/compare/v0.10.0...v0.10.1)

---
updated-dependencies:
- dependency-name: pprof
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-08-30 08:05:58 +02:00
dependabot[bot]
3c2df73371 Bump clap from 3.2.17 to 3.2.18
Bumps [clap](https://github.com/clap-rs/clap) from 3.2.17 to 3.2.18.
- [Release notes](https://github.com/clap-rs/clap/releases)
- [Changelog](https://github.com/clap-rs/clap/blob/v3.2.18/CHANGELOG.md)
- [Commits](https://github.com/clap-rs/clap/compare/v3.2.17...v3.2.18)

---
updated-dependencies:
- dependency-name: clap
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-08-30 08:05:42 +02:00
extrawurst
1787917c13 fix small todo 2022-08-28 13:35:39 +02:00
extrawurst
05d456a462 cleanup 2022-08-28 13:34:04 +02:00
extrawurst
7068332095 cargo update 2022-08-27 17:56:12 +02:00
extrawurst
ef3ece552d
PoC list submodules (#1090) 2022-08-27 17:55:06 +02:00
dependabot[bot]
bcb565788e Bump serde from 1.0.143 to 1.0.144
Bumps [serde](https://github.com/serde-rs/serde) from 1.0.143 to 1.0.144.
- [Release notes](https://github.com/serde-rs/serde/releases)
- [Commits](https://github.com/serde-rs/serde/compare/v1.0.143...v1.0.144)

---
updated-dependencies:
- dependency-name: serde
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-08-22 08:50:03 +02:00
dependabot[bot]
d6a84c1bd2 Bump easy-cast from 0.4.4 to 0.5.0
Bumps [easy-cast](https://github.com/kas-gui/easy-cast) from 0.4.4 to 0.5.0.
- [Release notes](https://github.com/kas-gui/easy-cast/releases)
- [Changelog](https://github.com/kas-gui/easy-cast/blob/master/CHANGELOG.md)
- [Commits](https://github.com/kas-gui/easy-cast/compare/0.4.4...0.5.0)

---
updated-dependencies:
- dependency-name: easy-cast
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-08-22 08:49:51 +02:00
extrawurst
e68e5e2f46 fix one more TODO 2022-08-21 15:11:12 +02:00
extrawurst
586acc434d Merge branch 'remove-another-clippy-workaround' 2022-08-21 15:03:55 +02:00
extrawurst
200563aa58 remove another clippy ignore 2022-08-20 15:13:34 +02:00
extrawurst
12a6ecb9f7 remove another clippy workaround 2022-08-20 15:12:06 +02:00
extrawurst
b71052ef9d remove audit ignores 2022-08-20 15:09:57 +02:00
extrawurst
b9dba008aa cargo updates 2022-08-20 14:51:18 +02:00
extrawurst
79f1be0983 Merge branch 'fix-some-clippy-workarounds' 2022-08-20 14:44:12 +02:00
extrawurst
0e2262882b fix some clippy workarounds 2022-08-20 11:52:13 +02:00
extrawurst
8e54bfd364 upgrade crossterm/tui 2022-08-17 22:12:39 +02:00
extrawurst
2eee7e9b0e bump dep 2022-08-17 20:05:06 +02:00
extrawurst
d040b33559 fix changelog 2022-08-17 20:05:01 +02:00
extrawurst
02f1555103 update changelog 2022-08-17 19:53:00 +02:00
extrawurst
6630dca197
add cargo deny to CI (#1285)
* check duplicate dependencies
* also use cargo-deny for licenses checking
* also run cargo deny check in make check
2022-08-17 19:48:14 +02:00
JayceFayne
d4949a676b
support copy to clipboard on wayland (#1233) 2022-08-17 19:46:56 +02:00
extrawurst
074bb7cdb5 less chrono features/dependencies 2022-08-17 19:05:12 +02:00
dependabot[bot]
dbc927779a Bump ron from 0.7.1 to 0.8.0
Bumps [ron](https://github.com/ron-rs/ron) from 0.7.1 to 0.8.0.
- [Release notes](https://github.com/ron-rs/ron/releases)
- [Changelog](https://github.com/ron-rs/ron/blob/master/CHANGELOG.md)
- [Commits](https://github.com/ron-rs/ron/compare/v0.7.1...v0.8.0)

---
updated-dependencies:
- dependency-name: ron
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-08-17 18:46:53 +02:00
extrawurst
8986f70a89 missing changelog 2022-08-17 18:31:49 +02:00
André Kelpe
caa7840091 mention macports in install section 2022-08-17 18:29:43 +02:00
extrawurst
948f4bb287 clap unittest 2022-08-17 18:23:55 +02:00
extrawurst
7d9e6f8c4d prepare for release 0.21 2022-08-17 17:30:11 +02:00
extrawurst
64f68fa668 remove old clippy ignores 2022-08-17 17:29:49 +02:00
extrawurst
594dd12864 fix 2022-08-17 16:56:54 +02:00
extrawurst
fa8d8aff62 update changelog 2022-08-17 16:38:46 +02:00
Stephan D
4a0e58ae15
msrv 160 (#1279)
* bump edition
* msrv 160
* upgrade clap and more deps
2022-08-17 16:30:54 +02:00
extrawurst
70e6a3db6e update 2022-08-15 13:00:50 +02:00
extrawurst
3ce715178b upgrade syntact to get rid of plist and #1275 2022-08-15 12:50:10 +02:00
extrawurst
32a9dbf43e upgrade 2022-08-06 17:00:19 +02:00
dependabot[bot]
8fd5d991bd Bump backtrace from 0.3.64 to 0.3.66
Bumps [backtrace](https://github.com/rust-lang/backtrace-rs) from 0.3.64 to 0.3.66.
- [Release notes](https://github.com/rust-lang/backtrace-rs/releases)
- [Commits](https://github.com/rust-lang/backtrace-rs/compare/0.3.64...0.3.66)

---
updated-dependencies:
- dependency-name: backtrace
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-08-05 15:49:51 +02:00
dependabot[bot]
6b424403b3 Bump thiserror from 1.0.30 to 1.0.32
Bumps [thiserror](https://github.com/dtolnay/thiserror) from 1.0.30 to 1.0.32.
- [Release notes](https://github.com/dtolnay/thiserror/releases)
- [Commits](https://github.com/dtolnay/thiserror/compare/1.0.30...1.0.32)

---
updated-dependencies:
- dependency-name: thiserror
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-08-05 15:49:23 +02:00
dependabot[bot]
0d9b6e3fef Bump serde from 1.0.136 to 1.0.142
Bumps [serde](https://github.com/serde-rs/serde) from 1.0.136 to 1.0.142.
- [Release notes](https://github.com/serde-rs/serde/releases)
- [Commits](https://github.com/serde-rs/serde/compare/v1.0.136...v1.0.142)

---
updated-dependencies:
- dependency-name: serde
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-08-05 15:48:56 +02:00
extrawurst
300e799aea update chrono dependency 2022-08-05 14:14:38 +02:00
Chigozie Joshua
edf798b91b
Refactor async tags (follow up PR) (#1245)
* AsyncTags
* Verify hash of last hash
* Make default Tags notification 'FinishUnchanged'
* fixed bug preventing tags from displaying

Co-authored-by: Martijn van Eijk <mfveijk@gmail.com>
Co-authored-by: extrawurst <mail@rusticorn.com>
2022-07-04 10:45:26 +02:00
extrawurst
1985fd2dbc clippy nighytly fix 2022-07-04 10:43:30 +02:00
extrawurst
c1cb868c4b fix nightly clippy 2022-07-02 08:17:15 +02:00
extrawurst
ad0ec7e9b8 add sparse repo limitation 2022-05-11 12:41:50 +02:00
ImgBotApp
0ab88f9cb2 [ImgBot] Optimize images
*Total -- 798.80kb -> 709.48kb (11.18%)

/assets/bad-credentials.png -- 125.33kb -> 68.70kb (45.18%)
/assets/termux-android.jpg -- 673.46kb -> 640.78kb (4.85%)

Signed-off-by: ImgBotApp <ImgBotHelp@gmail.com>
2022-04-25 22:10:52 +02:00
extrawurst
149841c604 doc comment 2022-04-24 22:34:50 +02:00
extrawurst
cce49a34b7 Revert "Change diff renamed files (#1040)"
This reverts commit 5f466ff983.
2022-04-24 22:30:40 +02:00
extrawurst
c3dbce1cd6 cargo fmt 2022-04-24 20:12:47 +02:00
Luka Markušić
638d7c2648
Sort fuzzy_matcher results based on score (#1183)
Co-authored-by: Stephan D <776816+extrawurst@users.noreply.github.com>
2022-04-24 20:11:26 +02:00
Gleb Davydov
5f466ff983
Change diff renamed files (#1040) 2022-04-24 19:25:50 +02:00
splitDEV
13afbf6bba
Add support for GIT_DIR and GIT_WORK_TREE environment variables (#1191)
* Use git env variables for git dir and git workdir

* Add changes to CHANGELOG.md

* Fix indentation

* Add link to PR

Co-authored-by: Stephan D <776816+extrawurst@users.noreply.github.com>
2022-04-24 19:12:00 +02:00
extrawurst
da531c61f6 added faq page 2022-04-24 18:58:50 +02:00
Stephan D
02efae1499
Fix stashlist after marked drop (#1207) 2022-04-23 19:01:15 +02:00
extrawurst
96aa346292 fix a bunch of links 2022-04-23 18:39:33 +02:00
dependabot[bot]
ece358fcc0 Bump anyhow from 1.0.56 to 1.0.57
Bumps [anyhow](https://github.com/dtolnay/anyhow) from 1.0.56 to 1.0.57.
- [Release notes](https://github.com/dtolnay/anyhow/releases)
- [Commits](https://github.com/dtolnay/anyhow/compare/1.0.56...1.0.57)

---
updated-dependencies:
- dependency-name: anyhow
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-04-22 11:10:17 +01:00
dependabot[bot]
bdf90e94da Bump log from 0.4.14 to 0.4.16
Bumps [log](https://github.com/rust-lang/log) from 0.4.14 to 0.4.16.
- [Release notes](https://github.com/rust-lang/log/releases)
- [Changelog](https://github.com/rust-lang/log/blob/master/CHANGELOG.md)
- [Commits](https://github.com/rust-lang/log/commits)

---
updated-dependencies:
- dependency-name: log
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-04-17 16:50:59 +01:00
extrawurst
3c27d4f9f5 nightly clippy fix 2022-04-17 16:47:00 +01:00
Philippe Eberli
b18cabf4d3 Fix URL to issue 846 2022-04-17 16:44:36 +01:00
dependabot[bot]
8dafa1266e Bump rayon-core from 1.9.1 to 1.9.2
Bumps [rayon-core](https://github.com/rayon-rs/rayon) from 1.9.1 to 1.9.2.
- [Release notes](https://github.com/rayon-rs/rayon/releases)
- [Changelog](https://github.com/rayon-rs/rayon/blob/master/RELEASES.md)
- [Commits](https://github.com/rayon-rs/rayon/commits)

---
updated-dependencies:
- dependency-name: rayon-core
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-04-14 18:05:21 +01:00
dependabot[bot]
505e01664f Bump crossbeam-channel from 0.5.3 to 0.5.4
Bumps [crossbeam-channel](https://github.com/crossbeam-rs/crossbeam) from 0.5.3 to 0.5.4.
- [Release notes](https://github.com/crossbeam-rs/crossbeam/releases)
- [Changelog](https://github.com/crossbeam-rs/crossbeam/blob/master/CHANGELOG.md)
- [Commits](https://github.com/crossbeam-rs/crossbeam/compare/crossbeam-channel-0.5.3...crossbeam-channel-0.5.4)

---
updated-dependencies:
- dependency-name: crossbeam-channel
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-03-21 08:32:06 +01:00
dependabot[bot]
c807ae4950 Bump which from 4.2.4 to 4.2.5
Bumps [which](https://github.com/harryfei/which-rs) from 4.2.4 to 4.2.5.
- [Release notes](https://github.com/harryfei/which-rs/releases)
- [Commits](https://github.com/harryfei/which-rs/compare/4.2.4...4.2.5)

---
updated-dependencies:
- dependency-name: which
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-03-21 08:26:05 +01:00
dependabot[bot]
fefe9450bd Bump git2 from 0.14.1 to 0.14.2
Bumps [git2](https://github.com/rust-lang/git2-rs) from 0.14.1 to 0.14.2.
- [Release notes](https://github.com/rust-lang/git2-rs/releases)
- [Commits](https://github.com/rust-lang/git2-rs/compare/git2-curl-0.14.1...0.14.2)

---
updated-dependencies:
- dependency-name: git2
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-03-16 10:32:18 +01:00
dependabot[bot]
3743983b70 Bump crossbeam-channel from 0.5.2 to 0.5.3
Bumps [crossbeam-channel](https://github.com/crossbeam-rs/crossbeam) from 0.5.2 to 0.5.3.
- [Release notes](https://github.com/crossbeam-rs/crossbeam/releases)
- [Changelog](https://github.com/crossbeam-rs/crossbeam/blob/master/CHANGELOG.md)
- [Commits](https://github.com/crossbeam-rs/crossbeam/compare/crossbeam-channel-0.5.2...crossbeam-channel-0.5.3)

---
updated-dependencies:
- dependency-name: crossbeam-channel
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-03-16 10:30:45 +01:00
Stephan D
2a578889f3
switch focus to index after staging last file (#1170) 2022-03-10 00:50:37 +01:00
extrawurst
dadf8ba269 fix nightly build 2022-03-09 21:34:31 +01:00
simonbusch
c2d57fca98 fix typo in readme. 2022-03-08 23:18:57 +01:00
dependabot[bot]
a620d1a01c Bump anyhow from 1.0.55 to 1.0.56
Bumps [anyhow](https://github.com/dtolnay/anyhow) from 1.0.55 to 1.0.56.
- [Release notes](https://github.com/dtolnay/anyhow/releases)
- [Commits](https://github.com/dtolnay/anyhow/compare/1.0.55...1.0.56)

---
updated-dependencies:
- dependency-name: anyhow
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-03-08 12:42:22 +01:00
dependabot[bot]
f1b2a43f53 Bump git2 from 0.14.0 to 0.14.1
Bumps [git2](https://github.com/rust-lang/git2-rs) from 0.14.0 to 0.14.1.
- [Release notes](https://github.com/rust-lang/git2-rs/releases)
- [Commits](https://github.com/rust-lang/git2-rs/compare/git2-curl-0.14.0...git2-curl-0.14.1)

---
updated-dependencies:
- dependency-name: git2
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-03-01 17:46:46 +01:00
dependabot[bot]
df7ed1a6d0 Bump anyhow from 1.0.54 to 1.0.55
Bumps [anyhow](https://github.com/dtolnay/anyhow) from 1.0.54 to 1.0.55.
- [Release notes](https://github.com/dtolnay/anyhow/releases)
- [Commits](https://github.com/dtolnay/anyhow/compare/1.0.54...1.0.55)

---
updated-dependencies:
- dependency-name: anyhow
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-02-25 21:04:55 +01:00
extrawurst
b0eaca72cb cargup update 2022-02-25 21:04:07 +01:00
extrawurst
3838fe74df cleanup 2022-02-25 10:58:52 +01:00
extrawurst
3347003fff do not advertise buymeacoffee anymore 2022-02-25 10:58:46 +01:00
extrawurst
970931abfc update changelog 2022-02-22 01:06:11 +01:00
dependabot[bot]
02170ee280 Bump serial_test from 0.5.1 to 0.6.0
Bumps [serial_test](https://github.com/palfrey/serial_test) from 0.5.1 to 0.6.0.
- [Release notes](https://github.com/palfrey/serial_test/releases)
- [Commits](https://github.com/palfrey/serial_test/compare/v0.5.1...v0.6.0)

---
updated-dependencies:
- dependency-name: serial_test
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-02-22 01:04:41 +01:00
dependabot[bot]
fa541fb797 Bump anyhow from 1.0.53 to 1.0.54
Bumps [anyhow](https://github.com/dtolnay/anyhow) from 1.0.53 to 1.0.54.
- [Release notes](https://github.com/dtolnay/anyhow/releases)
- [Commits](https://github.com/dtolnay/anyhow/compare/1.0.53...1.0.54)

---
updated-dependencies:
- dependency-name: anyhow
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-02-22 01:04:10 +01:00
PeroSar
d7f61ba321 README: Add Termux (Android) installation
Signed-off-by: PeroSar <perosar1111@gmail.com>
2022-02-22 01:01:16 +01:00
extrawurst
77472d086f change author name 2022-02-21 23:33:07 +01:00
dependabot[bot]
9db318ff1f Bump unicode-segmentation from 1.8.0 to 1.9.0
Bumps [unicode-segmentation](https://github.com/unicode-rs/unicode-segmentation) from 1.8.0 to 1.9.0.
- [Release notes](https://github.com/unicode-rs/unicode-segmentation/releases)
- [Commits](https://github.com/unicode-rs/unicode-segmentation/commits)

---
updated-dependencies:
- dependency-name: unicode-segmentation
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-02-08 09:17:20 +01:00
Stephan Dilly
86798008ed update changelog 2022-02-06 22:16:19 +01:00
Stephan Dilly
284c57fb72
generic popup stacking solution (#1124)
* generic popup stacking solution
* allow going back to file-revision popup
* do not select diff in coming back to files-revlog
* handle filetree popup via stacking
* allow going back to inspect commit
* allow coming back to compare/inspect commit
2022-02-06 22:13:05 +01:00
Stephan Dilly
e9d8de1be4 cargo update 2022-02-01 23:49:19 +01:00
Stephan Dilly
4f3ecfcd7c
fix left arrow closing popup (#1121)
* fix left arrow closing popup
* fix pageup/down being borked
* adhere to global diff options
* fix build for rust 1.50
* show revision count in header
* allow blaming any specific file revision
* show blame command
* allow opening history from blame
2022-01-31 20:56:59 +01:00
Stephan Dilly
750b45a6c4 fix changelog 2022-01-31 09:48:53 +01:00
Fernando Silva
2745d9f860 fix: blame tabs indentation 2022-01-31 09:47:56 +01:00
dependabot[bot]
6e0e4b57b5 Bump backtrace from 0.3.63 to 0.3.64
Bumps [backtrace](https://github.com/rust-lang/backtrace-rs) from 0.3.63 to 0.3.64.
- [Release notes](https://github.com/rust-lang/backtrace-rs/releases)
- [Commits](https://github.com/rust-lang/backtrace-rs/compare/0.3.63...0.3.64)

---
updated-dependencies:
- dependency-name: backtrace
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-01-31 08:53:35 +01:00
Stephan Dilly
9ace5357e1 cleanup 2022-01-31 00:40:22 +01:00
Stephan Dilly
457ea47457 file revlog: diff issues
* clear on open
* populate after log was loaded
2022-01-30 23:11:17 +01:00
Stephan Dilly
1013d3fa24 update changelog 2022-01-30 23:03:58 +01:00
Stephan Dilly
194fa6f2e7 perf improvement when fullscreen popup open
do not render tabs when fullscreen popup open
2022-01-30 23:00:00 +01:00
Stephan Dilly
ef78a3a8ba fix file log key events leaking through 2022-01-30 19:41:14 +01:00
Stephan Dilly
f2b09d39c6 clippy nightly fixes 2022-01-30 19:28:45 +01:00
Christoph Rüßler
b622ceef94
Add popup for file history (#841) 2022-01-30 18:50:50 +01:00
Stephan Dilly
3e72539ca1 fix typo 2022-01-27 23:37:22 +01:00
Stephan Dilly
e18aa48aea
fix issue with taglist component without remotes (#1112) 2022-01-27 21:56:54 +01:00
Stephan Dilly
11c055220d changelog 2022-01-26 10:01:55 +01:00
dependabot[bot]
ec8effd963 Bump serde from 1.0.135 to 1.0.136
Bumps [serde](https://github.com/serde-rs/serde) from 1.0.135 to 1.0.136.
- [Release notes](https://github.com/serde-rs/serde/releases)
- [Commits](https://github.com/serde-rs/serde/compare/v1.0.135...v1.0.136)

---
updated-dependencies:
- dependency-name: serde
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-01-26 09:56:43 +01:00
Stephan Dilly
fce9b90071
Fix 1104 performance regression (#1107) 2022-01-26 09:56:17 +01:00
Stephan Dilly
53e855e488 use auto proxy option 2022-01-26 09:54:48 +01:00
Stephan Dilly
bfba218fb4 bump versions 2022-01-26 00:35:05 +01:00
Stephan Dilly
6cf39a88f5
Fix 1102 performance reg (#1103) 2022-01-26 00:30:19 +01:00
Stephan Dilly
acb7e52696 fix windows deployment 2022-01-25 21:20:51 +01:00
Stephan Dilly
160bc6b99d prep for release 0.20 2022-01-25 19:52:51 +01:00
Stephan Dilly
98625272fb cargo update 2022-01-25 19:47:54 +01:00
dependabot[bot]
d08fb976e0 Bump anyhow from 1.0.52 to 1.0.53
Bumps [anyhow](https://github.com/dtolnay/anyhow) from 1.0.52 to 1.0.53.
- [Release notes](https://github.com/dtolnay/anyhow/releases)
- [Commits](https://github.com/dtolnay/anyhow/compare/1.0.52...1.0.53)

---
updated-dependencies:
- dependency-name: anyhow
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-01-25 12:01:40 +01:00
dependabot[bot]
d51f9c5d4b Bump serde from 1.0.133 to 1.0.135
Bumps [serde](https://github.com/serde-rs/serde) from 1.0.133 to 1.0.135.
- [Release notes](https://github.com/serde-rs/serde/releases)
- [Commits](https://github.com/serde-rs/serde/compare/v1.0.133...v1.0.135)

---
updated-dependencies:
- dependency-name: serde
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-01-25 12:01:23 +01:00
dependabot[bot]
390c65bf37 Bump which from 4.2.2 to 4.2.4
Bumps [which](https://github.com/harryfei/which-rs) from 4.2.2 to 4.2.4.
- [Release notes](https://github.com/harryfei/which-rs/releases)
- [Commits](https://github.com/harryfei/which-rs/compare/4.2.2...4.2.4)

---
updated-dependencies:
- dependency-name: which
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-01-25 12:01:10 +01:00
Stephan Dilly
317b245ba0
fix credential.helper config usage inside repo (#1091) 2022-01-24 00:36:58 +01:00
Stephan Dilly
9653545c81 debug print sideband progress 2022-01-22 18:53:59 +01:00
Stephan Dilly
614040b854 add note 2022-01-22 18:50:51 +01:00
Stephan Dilly
14c79c12a6 use @ and style as fat tag symbol 2022-01-22 17:59:11 +01:00
Stephan Dilly
8db28d485a add git-lfs limitation 2022-01-22 16:47:47 +01:00
Stephan Dilly
36e2c4a86e update 1.0 goals 2022-01-22 15:12:21 +01:00
Stephan Dilly
47bc83f243 Revert "cargo update"
This reverts commit 8b00d6756c.
2022-01-22 14:58:06 +01:00
Stephan Dilly
8b00d6756c cargo update 2022-01-22 14:53:35 +01:00
Stephan Dilly
12fbfa0946 prepare for v0.20 release 2022-01-22 01:27:20 +01:00
Stephan Dilly
0874f33eb3 update changelog 2022-01-22 01:21:21 +01:00
Stephan Dilly
166826f5ea allow inspecting tag annotation 2022-01-22 01:21:15 +01:00
Stephan Dilly
a1f3931b59 cargo update 2022-01-21 21:20:47 +01:00
Stephan Dilly
435de9cda3
support hookspath (#1054) 2022-01-17 15:06:54 +01:00
Stephan Dilly
449dc43a5f update 2022-01-17 13:11:30 +01:00
Stephan Dilly
212160a4cb
Test fix cargo wix (#1080) 2022-01-17 13:08:55 +01:00
Stephan Dilly
b51734cce8 changelog gif 2022-01-16 23:04:31 +01:00
Stephan Dilly
13b6b2fdc6
support deleting tag on remote (#1079) 2022-01-16 22:56:39 +01:00
Stephan Dilly
060380fdd6 cargo update 2022-01-16 20:52:33 +01:00
Stephan Dilly
a3d4f2bd5f revert pprof upgrade 2022-01-12 13:17:08 +01:00
Stephan Dilly
9118ed1f30 cargo update tempfile 2022-01-12 12:52:35 +01:00
Stephan Dilly
3fdf5ba618 update changelog 2022-01-12 12:49:32 +01:00
Stephan Dilly
132559ea7f
support annotated tags (#1073) 2022-01-12 12:44:34 +01:00
dependabot[bot]
d6ace56288 Bump simplelog from 0.11.1 to 0.11.2
Bumps [simplelog](https://github.com/drakulix/simplelog.rs) from 0.11.1 to 0.11.2.
- [Release notes](https://github.com/drakulix/simplelog.rs/releases)
- [Changelog](https://github.com/Drakulix/simplelog.rs/blob/master/CHANGELOG.md)
- [Commits](https://github.com/drakulix/simplelog.rs/compare/v0.11.1...v0.11.2)

---
updated-dependencies:
- dependency-name: simplelog
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-01-11 09:04:44 +01:00
Stephan Dilly
bc0895773c link 2022-01-11 00:11:16 +01:00
dependabot[bot]
25cffab65b Bump crossbeam-channel from 0.5.1 to 0.5.2
Bumps [crossbeam-channel](https://github.com/crossbeam-rs/crossbeam) from 0.5.1 to 0.5.2.
- [Release notes](https://github.com/crossbeam-rs/crossbeam/releases)
- [Changelog](https://github.com/crossbeam-rs/crossbeam/blob/master/CHANGELOG.md)
- [Commits](https://github.com/crossbeam-rs/crossbeam/compare/crossbeam-channel-0.5.1...crossbeam-channel-0.5.2)

---
updated-dependencies:
- dependency-name: crossbeam-channel
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-01-10 09:57:07 +01:00
Stephan Dilly
a6ad95a04a relax stale bot 2022-01-09 19:42:58 +01:00
Stephan Dilly
ec6e5987ad fix h for help keybinding in keyconfig example 2022-01-09 17:00:04 +01:00
Stephan Dilly
b6429bc330
do not allow pull on local-only branch (#1067) 2022-01-09 16:31:20 +01:00
Stephan Dilly
1fd81b463e revert commit gif 2022-01-08 00:12:13 +01:00
dependabot[bot]
6b83956138
Bump serde from 1.0.132 to 1.0.133 (#1060)
Bumps [serde](https://github.com/serde-rs/serde) from 1.0.132 to 1.0.133.
- [Release notes](https://github.com/serde-rs/serde/releases)
- [Commits](https://github.com/serde-rs/serde/compare/v1.0.132...v1.0.133)

---
updated-dependencies:
- dependency-name: serde
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-01-05 22:46:19 +01:00
Stephan Dilly
6f70652d42 update licenses 2022-01-02 11:33:52 +01:00
Stephan Dilly
d5d36de01e
implement reverting commit from revlog (#1057) 2021-12-29 16:40:22 +01:00
Stephan Dilly
508ee35ce5 update roadmap 2021-12-29 11:19:42 +01:00
Stephan Dilly
566c449384 revert pprof 2021-12-25 21:57:04 +01:00
uniqueNullptr2
e7f15ae457
Disable fetch,pull,push when no remote defined (#1051) 2021-12-25 21:37:30 +01:00
Stephan Dilly
4b03e92cb9 cargo upgrades 2021-12-25 21:27:24 +01:00
dependabot[bot]
2c5bd289bf Bump anyhow from 1.0.51 to 1.0.52
Bumps [anyhow](https://github.com/dtolnay/anyhow) from 1.0.51 to 1.0.52.
- [Release notes](https://github.com/dtolnay/anyhow/releases)
- [Commits](https://github.com/dtolnay/anyhow/compare/1.0.51...1.0.52)

---
updated-dependencies:
- dependency-name: anyhow
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2021-12-25 20:41:13 +01:00
dependabot[bot]
8dc669893f Bump serde from 1.0.131 to 1.0.132
Bumps [serde](https://github.com/serde-rs/serde) from 1.0.131 to 1.0.132.
- [Release notes](https://github.com/serde-rs/serde/releases)
- [Commits](https://github.com/serde-rs/serde/compare/v1.0.131...v1.0.132)

---
updated-dependencies:
- dependency-name: serde
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2021-12-21 18:37:28 +01:00
Stephan Dilly
d790b48f5d fix nightly clippy 2021-12-19 21:05:05 +01:00
Stephan Dilly
10b4704662
run hooks in correct pwd (#1049)
see #1046
2021-12-17 00:46:08 +01:00
Stephan Dilly
cef0de4f0e changelog 2021-12-15 15:34:50 +01:00
Stephan Dilly
55b9877e93 use .git path instead of workdir finding hooks
fixes #1046
2021-12-15 15:33:09 +01:00
Stephan Dilly
12f3d97ef0 cargo update 2021-12-15 10:29:01 +01:00
Christian Zangl
70ce3f88d6
add gg/G to vim config (#1041)
see #1039
2021-12-12 18:14:02 +01:00
Stephan Dilly
661e429082 cargo upgrade 2021-12-10 23:31:30 +01:00
Martin Kühl
5550415e3c
cred: check pushurl before checking url (#967)
* cred: check pushurl before checking url

if a remote has both a pushurl and a url, need_username_password should check the pushurl before checking the url since we might not need credentials for the former
2021-12-09 22:47:27 +01:00
Stephan Dilly
9eb30ecb4c clarify colors being defined by your terminal 2021-12-09 21:53:59 +01:00
Stephan Dilly
7c11800250 improved documentation on key bindings overwrites 2021-12-09 21:24:08 +01:00
Stephan Dilly
6249491484
keep commit msg on hook-fail (#1036)
fixes #1035
2021-12-09 21:12:31 +01:00
Stephan Dilly
cd639b29c0
release 0.19 (#1033) 2021-12-08 23:24:01 +01:00
Stephan Dilly
eddc703fb8 fix windows build 2021-12-08 22:24:56 +01:00
Stephan Dilly
a947ce35e7
fix status fetch hanging on bare repos w/o worktree (#1032)
closes #1029
2021-12-08 22:00:55 +01:00
dependabot[bot]
0a6ca35fc7 Bump itertools from 0.10.1 to 0.10.3
Bumps [itertools](https://github.com/rust-itertools/itertools) from 0.10.1 to 0.10.3.
- [Release notes](https://github.com/rust-itertools/itertools/releases)
- [Changelog](https://github.com/rust-itertools/itertools/blob/master/CHANGELOG.md)
- [Commits](https://github.com/rust-itertools/itertools/compare/v0.10.1...v0.10.3)

---
updated-dependencies:
- dependency-name: itertools
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2021-12-07 08:11:54 +01:00
Stephan Dilly
cb1d801be0 finally lifted a limitation 2021-12-05 00:38:54 +01:00
Stephan Dilly
006cdd6373
support bare repos (#1028) 2021-12-05 00:35:45 +01:00
Stephan Dilly
ecabee02da allow opening file selected in tree (#989) 2021-12-02 08:24:13 +01:00
Stephan Dilly
36699a0a36
git2rs upgrade (#1025)
closes #881
2021-12-01 19:32:48 +01:00
Stephan Dilly
328061dec6 file rename 2021-12-01 09:13:05 +01:00
Stephan Dilly
5a7c85daee rename 2021-12-01 09:12:39 +01:00
Stephan Dilly
8fb506520c cargo upgrades 2021-12-01 09:10:24 +01:00
Stephan Dilly
f4ee24055b Add debug log 2021-11-29 23:28:36 +01:00
dependabot[bot]
5c94c18c7b Bump anyhow from 1.0.49 to 1.0.50
Bumps [anyhow](https://github.com/dtolnay/anyhow) from 1.0.49 to 1.0.50.
- [Release notes](https://github.com/dtolnay/anyhow/releases)
- [Commits](https://github.com/dtolnay/anyhow/compare/1.0.49...1.0.50)

---
updated-dependencies:
- dependency-name: anyhow
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2021-11-29 08:22:24 +01:00
Stephan Dilly
74c3d6f21c fetch all branches fetches tags aswell 2021-11-28 16:54:37 +01:00
Stephan Dilly
26318c10e9 cargo updates 2021-11-28 14:41:23 +01:00
Stephan Dilly
304c0a3a6b check cargo sort in ci 2021-11-28 14:29:38 +01:00
Stephan Dilly
a847ea2a92 specify changelog better 2021-11-28 14:29:01 +01:00
Stephan Dilly
444e14631d cargo-sort 2021-11-28 13:38:48 +01:00
Stephan Dilly
9038b1d07d pull gets tags on current branch (closes #1013) 2021-11-28 13:37:36 +01:00
Stephan Dilly
3bc4feb2ac enable but ignore certain audits 2021-11-24 14:27:49 +01:00
Stephan Dilly
4d05dea4a6 update changelog 2021-11-23 22:16:15 +01:00
Stephan Dilly
33ac72c8e7
fetch/prune branches (#1000) 2021-11-23 22:14:37 +01:00
dependabot[bot]
ee7ec691f0 Bump anyhow from 1.0.47 to 1.0.48
Bumps [anyhow](https://github.com/dtolnay/anyhow) from 1.0.47 to 1.0.48.
- [Release notes](https://github.com/dtolnay/anyhow/releases)
- [Commits](https://github.com/dtolnay/anyhow/compare/1.0.47...1.0.48)

---
updated-dependencies:
- dependency-name: anyhow
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2021-11-23 08:06:30 +01:00
Stephan Dilly
d285ec0b09 disable sec audit temporarily 2021-11-22 23:51:59 +01:00
Stephan Dilly
cbf53d6013 update main UI aswell to show new branch 2021-11-22 23:04:49 +01:00
ImgBotApp
8466b39226 [ImgBot] Optimize images
/assets/rebase.png -- 509.84kb -> 274.62kb (46.14%)

Signed-off-by: ImgBotApp <ImgBotHelp@gmail.com>
2021-11-22 01:12:58 +01:00
Stephan Dilly
21a10a618a screencast 2021-11-22 00:04:18 +01:00
Stephan Dilly
ab908f83df contributors 2021-11-21 22:16:42 +01:00
Stephan Dilly
d617ba76fa simplify key bindings file handling (#946) 2021-11-21 22:10:37 +01:00
Stephan Dilly
f689c428b2 upgrade pprof 2021-11-21 22:05:30 +01:00
Curiosidad-Racional
38cf196b62
File find popup up/down keys resolves #977 (#993) 2021-11-21 20:10:14 +01:00
Stephan Dilly
1d409d31ab simplelog upgrade 2021-11-20 19:04:12 +01:00
Stephan Dilly
cd3989238b changelog update 2021-11-20 18:54:24 +01:00
Stephan Dilly
95b2737d9b document new symbol overwrite feature 2021-11-20 18:53:05 +01:00
Stephan Dilly
3db1a68515
allow config for key symbols (#997)
closes #465
2021-11-20 18:44:04 +01:00
Stephan Dilly
e548e8c75f cargo update 2021-11-20 01:41:28 +01:00
Stephan Dilly
dbf7661521 cargo update 2021-11-17 09:19:49 +01:00
Stephan Dilly
11d1f43830 use new msrv field in cargo.toml 2021-11-14 15:43:02 +01:00
Stephan Dilly
a55dcf62a3
blame: fixup windows paths (#984)
closes #981
2021-11-12 15:52:29 +01:00
Stephan Dilly
3c8eb7e049
improve files in diff speed (#979)
closes #976
2021-11-11 13:36:17 +01:00
Jakub Jirutka
fa7cd37ca7 Make gh-emoji optional
gh-emoji crate includes *images* of GitHub's emoji - this is quite a big
dependency. It increases the binary size by 1 MiB; that's +25 % when
building v0.18.0 on Alpine Linux with build flags to optimize size.
I consider it an unnecessary bloat that should be optional.
2021-11-10 18:07:48 +01:00
Stephan Dilly
0360bdf080 cargo upgrade 2021-11-09 14:23:45 +01:00
Stephan Dilly
c42c9f900a nightly clippy fix 2021-11-09 14:00:41 +01:00
dependabot[bot]
ae15bb41d3 Bump backtrace from 0.3.62 to 0.3.63
Bumps [backtrace](https://github.com/rust-lang/backtrace-rs) from 0.3.62 to 0.3.63.
- [Release notes](https://github.com/rust-lang/backtrace-rs/releases)
- [Commits](https://github.com/rust-lang/backtrace-rs/compare/0.3.62...0.3.63)

---
updated-dependencies:
- dependency-name: backtrace
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2021-11-02 10:00:09 +01:00
Stephan Dilly
d7c5bc583f cargo update 2021-11-01 12:10:39 +01:00
dependabot[bot]
056ffb166d Bump ron from 0.6.6 to 0.7.0
Bumps [ron](https://github.com/ron-rs/ron) from 0.6.6 to 0.7.0.
- [Release notes](https://github.com/ron-rs/ron/releases)
- [Changelog](https://github.com/ron-rs/ron/blob/master/CHANGELOG.md)
- [Commits](https://github.com/ron-rs/ron/compare/v0.6.6...v0.7.0)

---
updated-dependencies:
- dependency-name: ron
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2021-10-25 22:36:20 +02:00
Stephan Dilly
18e61efae6 fix changelog format 2021-10-24 19:57:35 +02:00
Stephan Dilly
389bd75d46 honor options in stage_all command (see #933) 2021-10-23 15:34:49 +02:00
Stephan Dilly
5c661be159 support home/end keys in branchlist (fixes #957) 2021-10-23 14:39:02 +02:00
Stephan Dilly
dee97a77c4 cargo update 2021-10-23 14:29:18 +02:00
Stephan Dilly
271d3f696c cargo update 2021-10-15 18:54:57 +02:00
Gleb Davydov
899168e1ce
Add highlighting for matches in fuzzy finder (#947)
closes #893
2021-10-15 17:23:57 +02:00
R0nd
153c79a828
support pull from remote (#945)
closes #920
2021-10-13 15:34:58 +02:00
dm9pZCAq
31f677187b
add trace-libgit feature (#905) 2021-10-13 15:32:17 +02:00
Stephan Dilly
d6a2af89ee branch deletion better distinguish local vs remote 2021-10-13 11:55:21 +02:00
Stephan Dilly
035870e52f undo ahash upgrade breaking rust 1.50 compat 2021-10-11 11:37:30 +02:00
Stephan Dilly
d1dcdab288 version bumps 2021-10-11 11:25:50 +02:00
Stephan Dilly
cd3a1e2793 branchlist: do not consume more-key (fixes #944) 2021-10-11 11:14:25 +02:00
Stephan Dilly
39400aac92
pending merge: more obvious repo state visualization (#943)
fixes #925
2021-10-11 11:08:34 +02:00
Stephan Dilly
d78b029c21 cargo update 2021-10-11 11:02:27 +02:00
Alessandro Menezes
6ae5b5e2b2
Add specific key bindings to stage / unstage items (#930)
* Add specific key bindings to stage / unstage items

Feature requested by Issue #909.

* Updating Help popup with stage / unstage keys

* Merging staging and unstaging into a single `stage_unstage_item` key

* Add/remove hunk now use the `stage_unstage_item` key
2021-10-10 02:58:21 +02:00
Alessandro Menezes
8486233053 Fixing the CI Pipeline
Rust nightly (1.57.0-nightly) was throwing errors when running `cargo clippy`.
2021-10-10 02:57:45 +02:00
Alessandro Menezes
d84120a895
Switch to status tab after merge / rebase with conflicts (#932) 2021-10-07 09:33:30 +02:00
Stephan Dilly
c6abbaf4d4
fix hirarchical branch names (#931) 2021-10-06 10:50:57 +02:00
Stephan Dilly
8596e01dcd cargo update 2021-10-06 10:40:13 +02:00
Stephan Dilly
d359fabe7b clippy fixes 2021-09-30 23:30:54 +02:00
R0nd
48f9331b0d
Use git_message_prettify (#924) 2021-09-30 23:20:43 +02:00
Stephan Dilly
5f34e9cc07 updated changelog 2021-09-29 18:59:25 +02:00
Stephan Dilly
e4c7867564
allow rebase with conflicts (#897) 2021-09-29 18:55:47 +02:00
Stephan Dilly
9f8fc6b907 cleanup 2021-09-29 17:38:26 +02:00
Stephan Dilly
23516f1a41 docs 2021-09-29 17:35:47 +02:00
Stephan Dilly
f8bad7d541 cleanup 2021-09-29 17:07:59 +02:00
Scott Reeves
07f5d69a48 Improve contrast in branch popup for selected branch 2021-09-29 16:36:10 +02:00
Stephan Dilly
b73b20072f cargo update 2021-09-29 12:03:16 +02:00
dependabot[bot]
8030ac8867 Bump pretty_assertions from 0.7.2 to 1.0.0
Bumps [pretty_assertions](https://github.com/colin-kiegel/rust-pretty-assertions) from 0.7.2 to 1.0.0.
- [Release notes](https://github.com/colin-kiegel/rust-pretty-assertions/releases)
- [Changelog](https://github.com/colin-kiegel/rust-pretty-assertions/blob/main/CHANGELOG.md)
- [Commits](https://github.com/colin-kiegel/rust-pretty-assertions/compare/v0.7.2...v1.0.0)

---
updated-dependencies:
- dependency-name: pretty_assertions
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2021-09-29 07:46:42 +02:00
Stephan Dilly
d2bdec43a1 cleanup 2021-09-26 10:40:55 +02:00
Stephan Dilly
05dd8eff1d cargo fmt 2021-09-25 15:51:33 +02:00
Stephan Dilly
915cece734 cleanup some lints 2021-09-25 15:39:54 +02:00
Stephan Dilly
d541d0d9f8
make fetch more error resilient (#915)
fixing #911
2021-09-24 13:01:55 +02:00
Stephan Dilly
5d01da1c93 cargo update 2021-09-24 12:31:25 +02:00
dependabot[bot]
1bc137ee35 Bump simplelog from 0.10.0 to 0.10.1
Bumps [simplelog](https://github.com/drakulix/simplelog.rs) from 0.10.0 to 0.10.1.
- [Release notes](https://github.com/drakulix/simplelog.rs/releases)
- [Changelog](https://github.com/Drakulix/simplelog.rs/blob/master/CHANGELOG.md)
- [Commits](https://github.com/drakulix/simplelog.rs/compare/v0.10.0...v0.10.1)

---
updated-dependencies:
- dependency-name: simplelog
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2021-09-23 06:57:31 +03:00
dependabot[bot]
da99aa07bd Bump openssl-sys from 0.9.66 to 0.9.67
Bumps [openssl-sys](https://github.com/sfackler/rust-openssl) from 0.9.66 to 0.9.67.
- [Release notes](https://github.com/sfackler/rust-openssl/releases)
- [Commits](https://github.com/sfackler/rust-openssl/compare/openssl-sys-v0.9.66...openssl-sys-v0.9.67)

---
updated-dependencies:
- dependency-name: openssl-sys
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2021-09-22 10:05:43 +03:00
dependabot[bot]
4993cc8f35 Bump unicode-width from 0.1.8 to 0.1.9
Bumps [unicode-width](https://github.com/unicode-rs/unicode-width) from 0.1.8 to 0.1.9.
- [Release notes](https://github.com/unicode-rs/unicode-width/releases)
- [Commits](https://github.com/unicode-rs/unicode-width/compare/v0.1.8...v0.1.9)

---
updated-dependencies:
- dependency-name: unicode-width
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2021-09-17 14:15:55 +03:00
dependabot[bot]
99bdb46795 Bump anyhow from 1.0.43 to 1.0.44
Bumps [anyhow](https://github.com/dtolnay/anyhow) from 1.0.43 to 1.0.44.
- [Release notes](https://github.com/dtolnay/anyhow/releases)
- [Commits](https://github.com/dtolnay/anyhow/compare/1.0.43...1.0.44)

---
updated-dependencies:
- dependency-name: anyhow
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2021-09-17 13:19:34 +03:00
Stephan Dilly
fa051e9226 nightly clippy fixes 2021-09-17 09:14:28 +00:00
Stephan Dilly
8729530ba9 nightly clippy fixes 2021-09-10 11:19:32 +02:00
Stephan Dilly
33344e93e2 add keybinding notes to changelog 2021-09-10 10:17:33 +02:00
Stephan Dilly
fc4ed855d6 cargo update 2021-09-10 10:14:30 +02:00
Stephan Dilly
7dd6b4e8d8 fix release date 2021-09-10 10:03:56 +02:00
Stephan Dilly
1a8caa32d4 cargo clippy 2021-09-10 10:01:03 +02:00
ImgBotApp
23e52b7c87 [ImgBot] Optimize images
/assets/emojified-commit-message.png -- 367.67kb -> 298.78kb (18.74%)

Signed-off-by: ImgBotApp <ImgBotHelp@gmail.com>
2021-09-09 11:56:43 +02:00
Stephan Dilly
22037f8991 prepare for release 2021-09-09 11:34:01 +02:00
Stephan Dilly
495b0acbb7 smaller image 2021-09-09 11:06:29 +02:00
Stephan Dilly
417db6ed6c changelog 2021-09-09 11:04:33 +02:00
Stephan Dilly
a12180e2e2 version bumps 2021-09-09 10:14:16 +02:00
Stephan Dilly
28a390cfdd bump git2rs to close #853 2021-09-08 17:10:00 +02:00
Stephan Dilly
62229f4b81
Update README.md 2021-09-07 00:21:25 +02:00
Stephan Dilly
f27227af41
Conflict free rebase (#896)
* unittest for rebasing with conflicts
* hide branchlist after rebase
2021-09-07 00:04:54 +02:00
Stephan Dilly
bf56d3bff2 changelog 2021-09-06 23:09:19 +02:00
Stephan Dilly
cfea72d709 rebase returns generated commit + unittest 2021-09-06 21:20:45 +02:00
Stephan Dilly
b70ea92608 show find command (closes #891) 2021-09-06 14:33:08 +02:00
Stephan Dilly
1e7258b617 cargo update 2021-09-05 10:51:36 +02:00
Stephan Dilly
6be4da873b version bumps 2021-09-05 10:46:27 +02:00
Stephan Dilly
6d6f60349a
More fuzzy finder (#892)
* allow selecting entries in fuzzy finder
* fix fuzzy finder also in files popup
* changelog
2021-09-04 23:56:34 +02:00
Stephan Dilly
26a9aaacf5 rename 2021-09-04 13:28:23 +02:00
Stephan Dilly
24217a12c3 typos 2021-09-04 13:26:50 +02:00
Stephan Dilly
fb2b990072
find files via fuzzy finder (#890) 2021-09-04 10:50:03 +02:00
Stephan Dilly
3b5d43ecb2 cleanup 2021-09-03 00:20:38 +02:00
Stephan Dilly
aaf0e4cfbf &str instead of weird &String 2021-09-02 19:19:09 +02:00
Stephan Dilly
6f2157c1d8 missing changelog (closes #889) 2021-09-02 18:55:20 +02:00
Stephan Dilly
40e03ba7de allow async jobs to set intermediate progress 2021-09-02 18:29:03 +02:00
Stephan Dilly
0454e2a1cd asyncjob supports sending arbitrary notifications
this is used to send progress reports during work on the job
2021-09-02 13:14:36 +02:00
Stephan Dilly
b9e4631ff4 allow async job to return dynamic notification 2021-09-02 11:36:46 +02:00
Stephan Dilly
e5688e3b8b make async test even more resilient 2021-09-02 10:38:37 +02:00
Stephan Dilly
2a2751bf2a fix clippy nightly 2021-09-02 09:15:41 +02:00
Stephan Dilly
8905fd22ea make test more stable 2021-09-01 23:02:08 +02:00
Stephan Dilly
777d362dfc less unneeded sleep 2021-08-31 18:12:09 +02:00
andrewpollack
3a1c1a6b99
merge branch closes window (#882)
fixes #876
2021-08-30 12:16:37 +02:00
Stephan Dilly
361e1d86e9 cargo update 2021-08-30 12:15:18 +02:00
Stephan Dilly
26a3b65312 fix clippy nightly 2021-08-30 12:15:05 +02:00
Stephan Dilly
63ab78ffb7 add missing edit in changelog (#880) 2021-08-28 14:38:04 +02:00
Stephan Dilly
084be75aa3 cargo update 2021-08-28 14:35:32 +02:00
Ashvin Arsakularatne
e275caa872 add scrollbar to revlog 2021-08-28 14:32:12 +02:00
Stephan Dilly
9c7ac0f84d some more immutable string optimizations
and precompute diff line trimming (newlines)
2021-08-27 09:40:45 +02:00
Stephan Dilly
8353dfdd36 use less memory per hunk (immutable string) 2021-08-27 09:31:31 +02:00
Stephan Dilly
a8654329ec allow rebase of a branch (#816) 2021-08-26 20:31:37 +02:00
Stephan Dilly
f51a3a953a todo 2021-08-26 19:27:39 +02:00
Stephan Dilly
6524af65a1 fix filetree content not showing tabs (fixes #874) 2021-08-24 23:02:23 +02:00
ImgBotApp
e500302b8b [ImgBot] Optimize images
/assets/emojified-commit-message.png -- 258.45kb -> 169.83kb (34.29%)

Signed-off-by: ImgBotApp <ImgBotHelp@gmail.com>
2021-08-23 12:24:11 +02:00
andrewpollack
d3a6fdd2e7
Issue/768/support markdown emoji (#866) 2021-08-23 12:09:00 +02:00
Stephan Dilly
fa1a1c8727 fix tag commit msg in taglist (closes #871) 2021-08-22 23:24:30 +02:00
Stephan Dilly
6b9e211f34 upgrade profiler dependency 2021-08-22 12:54:04 +02:00
Stephan Dilly
60d512a5b3 fix capacity 2021-08-22 12:53:53 +02:00
Stephan Dilly
7e9c988f50 cargo update 2021-08-22 12:30:23 +02:00
Stephan Dilly
70e1e31076 update changelog 2021-08-22 11:54:46 +02:00
Stephan Dilly
49a1855cbb dont use process abort on input thread error (#823)
rather perform a graceful shutdown
2021-08-22 00:25:04 +02:00
Stephan Dilly
eef1a79375 prepare release 2021-08-21 16:23:06 +02:00
Stephan Dilly
81924fb9f8 show subject line of commits compared 2021-08-21 16:11:33 +02:00
Stephan Dilly
3db3b95dd0 fix order of old/new in compare 2021-08-21 15:56:21 +02:00
Stephan Dilly
5672cfd033
compare two commits (#860) 2021-08-21 15:49:39 +02:00
Stephan Dilly
e35b196db6 gettings started with rust link 2021-08-21 15:34:50 +02:00
Stephan Dilly
a5c026378d cargo update 2021-08-21 01:03:54 +02:00
Stephan Dilly
dafbb4c4f3 added one more PR checklist item 2021-08-20 14:15:04 +02:00
Stephan Dilly
68efbe20d7 PR checklist 2021-08-20 14:08:43 +02:00
Stephan Dilly
f23e8df69a update changelog 2021-08-20 14:02:51 +02:00
jedel1043
13a0f4e9e2
Display mark for remote branches with tracking branches (#861) 2021-08-20 13:03:02 +02:00
Stephan Dilly
1faba1760c cleanup 2021-08-19 22:28:57 +02:00
Stephan Dilly
bc611bca52 allow inspecting top commit in branchlist 2021-08-19 22:20:54 +02:00
Stephan Dilly
6abc7d297f changelog 2021-08-19 11:06:10 +02:00
Stephan Dilly
f7ecc0e194 do not fetch filetree on empty repo (closes #859) 2021-08-19 11:05:20 +02:00
imgbot[bot]
2402777427
[ImgBot] Optimize images (#858)
/assets/options.gif -- 255.96kb -> 202.70kb (20.81%)

Signed-off-by: ImgBotApp <ImgBotHelp@gmail.com>

Co-authored-by: ImgBotApp <ImgBotHelp@gmail.com>
2021-08-19 09:05:16 +02:00
Stephan Dilly
7cc19f673a
support options for the way we calculate the status (#849) 2021-08-19 02:19:36 +02:00
dependabot[bot]
923323bd71 Bump openssl-sys from 0.9.65 to 0.9.66
Bumps [openssl-sys](https://github.com/sfackler/rust-openssl) from 0.9.65 to 0.9.66.
- [Release notes](https://github.com/sfackler/rust-openssl/releases)
- [Commits](https://github.com/sfackler/rust-openssl/compare/openssl-sys-v0.9.65...openssl-sys-v0.9.66)

---
updated-dependencies:
- dependency-name: openssl-sys
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2021-08-18 09:25:30 +02:00
ImgBotApp
ee9a8a6a9f [ImgBot] Optimize images
*Total -- 437.73kb -> 415.90kb (4.99%)

/assets/drop-multiple-stashes.gif -- 103.58kb -> 98.00kb (5.38%)
/assets/branch-validation.gif -- 334.16kb -> 317.90kb (4.86%)

Signed-off-by: ImgBotApp <ImgBotHelp@gmail.com>
2021-08-18 01:55:38 +02:00
Stephan Dilly
71e5cf1726 resize 2021-08-18 01:53:00 +02:00
Stephan Dilly
2b85b81a3e
drop multiple stashes (#855) 2021-08-18 01:41:33 +02:00
Stephan Dilly
5c694bd696 improve marker styling 2021-08-17 17:54:13 +02:00
Stephan Dilly
3847ec92cf distinct marked commits styling 2021-08-17 14:51:27 +02:00
Stephan Dilly
dad8e8d43d cargo fmt: use hardtabs
since it does not support hard-whitespaces its the only way to make whitespaces consisitent and checked
2021-08-17 14:24:25 +02:00
Stephan Dilly
25a49e22f2 supporting marking commits in the log 2021-08-17 14:21:24 +02:00
Stephan Dilly
a4bdfd16c1 cargo update 2021-08-17 13:45:19 +02:00
Stephan Dilly
0b48e6f4ff
fix tags being fetched every scroll in revlog (#851) 2021-08-17 13:35:59 +02:00
Stephan Dilly
55f224c5ca fix nightly 2021-08-17 13:14:33 +02:00
Stephan Dilly
274dedb5e8 todo 2021-08-15 18:20:44 +02:00
Stephan Dilly
8493ab47e6 update changelog 2021-08-15 18:15:02 +02:00
Stephan Dilly
d4db614a01 update branch list if a push notification arrives 2021-08-15 18:13:54 +02:00
zcorniere
56502ad3fd
ability to delete remote branch (#838)
* added ability to delete remote branch (closes #622)
2021-08-15 17:44:55 +02:00
Stephan Dilly
62ea1dea04 fix progress 0/0 panic 2021-08-15 17:42:25 +02:00
Stephan Dilly
47ff93734e workaround tui-rs issue 2021-08-15 17:18:06 +02:00
Stephan Dilly
047939ba6e update changelog 2021-08-15 15:15:08 +02:00
Stephan Dilly
d42b00389c
check branch name validity while typing (#842)
closes #559
2021-08-15 15:14:35 +02:00
Stephan Dilly
e16dfcaee7
hook into libgit2 tracing (#822) 2021-08-15 14:24:20 +02:00
Stephan Dilly
71b398e858 cargo update 2021-08-15 13:48:26 +02:00
Stephan Dilly
71e3d9a9b8 get_commit_diff on commit with unknown parent (#836) 2021-08-11 13:25:38 +02:00
Stephan Dilly
29f71f50d4 do not allow to ignore gitignore (fixes #825) 2021-08-03 23:47:41 +02:00
Stephan Dilly
d6f43f063e cargo upgrade 2021-08-03 23:00:59 +02:00
Stephan Dilly
0f896933a5 bump tui/crossterm 2021-08-03 22:56:39 +02:00
Philippe
c0022d4eea
Fix the path of the theme file on mac (#819) 2021-07-17 10:31:20 +02:00
Benedykt Jaworski
36e9f7754b
vim style config: allow Q in pop-up inputs (#818)
Change `exit` (which exits the app regardless of context) to <kbd>Ctrl</kbd>+<kbd>C</kbd> (which doesn’t cause quitting on upper-case `Q` insert in popup inputs, cf. #771).
2021-07-15 16:34:00 +02:00
Stephan Dilly
8f12010aa8 use new label to close stale issues (closes #815) 2021-07-12 15:50:41 +02:00
Stephan Dilly
c9a6978fe0 update roadmap to 1.0 2021-07-12 14:38:26 +02:00
Stephan Dilly
19f2fd03cf
app has its own async notifications now (#813)
* app has its own async notifications now
2021-07-11 13:24:19 +02:00
260 changed files with 50280 additions and 25860 deletions

8
.cargo/config.toml Normal file
View file

@ -0,0 +1,8 @@
[target.aarch64-unknown-linux-gnu]
linker = "aarch64-linux-gnu-gcc"
[target.arm-unknown-linux-gnueabihf]
linker = "arm-linux-gnueabihf-gcc"
[target.armv7-unknown-linux-gnueabihf]
linker = "arm-linux-gnueabihf-gcc"

View file

@ -1,2 +1,2 @@
msrv = "1.50.0"
cognitive-complexity-threshold = 18
msrv = "1.88.0"
cognitive-complexity-threshold = 18

3
.editorconfig Normal file
View file

@ -0,0 +1,3 @@
root = true
[*.rs]
indent_style = tab

2
.github/FUNDING.yml vendored
View file

@ -1 +1 @@
github: extrawurst
github: extrawurst

View file

@ -2,7 +2,7 @@
name: Bug report
about: Create a report to help us improve
title: ''
labels: ''
labels: 'bug'
assignees: ''
---

View file

@ -2,7 +2,7 @@
name: Feature request
about: Suggest an idea for this project
title: ''
labels: ''
labels: 'feature-request'
assignees: ''
---

16
.github/PULL_REQUEST_TEMPLATE.md vendored Normal file
View file

@ -0,0 +1,16 @@
<!---
Thank you for contributing to GitUI! Please fill out the template below, and remove or add any
information as you feel necessary.
--->
This Pull Request fixes/closes #{issue_num}.
It changes the following:
-
-
I followed the checklist:
- [ ] I added unittests
- [ ] I ran `make check` without errors
- [ ] I tested the overall application
- [ ] I added an appropriate item to the changelog

View file

@ -5,7 +5,12 @@ updates:
schedule:
interval: daily
open-pull-requests-limit: 10
ignore:
- dependency-name: pprof
versions:
- 0.4.1
groups:
cargo-minor:
patterns: ["*"]
update-types:
- 'minor'
cargo-patch:
patterns: ["*"]
update-types:
- 'patch'

8
.github/stale.yml vendored
View file

@ -1,18 +1,18 @@
# Number of days of inactivity before an issue becomes stale
daysUntilStale: 90
daysUntilStale: 180
# Number of days of inactivity before a stale issue is closed
daysUntilClose: 7
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: wontfix
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
recent activity. It will be closed if no further activity occurs. Thank you
any activity half a year. It will be closed in 14 days if no further activity occurs. Thank you
for your contributions.
# Comment to post when closing a stale issue. Set to `false` to disable
closeComment: false

23
.github/workflows/brew.yml vendored Normal file
View file

@ -0,0 +1,23 @@
name: brew update
on:
# only manually
workflow_dispatch:
inputs:
tag-name:
required: true
description: 'release tag'
jobs:
update_brew:
runs-on: ubuntu-latest
steps:
- name: Bump homebrew-core formula
uses: mislav/bump-homebrew-formula-action@v3
env:
COMMITTER_TOKEN: ${{ secrets.BREW_TOKEN }}
with:
formula-name: gitui
# https://github.com/mislav/bump-homebrew-formula-action/issues/58
formula-path: Formula/g/gitui.rb
tag-name: ${{ github.event.inputs.tag-name }}

View file

@ -2,98 +2,133 @@ name: CD
on:
push:
tags:
- '*'
tags:
- "*"
workflow_dispatch:
permissions:
contents: write
jobs:
release:
strategy:
fail-fast: false
matrix:
os: [ubuntu-latest, macos-latest, windows-latest]
os: [ubuntu-latest, macos-latest, windows-latest, ubuntu-22.04]
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@v2
- uses: actions/checkout@v4
- name: Get version
id: get_version
run: echo ::set-output name=version::${GITHUB_REF/refs\/tags\//}
- name: Get version
id: get_version
run: echo "version=${GITHUB_REF/refs\/tags\//}" >> $GITHUB_OUTPUT
- name: Install Rust
uses: actions-rs/toolchain@v1
with:
toolchain: stable
profile: minimal
components: clippy
- name: Restore cargo cache
uses: Swatinem/rust-cache@v2
env:
cache-name: ci
with:
shared-key: ${{ matrix.os }}-${{ env.cache-name }}-stable
- name: New Resolver
run: |
cargo install cargo-modify --force
cargo modify new-resolver
- name: Install Rust
uses: dtolnay/rust-toolchain@stable
with:
components: clippy
- name: Build
run: cargo build
- name: Run tests
run: make test
- name: Run clippy
run: |
cargo clean
make clippy
- uses: taiki-e/install-action@nextest
- name: Setup MUSL
if: matrix.os == 'ubuntu-latest'
run: |
rustup target add x86_64-unknown-linux-musl
sudo apt-get -qq install musl-tools
- name: Build
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: Build Release Mac
if: matrix.os == 'macos-latest'
run: make release-mac
- name: Build Release Linux
if: matrix.os == 'ubuntu-latest'
run: make release-linux-musl
- name: Build Release Win
if: matrix.os == 'windows-latest'
run: make release-win
- 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: Set SHA
if: matrix.os == 'macos-latest'
id: shasum
run: |
echo ::set-output name=sha::"$(shasum -a 256 ./release/gitui-mac.tar.gz | awk '{printf $1}')"
- name: 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
- name: Extract release notes
if: matrix.os == 'ubuntu-latest'
id: release_notes
uses: ffurrer2/extract-release-notes@v1
- name: Release
uses: softprops/action-gh-release@v1
with:
body: ${{ steps.release_notes.outputs.release_notes }}
prerelease: ${{ contains(github.ref, '-') }}
files: |
./release/*.tar.gz
./release/*.zip
./release/*.msi
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
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
# - name: Bump personal tap formula
# uses: mislav/bump-homebrew-formula-action@v1
# if: "matrix.os == 'macos-latest' && !contains(github.ref, '-')" # skip prereleases
# env:
# COMMITTER_TOKEN: ${{ secrets.BREW_TOKEN }}
# with:
# formula-name: gitui
# homebrew-tap: extrawurst/tap
# download-url: https://github.com/extrawurst/gitui/releases/download/${{ steps.get_version.outputs.version }}/gitui-mac.tar.gz
tar xf $GITHUB_WORKSPACE/aarch64.tar.xz
tar xf $GITHUB_WORKSPACE/arm.tar.xz
- name: Bump homebrew-core formula
uses: mislav/bump-homebrew-formula-action@v1
if: "matrix.os == 'macos-latest' && !contains(github.ref, '-')" # skip prereleases
env:
COMMITTER_TOKEN: ${{ secrets.BREW_TOKEN }}
with:
formula-name: gitui
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

View file

@ -2,11 +2,11 @@ name: CI
on:
schedule:
- cron: '0 2 * * *' # run at 2 AM UTC
- cron: "0 2 * * *"
push:
branches: [ '*' ]
branches: ["*"]
pull_request:
branches: [ master ]
branches: [master]
env:
CARGO_TERM_COLOR: always
@ -17,151 +17,309 @@ jobs:
fail-fast: false
matrix:
os: [ubuntu-latest, macos-latest, windows-latest]
rust: [nightly, stable, '1.50']
rust: [nightly, stable, "1.88"]
runs-on: ${{ matrix.os }}
continue-on-error: ${{ matrix.rust == 'nightly' }}
steps:
- uses: actions/checkout@v2
- uses: actions/checkout@v4
- name: Restore cargo cache
uses: actions/cache@v2
env:
cache-name: ci
with:
path: |
~/.cargo/registry
~/.cargo/git
~/.cargo/bin
target
key: ${{ matrix.os }}-${{ env.cache-name }}-${{ matrix.rust }}-${{ hashFiles('Cargo.lock') }}
- 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: MacOS Workaround
if: matrix.os == 'macos-latest'
run: cargo clean -p serde_derive -p thiserror
- name: Install Rust
uses: actions-rs/toolchain@v1
with:
toolchain: ${{ matrix.rust }}
default: true
profile: minimal
components: clippy
- name: Install Rust
uses: dtolnay/rust-toolchain@master
with:
toolchain: ${{ matrix.rust }}
components: clippy
- name: New Resolver
if: matrix.rust != '1.50'
run: |
cargo install cargo-modify --force
cargo modify new-resolver
- name: Override rust toolchain
run: rustup override set ${{ matrix.rust }}
- name: Build Debug
run: |
cargo build
- name: Rustup Show
run: rustup show
- name: Run tests
run: make test
- uses: taiki-e/install-action@nextest
- name: Run clippy
run: |
make clippy
- name: Build Debug
run: |
cargo build
- name: Build Release
run: make build-release
- name: Run tests
run: make test
- 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: Run clippy
run: |
make clippy
- name: Binary dependencies (mac)
if: matrix.os == 'macos-latest'
run: |
otool -L ./target/release/gitui
- name: Build Release
run: make build-release
- name: Build MSI (windows)
if: matrix.os == 'windows-latest'
run: |
cargo install cargo-wix
cargo wix --no-build --nocapture --output ./target/wix/gitui.msi
ls -l ./target/wix/gitui.msi
- 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.50']
rust: [nightly, stable, "1.88"]
continue-on-error: ${{ matrix.rust == 'nightly' }}
steps:
- uses: actions/checkout@master
- name: Install Rust
uses: actions-rs/toolchain@v1
with:
toolchain: ${{ matrix.rust }}
profile: minimal
default: true
target: x86_64-unknown-linux-musl
- uses: actions/checkout@v4
# TODO: remove once we depend on 1.51 as a msrv and resolver is supported by default
- name: New Resolver
if: matrix.rust != '1.50'
run: |
cargo install cargo-modify --force
cargo modify new-resolver
- name: Restore cargo cache
uses: Swatinem/rust-cache@v2
env:
cache-name: ci
with:
key: ubuntu-latest-${{ env.cache-name }}-${{ matrix.rust }}
- name: Setup MUSL
run: |
sudo apt-get -qq install musl-tools
- name: Build Debug
run: |
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: Install Rust
uses: dtolnay/rust-toolchain@master
with:
toolchain: ${{ matrix.rust }}
targets: x86_64-unknown-linux-musl
rustfmt:
name: Rustfmt
# 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@master
- name: Install Rust
uses: actions-rs/toolchain@v1
with:
toolchain: stable
components: rustfmt
- run: cargo fmt -- --check
- uses: actions/checkout@v4
sec:
name: Security audit
- name: Restore cargo cache
uses: Swatinem/rust-cache@v2
env:
cache-name: ci
with:
key: ubuntu-latest-${{ env.cache-name }}-stable
- name: Install Rust
uses: dtolnay/rust-toolchain@stable
with:
components: rustfmt
- run: cargo fmt -- --check
- name: tombi install
uses: tombi-toml/setup-tombi@v1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
version: '0.9.0'
- name: tombi check
run: |
tombi format --check
- name: cargo-deny install
run: |
cargo install --locked cargo-deny
- name: cargo-deny checks
run: |
cargo deny check
udeps:
name: udeps
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: actions-rs/audit-check@v1
with:
token: ${{ secrets.GITHUB_TOKEN }}
- 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@master
- name: Extract release notes
id: extract_release_notes
uses: ffurrer2/extract-release-notes@v1
with:
release_notes_file: ./release-notes.txt
- uses: actions/upload-artifact@v1
with:
name: release-notes.txt
path: ./release-notes.txt
- uses: actions/checkout@v4
- name: Extract release notes
id: extract_release_notes
uses: ffurrer2/extract-release-notes@v2
with:
release_notes_file: ./release-notes.txt
- uses: actions/upload-artifact@v4
with:
name: release-notes.txt
path: ./release-notes.txt
test-homebrew:
name: Test Homebrew Formula (macOS)
runs-on: macos-latest
steps:
- name: Set up Homebrew
uses: Homebrew/actions/setup-homebrew@master
- name: Install stable Rust
uses: actions-rs/toolchain@v1
with:
toolchain: stable
- name: Let Homebrew build gitui from source
run: brew install --build-from-source gitui

125
.github/workflows/nightly.yml vendored Normal file
View file

@ -0,0 +1,125 @@
name: Build Nightly Releases
on:
schedule:
- cron: "0 3 * * *"
workflow_dispatch:
env:
CARGO_TERM_COLOR: always
AWS_BUCKET_NAME: s3://gitui/nightly/
jobs:
release:
strategy:
fail-fast: false
matrix:
os: [ubuntu-latest, macos-latest, windows-latest, ubuntu-22.04]
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@v4
- name: Restore cargo cache
uses: Swatinem/rust-cache@v2
env:
cache-name: ci
with:
shared-key: ${{ matrix.os }}-${{ env.cache-name }}-stable
- name: Install Rust
uses: dtolnay/rust-toolchain@stable
with:
components: clippy
- uses: taiki-e/install-action@nextest
# ideally we trigger the nightly build/deploy only if the normal nightly CI finished successfully
- name: Run tests
if: matrix.os != 'ubuntu-22.04'
run: make test
- name: Run clippy
if: matrix.os != 'ubuntu-22.04'
run: |
cargo clean
make clippy
- name: Setup MUSL
if: matrix.os == 'ubuntu-latest'
run: |
rustup target add x86_64-unknown-linux-musl
sudo apt-get -qq install musl-tools
- name: Setup ARM toolchain
if: matrix.os == 'ubuntu-22.04'
run: |
rustup target add aarch64-unknown-linux-gnu
rustup target add armv7-unknown-linux-gnueabihf
rustup target add arm-unknown-linux-gnueabihf
curl -o $GITHUB_WORKSPACE/aarch64.tar.xz https://armkeil.blob.core.windows.net/developer/Files/downloads/gnu-a/8.2-2018.08/gcc-arm-8.2-2018.08-x86_64-aarch64-linux-gnu.tar.xz
curl -o $GITHUB_WORKSPACE/arm.tar.xz https://armkeil.blob.core.windows.net/developer/Files/downloads/gnu-a/8.2-2018.08/gcc-arm-8.2-2018.08-x86_64-arm-linux-gnueabihf.tar.xz
tar xf $GITHUB_WORKSPACE/aarch64.tar.xz
tar xf $GITHUB_WORKSPACE/arm.tar.xz
echo "$GITHUB_WORKSPACE/gcc-arm-8.2-2018.08-x86_64-aarch64-linux-gnu/bin" >> $GITHUB_PATH
echo "$GITHUB_WORKSPACE/gcc-arm-8.2-2018.08-x86_64-arm-linux-gnueabihf/bin" >> $GITHUB_PATH
- name: Build Release Mac
if: matrix.os == 'macos-latest'
run: make release-mac
- name: Build Release Mac x86
if: matrix.os == 'macos-latest'
run: |
rustup target add x86_64-apple-darwin
make release-mac-x86
- name: Build Release Linux
if: matrix.os == 'ubuntu-latest'
run: make release-linux-musl
- name: Build Release Win
if: matrix.os == 'windows-latest'
run: make release-win
- name: Build Release Linux ARM
if: matrix.os == 'ubuntu-22.04'
run: make release-linux-arm
- name: Ubuntu 22.04 Upload Artifact
if: matrix.os == 'ubuntu-22.04'
env:
AWS_ACCESS_KEY_ID: ${{ secrets.AWS_KEY_ID }}
AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_KEY_SECRET }}
AWS_DEFAULT_REGION: ${{ secrets.AWS_REGION }}
run: |
aws s3 cp ./release/gitui-linux-armv7.tar.gz $AWS_BUCKET_NAME
aws s3 cp ./release/gitui-linux-arm.tar.gz $AWS_BUCKET_NAME
aws s3 cp ./release/gitui-linux-aarch64.tar.gz $AWS_BUCKET_NAME
- name: Ubuntu Latest Upload Artifact
if: matrix.os == 'ubuntu-latest'
env:
AWS_ACCESS_KEY_ID: ${{ secrets.AWS_KEY_ID }}
AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_KEY_SECRET }}
AWS_DEFAULT_REGION: ${{ secrets.AWS_REGION }}
run: |
aws s3 cp ./release/gitui-linux-x86_64.tar.gz $AWS_BUCKET_NAME
- name: MacOS Upload Artifact
if: matrix.os == 'macos-latest'
env:
AWS_ACCESS_KEY_ID: ${{ secrets.AWS_KEY_ID }}
AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_KEY_SECRET }}
AWS_DEFAULT_REGION: ${{ secrets.AWS_REGION }}
run: |
aws s3 cp ./release/gitui-mac.tar.gz $AWS_BUCKET_NAME
aws s3 cp ./release/gitui-mac-x86.tar.gz $AWS_BUCKET_NAME
- name: Windows Upload Artifact
if: matrix.os == 'windows-latest'
env:
AWS_ACCESS_KEY_ID: ${{ secrets.AWS_KEY_ID }}
AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_KEY_SECRET }}
AWS_DEFAULT_REGION: ${{ secrets.AWS_REGION }}
run: |
aws s3 cp ./release/gitui-win.msi $env:AWS_BUCKET_NAME
aws s3 cp ./release/gitui-win.tar.gz $env:AWS_BUCKET_NAME

View file

@ -1,5 +1,4 @@
{
"editor.formatOnSave": true,
"workbench.settings.enableNaturalLanguageSearch": false,
"telemetry.enableTelemetry": false,
}

File diff suppressed because it is too large Load diff

26
CONTRIBUTING.md Normal file
View file

@ -0,0 +1,26 @@
# Contributing
Were 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
Theres a [Discord server][discord-server] you can join if you get stuck or
dont 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 dont 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

4810
Cargo.lock generated

File diff suppressed because it is too large Load diff

View file

@ -1,87 +1,110 @@
[package]
name = "gitui"
version = "0.16.2"
authors = ["Stephan Dilly <dilly.stephan@gmail.com>"]
version = "0.28.1"
authors = ["extrawurst <mail@rusticorn.com>"]
description = "blazing fast terminal-ui for git"
edition = "2018"
exclude = [".github/*",".vscode/*","assets/*"]
homepage = "https://github.com/extrawurst/gitui"
repository = "https://github.com/extrawurst/gitui"
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 = [
"git",
"gui",
"cli",
"terminal",
"ui",
keywords = ["cli", "git", "gui", "terminal", "ui"]
build = "build.rs"
[workspace]
members = [
"asyncgit",
"filetreelist",
"git2-hooks",
"git2-testing",
"scopetime",
]
[dependencies]
scopetime = { path = "./scopetime", version = "0.1" }
asyncgit = { path = "./asyncgit", version = "0.16" }
filetreelist = { path = "./filetreelist", version = "0.2" }
crossterm = { version = "0.19", features = [ "serde" ] }
clap = { version = "2.33", default-features = false }
tui = { version = "0.15", default-features = false, features = ['crossterm', 'serde'] }
bytesize = { version = "1.0", default-features = false}
itertools = "0.10"
rayon-core = "1.9"
log = "0.4"
simplelog = { version = "0.10", default-features = false }
dirs-next = "2.0"
crossbeam-channel = "0.5"
scopeguard = "1.1"
bitflags = "1.2"
chrono = "0.4"
backtrace = "0.3"
ron = "0.6"
serde = "1.0"
anyhow = "1.0"
unicode-width = "0.1"
textwrap = "0.14"
unicode-truncate = "0.2"
unicode-segmentation = "1.7"
easy-cast = "0.4"
bugreport = "0.4"
lazy_static = "1.4"
syntect = { version = "4.5", default-features = false, features = ["metadata", "default-fancy"]}
[target.'cfg(all(target_family="unix",not(target_os="macos")))'.dependencies]
which = "4.1"
# pprof is not available on windows
[target.'cfg(not(windows))'.dependencies]
pprof = { version = "0.4", features = ["flamegraph"], optional = true }
asyncgit = { path = "./asyncgit", version = "0.28.1", default-features = false }
backtrace = "0.3"
base64 = "0.22"
bitflags = "2.10"
bugreport = "0.5.1"
bwrap = { version = "1.3", features = ["use_std"] }
bytesize = { version = "2.3", default-features = false }
chrono = { version = "0.4", default-features = false, features = ["clock"] }
clap = { version = "4.5", features = ["cargo", "env"] }
crossbeam-channel = "0.5"
crossterm = { version = "0.29", features = ["serde"] }
dirs = "6.0"
easy-cast = "0.5"
filetreelist = { path = "./filetreelist", version = ">=0.6" }
fuzzy-matcher = "0.3"
gh-emoji = { version = "1.0", optional = true }
indexmap = "2"
itertools = "0.14"
log = "0.4"
notify = "8"
notify-debouncer-mini = "0.7"
once_cell = "1"
parking_lot_core = "0.9"
ratatui = { version = "0.30", default-features = false, features = [
"crossterm",
"serde",
] }
ratatui-textarea = "0.8"
rayon-core = "1.13"
ron = "0.12"
scopeguard = "1.2"
scopetime = { path = "./scopetime", version = "0.1" }
serde = "1.0"
shellexpand = "3.1"
simplelog = { version = "0.12", default-features = false }
struct-patch = "0.10"
syntect = { version = "5.3", default-features = false, features = [
"default-syntaxes",
"default-themes",
"html",
"parsing",
"plist-load",
] }
two-face = { version = "0.4.4", default-features = false }
unicode-segmentation = "1.12"
unicode-truncate = "2.0"
unicode-width = "0.2"
which = "8.0"
[dev-dependencies]
pretty_assertions = "0.7"
env_logger = "0.11"
git2-testing = { path = "./git2-testing" }
insta = { version = "1.41.0", features = ["filters"] }
pretty_assertions = "1.4"
tempfile = "3"
[build-dependencies]
chrono = { version = "0.4", default-features = false, features = ["clock"] }
[badges]
maintenance = { status = "actively-developed" }
[features]
default=[]
timing=["scopetime/enabled"]
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"]
[workspace]
members=[
"asyncgit",
"scopetime",
"filetreelist",
]
[profile.release]
lto = true
opt-level = 'z' # Optimize for size.
codegen-units = 1
# make debug build as fast as release
# usage of utf8 encoding inside tui
# make debug build as fast as release
# usage of utf8 encoding inside tui
# makes their debug profile slow
[profile.dev.package."tui"]
[profile.dev.package."ratatui"]
opt-level = 3
[profile.dev]
split-debuginfo = "unpacked"
[profile.release]
opt-level = "z" # Optimize for size.
strip = "debuginfo"
lto = true
codegen-units = 1

26
FAQ.md Normal file
View file

@ -0,0 +1,26 @@
## <a name="table-of-contents"></a> Table of Contents
1. ["Bad Credentials" Error](#credentials)
2. [Custom key bindings](#keybindings)
2. [Watcher](#watcher)
## 1. <a name="credentials"></a> "Bad Credentials" Error <small><sup>[Top ▲](#table-of-contents)</sup></small>
Some users have trouble pushing/pulling from remotes and adding their ssh-key to their ssh-agent solved the issue. The error they get is:
![](./assets/bad-credentials.png)
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.

View file

@ -4,14 +4,48 @@ The default keys are based on arrow keys to navigate.
However popular demand lead to fully customizability of the key bindings.
On first start `gitui` will create `key_config.ron` file automatically based on the defaults.
This file allows changing every key binding.
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_config.ron` (mac)
* `$XDG_CONFIG_HOME/gitui/key_config.ron` (linux using XDG)
* `$HOME/.config/gitui/key_config.ron` (linux)
* `%APPDATA%/gitui/key_config.ron` (Windows)
* `$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)
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_config.ron` to get vim style key bindings.
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))

View file

@ -1,6 +1,6 @@
MIT License
Copyright (c) 2021 Stephan Dilly
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

View file

@ -1,66 +1,120 @@
.PHONY: debug build-release release-linux-musl test clippy clippy-pedantic install install-debug
.PHONY: debug build-release release-linux-musl test clippy clippy-pedantic install install-debug sort
ARGS=-l
# ARGS=-l -d <some_path>
# 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 run --features=timing,pprof -- ${ARGS}
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
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
cargo wix --no-build --nocapture --output ./release/gitui.msi
ls -l ./release/gitui.msi
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-musl.tar.gz ./gitui
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
cargo build --release --target=x86_64-unknown-linux-musl --locked
test-linux-musl:
cargo test --workspace --target=x86_64-unknown-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 test --workspace
cargo nextest run --workspace
fmt:
cargo fmt -- --check
clippy:
touch src/main.rs
cargo clean -p gitui -p asyncgit -p scopetime -p filetreelist
cargo clippy --workspace --all-features
clippy-nightly:
touch src/main.rs
cargo clean -p gitui -p asyncgit -p scopetime -p filetreelist
cargo +nightly clippy --workspace --all-features
check: fmt clippy test
check: fmt clippy test sort deny
check-nightly:
cargo +nightly c
cargo +nightly clippy --workspace --all-features
cargo +nightly t
deny:
cargo deny check
sort:
tombi format --check
install:
cargo install --path "." --offline
cargo install --path "." --offline --locked
install-timing:
cargo install --features=timing --path "." --offline
cargo install --features=timing --path "." --offline --locked
licenses:
cargo bundle-licenses --format toml --output THIRDPARTY.toml
clean:
cargo clean

14
NIGHTLIES.md Normal file
View file

@ -0,0 +1,14 @@
# Nightlies
**Use with caution as these binaries are build nightly and might be broken**
When you find problems please report them and always mention the version that you see in the `help popup` or when running `gitui -V`
* [gitui-linux-aarch64.tar.gz](https://gitui.s3.eu-west-1.amazonaws.com/nightly/gitui-linux-aarch64.tar.gz)
* [gitui-linux-arm.tar.gz](https://gitui.s3.eu-west-1.amazonaws.com/nightly/gitui-linux-arm.tar.gz)
* [gitui-linux-armv7.tar.gz](https://gitui.s3.eu-west-1.amazonaws.com/nightly/gitui-linux-armv7.tar.gz)
* [gitui-linux-x86_64.tar.gz](https://gitui.s3.eu-west-1.amazonaws.com/nightly/gitui-linux-x86_64.tar.gz)
* [gitui-mac.tar.gz](https://gitui.s3.eu-west-1.amazonaws.com/nightly/gitui-mac.tar.gz)
* [gitui-mac-x86.tar.gz](https://gitui.s3.eu-west-1.amazonaws.com/nightly/gitui-mac-x86.tar.gz)
* [gitui-win.tar.gz](https://gitui.s3.eu-west-1.amazonaws.com/nightly/gitui-win.tar.gz)
* [gitui-win.msi](https://gitui.s3.eu-west-1.amazonaws.com/nightly/gitui-win.msi)

195
README.md
View file

@ -1,23 +1,23 @@
<h1 align="center">
<img width="300px" src="assets/logo.png" />
[![CI][s0]][l0] [![crates][s1]][l1] ![MIT][s2] [![UNSAFE][s3]][l3] [![ITCH][s4]][l4] [![DISC][s5]][l5] [![TWEET][s6]][l6]
[![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/extrawurst/gitui/workflows/CI/badge.svg
[l0]: https://github.com/extrawurst/gitui/actions
[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/
[s4]: https://img.shields.io/badge/itch.io-ok-green
[l4]: https://extrawurst.itch.io/gitui
[s5]: https://img.shields.io/discord/723083834811220028.svg?logo=chat
[l5]: https://discord.gg/7TGFfuq
[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>
@ -32,30 +32,35 @@
5. [Limitations](#limitations)
6. [Installation](#installation)
7. [Build](#build)
8. [Diagnostics](#diagnostics)
9. [Color Theme](#theme)
10. [Key Bindings](#bindings)
11. [Sponsoring](#sponsoring)
12. [Inspiration](#inspiration)
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: _commit-msg_/_post-commit_)
- 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
- Push / Fetch to / from remote
- Branch List (create, rename, delete, checkout, remotes)
- Browse commit log, diff committed changes
- Scalable terminal UI layout
- 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.
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.
@ -65,25 +70,22 @@ For a [RustBerlin meetup presentation](https://youtu.be/rpilJV-eIVw?t=5334) ([sl
| | Time | Memory (GB) | Binary (MB) | Freezes | Crashes |
| --------- | ---------- | ----------- | ----------- | --------- | --------- |
| `gitui` | **24 s** ✅ | **0.17** ✅ | 1.4 | **No** ✅ | **No** ✅ |
| `lazygit` | 57 s | 2.6 | 16 | Yes | Sometimes |
| `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`:
* log search (commit, author, sha) ([#449](https://github.com/extrawurst/gitui/issues/449),[#429](https://github.com/extrawurst/gitui/issues/429))
* file history log ([#381](https://github.com/extrawurst/gitui/issues/381))
* visualize branching structure in log tab ([#81](https://github.com/extrawurst/gitui/issues/81))
* interactive rebase ([#32](https://github.com/extrawurst/gitui/issues/32))
* notify-based change detection ([#1](https://github.com/extrawurst/gitui/issues/1))
* 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 support for [bare repositories](https://git-scm.com/book/en/v2/Git-on-the-Server-Getting-Git-on-a-Server) (see [#100](https://github.com/extrawurst/gitui/issues/100))
- no support for [core.hooksPath](https://git-scm.com/docs/githooks) config
- no support for GPG signing (see [#97](https://github.com/extrawurst/gitui/issues/97))
- 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.
@ -93,22 +95,30 @@ All support is welcomed! Sponsors as well! ❤️
## 6. <a name="installation"></a> Installation <small><sup>[Top ▲](#table-of-contents)</sup></small>
For the time being this product is in alpha and is not considered production ready. However, for personal use it is reasonably stable and is being used while developing itself.
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.
### [Arch Linux](https://archlinux.org/packages/community/x86_64/gitui/)
<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
##### Fedora
```sh
sudo dnf install gitui
```
### Gentoo
##### Gentoo
Available in [dm9pZCAq overlay](https://github.com/gentoo-mirror/dm9pZCAq)
```sh
@ -117,25 +127,49 @@ sudo emerge --sync dm9pZCAq
sudo emerge dev-vcs/gitui::dm9pZCAq
```
### Homebrew (macOS)
##### [openSUSE](https://software.opensuse.org/package/gitui)
```sh
sudo zypper install gitui
```
##### Homebrew (macOS)
```sh
brew install gitui
```
### [Scoop](https://github.com/ScoopInstaller/Main/blob/master/bucket/gitui.json) (Windows)
##### [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)
##### [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)
##### [Mise](https://github.com/jdx/mise)
```shell
mise use -g gitui@latest
```
##### [Nix](https://search.nixos.org/packages?channel=unstable&show=gitui&from=0&size=50&sort=relevance&query=gitui) (Nix/NixOS)
Nixpkg
```
@ -146,28 +180,78 @@ NixOS
nix-env -iA nixos.gitui
```
## Release Binaries
##### [Termux](https://github.com/termux/termux-packages/tree/master/packages/gitui) (Android)
[Available for download in releases](https://github.com/extrawurst/gitui/releases)
```
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
- macOS
- Windows
#### 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
- Latest `rust` and `cargo`
- 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`
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)
## 8. <a name="diagnostics"></a> Diagnostics <small><sup>[Top ▲](#table-of-contents)</sup></small>
### 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`.
@ -178,7 +262,7 @@ This will log to:
- Linux: `$HOME/.cache/gitui/gitui.log`
- Windows: `%LOCALAPPDATA%/gitui/gitui.log`
## 9. <a name="theme"></a> Color Theme <small><sup>[Top ▲](#table-of-contents)</sup></small>
## 10. <a name="theme"></a> Color Theme <small><sup>[Top ▲](#table-of-contents)</sup></small>
![](assets/light-theme.png)
@ -186,19 +270,32 @@ This will log to:
However, you can customize everything to your liking: See [Themes](THEMES.md).
## 10. <a name="bindings"></a> Key Bindings <small><sup>[Top ▲](#table-of-contents)</sup></small>
## 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.
## 11. <a name="sponsoring"></a> Sponsoring <small><sup>[Top ▲](#table-of-contents)</sup></small>
## 12. <a name="sponsoring"></a> Sponsoring <small><sup>[Top ▲](#table-of-contents)</sup></small>
[![github](https://img.shields.io/badge/-GitHub%20Sponsors-fafbfc?logo=GitHub%20Sponsors)](https://github.com/sponsors/extrawurst)
[![buy-me-a-coffee](https://img.shields.io/badge/-Buy%20Me%20a%20Coffee-ffdd00?logo=Buy%20Me%20A%20Coffee&logoColor=000000)](https://www.buymeacoffee.com/extrawurst)
## 12. <a name="inspiration"></a> Inspiration <small><sup>[Top ▲](#table-of-contents)</sup></small>
## 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>

View file

@ -3,14 +3,88 @@
default on light terminal:
![](assets/light-theme.png)
to change the colors of the default theme you have to modify `theme.ron` file
[Ron format](https://github.com/ron-rs/ron) located at config path. The path differs depending on the operating system:
## Configuration
* `$HOME/Library/Application Support/gitui/theme.ron` (mac)
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 dont 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 may make a theme in the same directory mentioned above with and select 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` wil use `$XDG_CONFIG_HOME/gitui/arc.ron` or `$HOME/.config/gitui/arc.ron`
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`.
Valid colors can be found in tui-rs' [Color](https://docs.rs/tui/0.12.0/tui/style/enum.Color.html) struct. note that rgb colors might not be supported in every terminal.
Example theme override:
```ron
(
selection_bg: Some("Blue"),
selection_fg: Some("#ffffff"),
)
```
Note that you need to wrap values in `Some` due to the way the overrides work (as of 0.23).
Notes:
* rgb colors might not be supported in every terminal.
* using a color like `yellow` might appear in whatever your terminal/theme defines for `yellow`
* valid colors can be found in ratatui's [Color](https://docs.rs/ratatui/latest/ratatui/style/enum.Color.html) struct.
* all customizable theme elements can be found in [`style.rs` in the `impl Default for Theme` block](https://github.com/gitui-org/gitui/blob/master/src/ui/style.rs#L305)
## Preset Themes
You can find preset themes by Catppuccin [here](https://github.com/catppuccin/gitui.git).
## Syntax Highlighting
The syntax highlighting theme can be defined using the element `syntax`. Both [default themes of the syntect library](https://github.com/trishume/syntect/blob/7fe13c0fd53cdfa0f9fea1aa14c5ba37f81d8b71/src/dumps.rs#L215) and custom themes are supported.
Example syntax theme:
```ron
(
syntax: Some("InspiredGitHub"),
)
```
Custom themes are located in the [configuration directory](#configuration), are using TextMate's theme format and must have a `.tmTheme` file extension. To load a custom theme, `syntax` must be set to the file name without the file extension. For example, to load [`Blackboard.tmTheme`](https://raw.githubusercontent.com/filmgirl/TextMate-Themes/refs/heads/master/Blackboard.tmTheme), place the file next to `theme.ron` and set:
```ron
(
syntax: Some("Blackboard"),
)
```
[filmgirl/TextMate-Themes](https://github.com/filmgirl/TextMate-Themes) offers many [beautiful](https://inkdeep.github.io/TextMate-Themes) TextMate themes to choose from.
## Customizing line breaks
If you want to change how the line break is displayed in the diff, you can also specify `line_break` in your `theme.ron`:
```ron
(
line_break: Some("¶"),
)
```
Note that if you want to turn it off, you should use a blank string:
```ron
(
line_break: Some(""),
)
```
## Customizing selection
By default the `selection_fg` color is used to color the text of the selected line.
Diff line, filename, commit hashes, time and author are re-colored with `selection_fg` color.
This can be changed by specifying the `use_selection_fg` boolean in your `theme.ron`:
```
(
use_selection_fg: Some(false),
)
```
By default, `use_selection_fg` is set to `true`.

BIN
assets/add-remote.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 67 KiB

BIN
assets/bad-credentials.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 69 KiB

BIN
assets/blame-goto-line.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 302 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 318 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 481 KiB

BIN
assets/compare.gif Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 653 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 243 KiB

BIN
assets/diff-empty-line.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 136 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 96 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 98 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 299 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 900 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 444 KiB

BIN
assets/fuzzy-find.gif Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 415 KiB

BIN
assets/gitui-signing.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

BIN
assets/log-search.gif Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.7 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 18 KiB

After

Width:  |  Height:  |  Size: 36 KiB

1
assets/logo.svg Normal file
View file

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="1000" height="600" fill="none"><g clip-path="url(#a)"><mask id="c" width="438" height="484" x="38" y="57" maskUnits="userSpaceOnUse" style="mask-type:alpha"><path fill="#D9D9D9" d="m340 541 76-39 60-52-76-43-35-8-13 42H231l-13-38-46-7-24-106 20-29v-44l85-47 101-6 48 26 67-40-60-67-150-25-189 78-31 201 116 182 185 22Z"/></mask><g stroke="#000" filter="url(#b)" mask="url(#c)"><path stroke-width="38.4" d="M290 483a183 183 0 1 0 0-366 183 183 0 0 0 0 366Z"/><path fill="#000" stroke-linejoin="round" stroke-width="12.8" d="m480 351 23-9-18-16-5 25Zm-14 36 25-4-15-20-10 24Zm-20 33 25 1-11-23-14 22Zm-26 28 24 6-6-24-18 18Zm-32 22 23 11-1-25-22 14Zm-35 16 20 15 4-25-24 10Zm-37 9 16 18 9-23-25 5Zm-39 1 13 21 13-21h-26Zm-38-6 9 23 16-18-25-5Zm-36-14 4 25 20-15-24-10Zm-33-20-1 25 23-11-22-14Zm-28-26-6 24 24-6-18-18Zm-22-32-11 23 25-1-14-22Zm-16-35-15 20 25 4-10-24Zm-9-37-18 16 23 9-5-25Zm-1-39-21 13 21 13v-26Zm6-38-23 9 18 16 5-25Zm14-36-25 4 15 20 10-24Zm20-33-25-1 11 23 14-22Zm26-28-24-6 6 24 18-18Zm32-22-23-11 1 25 22-14Zm35-16-20-15-4 25 24-10Zm37-9-16-18-9 23 25-5Zm39-1-13-21-13 21h26Zm38 6-9-23-16 18 25 5Zm36 14-4-25-20 15 24 10Zm33 20 1-25-23 11 22 14Zm28 26 6-24-24 6 18 18Zm22 32 11-23-25 1 14 22Zm16 36 15-20-25-4 10 24Zm9 36 18-16-23-9 5 25Z"/><path fill="#000" stroke-linejoin="round" stroke-width="25.6" d="m260 121 30 30 30-30h-60Zm191 95-19 38 38 19-19-57Zm-32 211-41-6-7 41 48-35Zm-210 35-7-41-41 6 48 35Zm-99-189 38-19-19-38-19 57Z"/></g></g><g filter="url(#d)"><path fill="#000" d="M305 307h81v7h-18v71l-35-6a98 98 0 0 1-92-5c-24-16-36-41-36-75 0-26 8-47 23-63 16-16 37-24 63-24 15 0 28 3 41 8l27-5 1 60h-9c-7-36-24-54-51-54h-7c-29 4-44 30-44 80 0 16 1 30 4 42 7 24 22 36 45 36 12 0 22-4 31-10v-55h-24v-7Zm157 71h3c2 0 4 3 4 7h-70v-7l6-1c7-1 10-6 10-14v-92h-16v-8h52v115h11Zm-8-152c0 4-1 7-3 10-4 7-10 11-18 11-4 0-7-1-10-3-8-4-11-10-11-18l2-10c4-7 10-11 19-11 3 0 7 1 10 3 7 4 11 10 11 18Zm24 46v-7h20v-27l36-6v33h37v7h-37v86l1 7c1 10 5 15 12 15l4-1c7-1 13-10 16-25l8 1c-1 8-4 14-6 19-6 9-16 14-30 14h-7c-23-3-34-18-34-45v-71h-20Zm101-49v-8h85v8h-22v97c0 11 0 20 2 28 5 21 16 31 36 31 30 0 45-19 45-59v-97h-22v-8h55v8h-22v96c0 13-2 25-5 34-8 23-27 35-57 35-48 0-72-22-72-65V223h-23Zm271 162h-85v-8h23V223h-23v-8h85v8h-22v154h15c4 1 6 3 7 8Z"/></g><defs><filter id="b" width="455.7" height="455.7" x="62" y="72" color-interpolation-filters="sRGB" filterUnits="userSpaceOnUse"><feFlood flood-opacity="0" result="BackgroundImageFix"/><feColorMatrix in="SourceAlpha" result="hardAlpha" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0"/><feOffset/><feGaussianBlur stdDeviation="2"/><feComposite in2="hardAlpha" operator="out"/><feColorMatrix values="0 0 0 0 1 0 0 0 0 1 0 0 0 0 1 0 0 0 1 0"/><feBlend in2="BackgroundImageFix" result="effect1_dropShadow_1_2"/><feBlend in="SourceGraphic" in2="effect1_dropShadow_1_2" result="shape"/></filter><filter id="d" width="653.4" height="191.2" x="201" y="201" color-interpolation-filters="sRGB" filterUnits="userSpaceOnUse"><feFlood flood-opacity="0" result="BackgroundImageFix"/><feColorMatrix in="SourceAlpha" result="hardAlpha" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0"/><feOffset/><feGaussianBlur stdDeviation="2"/><feComposite in2="hardAlpha" operator="out"/><feColorMatrix values="0 0 0 0 1 0 0 0 0 1 0 0 0 0 1 0 0 0 1 0"/><feBlend in2="BackgroundImageFix" result="effect1_dropShadow_1_2"/><feBlend in="SourceGraphic" in2="effect1_dropShadow_1_2" result="shape"/></filter><clipPath id="a"><path fill="#fff" d="M64 74h452v452H64z"/></clipPath></defs></svg>

After

Width:  |  Height:  |  Size: 3.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1 MiB

1
assets/options.drawio Normal file
View file

@ -0,0 +1 @@
<mxfile host="app.diagrams.net" modified="2021-08-17T21:58:53.216Z" agent="5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/14.1.1 Safari/605.1.15" etag="DR-vNI6rA-1d9_EpYLQC" version="14.9.7" type="device"><diagram id="cIq82w5ce00BbVejkL92" name="Page-1">5Zldc6IwFIZ/DZfrEJCvS0Xb7exHZ9bd6WxvdlIIkGlMmBir3V+/iQQVg+12Bqm7eqHwkkB43nMCJ1puPF9fc1gWX1iKiOXY6dpyJ5bjABCG8kcpz5Xi+0El5BynutFOmOHfSIu2Vpc4RYtGQ8EYEbhsigmjFCWioUHO2arZLGOkedUS5sgQZgkkpnqHU1FUaugEO/0jwnlRXxn4UXVkDuvG+k4WBUzZak9yp5Ybc8ZEtTVfx4goeDWXqt/VkaPbgXFExd90KLNx+X10/wl4v+6/5fx28jMVH/RZniBZ6hvWgxXPNQHOljRF6iS25Y5XBRZoVsJEHV1Jz6VWiDmRe0Bu6tMhLtD66DjB9u5l2CA2R4I/yyZ1h0B30REDvGDgVcpq54AbaazFHn0n1CLUrufbs+/AyA3N5g2cnPPj5Hh+g5Njm5S2wbdPCYTeiSi5BqVZIUPesX9QwWHyKOkcUpP3L4c1hgTnVCoEZWpXgcEyCUdanuM0VT3GTcgZJiRmhPHNudxs8+mIrn9A17UNutE2MBtBeKoYHBp0JyiDSyKOQd2Lt4Xg7BHVrCij6ACflmofEokJ8ZecaAvwpjsduDC03RrxCz74bVPBqVzwDBfuFInFhoR8DhWQ5vKB1b0lr6VGL4Y4fjBwDhwJTUcA6Dc1fMOUm5wyjv7nzBgOgteN6DU1AsOFmNENccf+jKnKCh/OFQ76sCi3HC4iS1zbNCfs05zQMGd4UdnRZkCv2REZBmDFqljSRymTKj8uJx+clvepPu2oC8w9P+wT8D/jhGhxoNeEAGbxORNQLM8zDTL5KJvpIQ07cKTtTQq8e40BzEJ3gmVNdamO+O/viFlU35YCM3r+aaL2CXxAZMx4ivjBNbtwzIsOZzUQvb9jZoloWPWmxaIDdzoB11zdcD2TWuva0fBk0MwSLt6U0moAkKby/SiXm0xORfZyt5pky2BW7032DZVf9ePDtrzxNRYJoxmWvWJ1aLMQpba+oWTJF/gJWd7kWAp1uxzVZm8nJgaDenl962PLGiDwt9PavpXuyaxsqQMJWxwtxs9u7Q8EzewYAq9tBbrXOcUs32YrGeFFFezW1LGiqTUC1vTKGodWeKWUUWyF3k7pK9xPMD+1OxBEfTpg1m9f4VOFfzD4p+DWL94vh3c3cOXu7i+uzbG9Pwrd6R8=</diagram></mxfile>

BIN
assets/options.gif Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 203 KiB

BIN
assets/popup-stacking.gif Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 781 KiB

BIN
assets/rebase.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 275 KiB

BIN
assets/reset_in_log.gif Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 MiB

BIN
assets/revert-commit.gif Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 682 KiB

BIN
assets/reword.gif Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.6 MiB

BIN
assets/submodules.gif Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 640 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 750 KiB

BIN
assets/tag-annotation.gif Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 285 KiB

BIN
assets/termux-android.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 641 KiB

View file

@ -1,33 +1,52 @@
[package]
name = "asyncgit"
version = "0.16.3"
authors = ["Stephan Dilly <dilly.stephan@gmail.com>"]
edition = "2018"
version = "0.28.1"
authors = ["extrawurst <mail@rusticorn.com>"]
edition = "2021"
description = "allow using git2 in a asynchronous context"
homepage = "https://github.com/extrawurst/gitui"
repository = "https://github.com/extrawurst/gitui"
homepage = "https://github.com/gitui-org/gitui"
repository = "https://github.com/gitui-org/gitui"
readme = "README.md"
license = "MIT"
categories = ["concurrency","asynchronous"]
categories = ["asynchronous", "concurrency"]
keywords = ["git"]
[dependencies]
scopetime = { path = "../scopetime", version = "0.1" }
git2 = "0.13"
# pinning to vendored openssl, using the git2 feature this gets lost with new resolver
openssl-sys = { version = '0.9', features= ["vendored"] }
# git2 = { path = "../../github/git2-rs", features = ["vendored-openssl"]}
# git2 = { git="https://github.com/extrawurst/git2-rs.git", rev="513a8c9", features = ["vendored-openssl"]}
rayon-core = "1.9"
bitflags = "2"
crossbeam-channel = "0.5"
dirs = "6.0"
easy-cast = "0.5"
fuzzy-matcher = "0.3"
git2 = "0.20"
git2-hooks = { path = "../git2-hooks", version = "0.7" }
gix = { version = "0.78.0", default-features = false, features = [
"mailmap",
"max-performance",
"revision",
"status",
] }
log = "0.4"
thiserror = "1.0"
url = "2.2"
unicode-truncate = "0.2.0"
easy-cast = "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]
tempfile = "3.2"
env_logger = "0.11"
invalidstring = { path = "../invalidstring", version = "0.1" }
serial_test = "0.5.1"
pretty_assertions = "0.7"
pretty_assertions = "1.4"
serial_test = "3.3"
tempfile = "3"
[features]
default = ["trace-libgit"]
trace-libgit = []
vendor-openssl = ["openssl-sys"]

View file

@ -4,206 +4,299 @@
use crate::error::Result;
use crossbeam_channel::Sender;
use std::sync::{Arc, Mutex};
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 {
/// can run a synchronous time intensive task
fn run(&mut self);
/// 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, T: Copy + Send + 'static> {
next: Arc<Mutex<Option<J>>>,
last: Arc<Mutex<Option<J>>>,
sender: Sender<T>,
pending: Arc<Mutex<()>>,
notification: T,
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, T: Copy + Send + 'static>
AsyncSingleJob<J, T>
{
///
pub fn new(sender: Sender<T>, value: T) -> Self {
Self {
next: Arc::new(Mutex::new(None)),
last: Arc::new(Mutex::new(None)),
pending: Arc::new(Mutex::new(())),
notification: value,
sender,
}
}
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()
}
///
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(&mut self) -> bool {
if let Ok(mut next) = self.next.lock() {
if next.is_some() {
*next = None;
return true;
}
}
/// 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
}
false
}
/// take out last finished job
pub fn take_last(&self) -> Option<J> {
if let Ok(mut last) = self.last.lock() {
last.take()
} else {
None
}
}
/// 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
pub fn spawn(&mut self, task: J) -> bool {
self.schedule_next(task);
self.check_for_job()
}
/// 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()
}
fn check_for_job(&self) -> bool {
if self.is_pending() {
return false;
}
///
pub fn progress(&self) -> Option<J::Progress> {
self.progress.read().ok().map(|d| (*d).clone())
}
if let Some(task) = self.take_next() {
let self_arc = self.clone();
fn check_for_job(&self) -> bool {
if self.is_pending() {
return false;
}
rayon_core::spawn(move || {
if let Err(e) = self_arc.run_job(task) {
log::error!("async job error: {}", e);
}
});
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;
}
return true;
}
false
}
false
}
fn run_job(&self, mut task: J) -> Result<()> {
//limit the pending scope
{
let _pending = self.pending.lock()?;
fn run_job(&self, mut task: J) -> Result<()> {
//limit the pending scope
{
let _pending = self.pending.lock()?;
task.run();
let notification = task.run(RunParams {
progress: self.progress.clone(),
sender: self.sender.clone(),
})?;
if let Ok(mut last) = self.last.lock() {
*last = Some(task);
}
if let Ok(mut last) = self.last.lock() {
*last = Some(task);
}
self.sender.send(self.notification)?;
}
self.sender.send(notification)?;
}
self.check_for_job();
self.check_for_job();
Ok(())
}
Ok(())
}
fn schedule_next(&mut self, task: J) {
if let Ok(mut next) = self.next.lock() {
*next = Some(task);
}
}
fn schedule_next(&self, task: J) {
if let Ok(mut next) = self.next.lock() {
*next = Some(task);
}
}
fn take_next(&self) -> Option<J> {
if let Ok(mut next) = self.next.lock() {
next.take()
} else {
None
}
}
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::AtomicU32, thread::sleep, time::Duration,
};
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>,
value_to_add: u32,
}
#[derive(Clone)]
struct TestJob {
v: Arc<AtomicU32>,
finish: Arc<AtomicBool>,
value_to_add: u32,
}
impl AsyncJob for TestJob {
fn run(&mut self) {
sleep(Duration::from_millis(100));
type TestNotification = ();
self.v.fetch_add(
self.value_to_add,
std::sync::atomic::Ordering::Relaxed,
);
}
}
impl AsyncJob for TestJob {
type Notification = TestNotification;
type Progress = ();
type Notificaton = ();
fn run(
&mut self,
_params: RunParams<Self::Notification, Self::Progress>,
) -> Result<Self::Notification> {
println!("[job] wait");
#[test]
fn test_overwrite() {
let (sender, receiver) = unbounded();
while !self.finish.load(Ordering::SeqCst) {
std::thread::yield_now();
}
let mut job: AsyncSingleJob<TestJob, Notificaton> =
AsyncSingleJob::new(sender, ());
println!("[job] sleep");
let task = TestJob {
v: Arc::new(AtomicU32::new(1)),
value_to_add: 1,
};
thread::sleep(Duration::from_millis(100));
assert!(job.spawn(task.clone()));
sleep(Duration::from_millis(1));
for _ in 0..5 {
assert!(!job.spawn(task.clone()));
}
println!("[job] done sleeping");
let _foo = receiver.recv().unwrap();
let _foo = receiver.recv().unwrap();
assert!(receiver.is_empty());
let res =
self.v.fetch_add(self.value_to_add, Ordering::SeqCst);
assert_eq!(
task.v.load(std::sync::atomic::Ordering::Relaxed),
3
);
}
println!("[job] value: {res}");
#[test]
fn test_cancel() {
let (sender, receiver) = unbounded();
Ok(())
}
}
let mut job: AsyncSingleJob<TestJob, Notificaton> =
AsyncSingleJob::new(sender, ());
#[test]
fn test_overwrite() {
let (sender, receiver) = unbounded();
let task = TestJob {
v: Arc::new(AtomicU32::new(1)),
value_to_add: 1,
};
let job: AsyncSingleJob<TestJob> =
AsyncSingleJob::new(sender);
assert!(job.spawn(task.clone()));
sleep(Duration::from_millis(1));
let task = TestJob {
v: Arc::new(AtomicU32::new(1)),
finish: Arc::new(AtomicBool::new(false)),
value_to_add: 1,
};
for _ in 0..5 {
assert!(!job.spawn(task.clone()));
}
assert!(job.cancel());
assert!(job.spawn(task.clone()));
task.finish.store(true, Ordering::SeqCst);
thread::sleep(Duration::from_millis(10));
let _foo = receiver.recv().unwrap();
for _ in 0..5 {
println!("spawn");
assert!(!job.spawn(task.clone()));
}
assert_eq!(
task.v.load(std::sync::atomic::Ordering::Relaxed),
2
);
}
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
);
}
}

View file

@ -1,179 +1,188 @@
use crate::{
error::Result,
hash,
sync::{self, FileBlame},
AsyncGitNotification, CWD,
error::Result,
hash,
sync::{self, CommitId, FileBlame, RepoPath},
AsyncGitNotification,
};
use crossbeam_channel::Sender;
use std::{
hash::Hash,
sync::{
atomic::{AtomicUsize, Ordering},
Arc, Mutex,
},
hash::Hash,
sync::{
atomic::{AtomicUsize, Ordering},
Arc, Mutex,
},
};
///
#[derive(Hash, Clone, PartialEq)]
#[derive(Hash, Clone, PartialEq, Eq)]
pub struct BlameParams {
/// path to the file to blame
pub file_path: String,
/// 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,
hash: u64,
result: 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>,
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(sender: &Sender<AsyncGitNotification>) -> Self {
Self {
current: Arc::new(Mutex::new(Request(0, None))),
last: Arc::new(Mutex::new(None)),
sender: sender.clone(),
pending: Arc::new(AtomicUsize::new(0)),
}
}
///
pub fn new(
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(
&mut self,
) -> Result<Option<(BlameParams, FileBlame)>> {
let last = self.last.lock()?;
///
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)
}))
}
Ok(last.clone().map(|last_result| {
(last_result.params, last_result.result)
}))
}
///
pub fn refresh(&mut self) -> Result<()> {
if let Ok(Some(param)) = self.get_last_param() {
self.clear_current()?;
self.request(param)?;
}
Ok(())
}
///
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 is_pending(&self) -> bool {
self.pending.load(Ordering::Relaxed) > 0
}
///
pub fn request(
&mut self,
params: BlameParams,
) -> Result<Option<FileBlame>> {
log::trace!("request");
///
pub fn request(
&self,
params: BlameParams,
) -> Result<Option<FileBlame>> {
log::trace!("request");
let hash = hash(&params);
let hash = hash(&params);
{
let mut current = self.current.lock()?;
{
let mut current = self.current.lock()?;
if current.0 == hash {
return Ok(current.1.clone());
}
if current.0 == hash {
return Ok(current.1.clone());
}
current.0 = hash;
current.1 = None;
}
current.0 = hash;
current.1 = None;
}
let arc_current = Arc::clone(&self.current);
let arc_last = Arc::clone(&self.last);
let sender = self.sender.clone();
let arc_pending = Arc::clone(&self.pending);
let 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);
self.pending.fetch_add(1, Ordering::Relaxed);
rayon_core::spawn(move || {
let notify = Self::get_blame_helper(
params,
&arc_last,
&arc_current,
hash,
);
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,
};
let notify = match notify {
Err(err) => {
log::error!("get_blame_helper error: {err}");
true
}
Ok(notify) => notify,
};
arc_pending.fetch_sub(1, Ordering::Relaxed);
arc_pending.fetch_sub(1, Ordering::Relaxed);
sender
.send(if notify {
AsyncGitNotification::Blame
} else {
AsyncGitNotification::FinishUnchanged
})
.expect("error sending blame");
});
sender
.send(if notify {
AsyncGitNotification::Blame
} else {
AsyncGitNotification::FinishUnchanged
})
.expect("error sending blame");
});
Ok(None)
}
Ok(None)
}
fn get_blame_helper(
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(CWD, &params.file_path)?;
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,
&params.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 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,
hash,
params,
});
}
{
let mut last = arc_last.lock()?;
*last = Some(LastResult {
result: file_blame,
params,
});
}
Ok(notify)
}
Ok(notify)
}
fn get_last_param(&self) -> Result<Option<BlameParams>> {
Ok(self
.last
.lock()?
.clone()
.map(|last_result| last_result.params))
}
fn get_last_param(&self) -> Result<Option<BlameParams>> {
Ok(self
.last
.lock()?
.clone()
.map(|last_result| last_result.params))
}
fn clear_current(&mut self) -> Result<()> {
let mut current = self.current.lock()?;
current.0 = 0;
current.1 = None;
Ok(())
}
fn clear_current(&self) -> Result<()> {
let mut current = self.current.lock()?;
current.0 = 0;
current.1 = None;
Ok(())
}
}

77
asyncgit/src/branches.rs Normal file
View file

@ -0,0 +1,77 @@
use crate::{
asyncjob::{AsyncJob, RunParams},
error::Result,
sync::{branch::get_branches_info, BranchInfo, RepoPath},
AsyncGitNotification,
};
use std::sync::{Arc, Mutex};
enum JobState {
Request {
local_branches: bool,
repo: RepoPath,
},
Response(Result<Vec<BranchInfo>>),
}
///
#[derive(Clone, Default)]
pub struct AsyncBranchesJob {
state: Arc<Mutex<Option<JobState>>>,
}
///
impl AsyncBranchesJob {
///
pub fn new(repo: RepoPath, local_branches: bool) -> Self {
Self {
state: Arc::new(Mutex::new(Some(JobState::Request {
repo,
local_branches,
}))),
}
}
///
pub fn result(&self) -> Option<Result<Vec<BranchInfo>>> {
if let Ok(mut state) = self.state.lock() {
if let Some(state) = state.take() {
return match state {
JobState::Request { .. } => None,
JobState::Response(result) => Some(result),
};
}
}
None
}
}
impl AsyncJob for AsyncBranchesJob {
type Notification = AsyncGitNotification;
type Progress = ();
fn run(
&mut self,
_params: RunParams<Self::Notification, Self::Progress>,
) -> Result<Self::Notification> {
if let Ok(mut state) = self.state.lock() {
*state = state.take().map(|state| match state {
JobState::Request {
local_branches,
repo,
} => {
let branches =
get_branches_info(&repo, local_branches);
JobState::Response(branches)
}
JobState::Response(result) => {
JobState::Response(result)
}
});
}
Ok(AsyncGitNotification::Branches)
}
}

View file

@ -1,48 +1,47 @@
use crate::{
error::Result,
sync::{self, branch::get_branch_name},
error::Result,
sync::{self, branch::get_branch_name, RepoPathRef},
};
use sync::Head;
///
pub struct BranchName {
last_result: Option<(Head, String)>,
repo_path: String,
last_result: Option<(Head, String)>,
repo: RepoPathRef,
}
impl BranchName {
///
pub fn new(path: &str) -> Self {
Self {
repo_path: path.to_string(),
last_result: None,
}
}
///
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_path.as_str())?;
///
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());
}
}
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)
}
self.fetch(current_head)
}
///
pub fn last(&self) -> Option<String> {
self.last_result.as_ref().map(|last| last.1.clone())
}
///
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_path.as_str())?;
self.last_result = Some((head, name.clone()));
Ok(name)
}
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)
}
}

View file

@ -1,105 +1,146 @@
use crate::{
error::Result,
sync::{self, CommitId},
AsyncGitNotification, StatusItem, CWD,
error::Result,
sync::{self, commit_files::OldNew, CommitId, RepoPath},
AsyncGitNotification, StatusItem,
};
use crossbeam_channel::Sender;
use std::sync::{
atomic::{AtomicUsize, Ordering},
Arc, Mutex,
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<CommitId, ResultType>>>>,
sender: Sender<AsyncGitNotification>,
pending: Arc<AtomicUsize>,
current:
Arc<Mutex<Option<Request<CommitFilesParams, ResultType>>>>,
sender: Sender<AsyncGitNotification>,
pending: Arc<AtomicUsize>,
repo: RepoPath,
}
impl AsyncCommitFiles {
///
pub fn new(sender: &Sender<AsyncGitNotification>) -> Self {
Self {
current: Arc::new(Mutex::new(None)),
sender: sender.clone(),
pending: Arc::new(AtomicUsize::new(0)),
}
}
///
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(
&mut self,
) -> Result<Option<(CommitId, ResultType)>> {
let c = self.current.lock()?;
///
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()))))
}
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 is_pending(&self) -> bool {
self.pending.load(Ordering::Relaxed) > 0
}
///
pub fn fetch(&mut self, id: CommitId) -> Result<()> {
if self.is_pending() {
return Ok(());
}
///
pub fn fetch(&self, params: CommitFilesParams) -> Result<()> {
if self.is_pending() {
return Ok(());
}
log::trace!("request: {}", id.to_string());
log::trace!("request: {params:?}");
{
let current = self.current.lock()?;
if let Some(c) = &*current {
if c.0 == id {
return Ok(());
}
}
}
{
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 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);
self.pending.fetch_add(1, Ordering::Relaxed);
rayon_core::spawn(move || {
Self::fetch_helper(id, &arc_current)
.expect("failed to fetch");
rayon_core::spawn(move || {
Self::fetch_helper(&repo, params, &arc_current)
.expect("failed to fetch");
arc_pending.fetch_sub(1, Ordering::Relaxed);
arc_pending.fetch_sub(1, Ordering::Relaxed);
sender
.send(AsyncGitNotification::CommitFiles)
.expect("error sending");
});
sender
.send(AsyncGitNotification::CommitFiles)
.expect("error sending");
});
Ok(())
}
Ok(())
}
fn fetch_helper(
id: CommitId,
arc_current: &Arc<
Mutex<Option<Request<CommitId, ResultType>>>,
>,
) -> Result<()> {
let res = sync::get_commit_files(CWD, id)?;
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: {} ({})",
id.to_string(),
res.len()
);
log::trace!("get_commit_files: {:?} ({})", params, res.len());
{
let mut current = arc_current.lock()?;
*current = Some(Request(id, res));
}
{
let mut current = arc_current.lock()?;
*current = Some(Request(params, res));
}
Ok(())
}
Ok(())
}
}

View file

@ -1,195 +1,221 @@
use crate::{
error::Result,
hash,
sync::{self, CommitId},
AsyncGitNotification, FileDiff, CWD,
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,
},
hash::Hash,
sync::{
atomic::{AtomicUsize, Ordering},
Arc, Mutex,
},
};
///
#[derive(Hash, Clone, PartialEq)]
#[derive(Debug, Hash, Clone, PartialEq, Eq)]
pub enum DiffType {
/// diff in a given commit
Commit(CommitId),
/// diff against staged file
Stage,
/// diff against file in workdir
WorkDir,
/// 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(Hash, Clone, PartialEq)]
#[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,
/// 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,
hash: u64,
result: 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>,
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(sender: &Sender<AsyncGitNotification>) -> Self {
Self {
current: Arc::new(Mutex::new(Request(0, None))),
last: Arc::new(Mutex::new(None)),
sender: sender.clone(),
pending: Arc::new(AtomicUsize::new(0)),
}
}
///
pub fn new(
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(&mut self) -> Result<Option<(DiffParams, FileDiff)>> {
let last = self.last.lock()?;
///
pub fn last(&self) -> Result<Option<(DiffParams, FileDiff)>> {
let last = self.last.lock()?;
Ok(last.clone().map(|res| (res.params, res.result)))
}
Ok(last.clone().map(|res| (res.params, res.result)))
}
///
pub fn refresh(&mut self) -> Result<()> {
if let Ok(Some(param)) = self.get_last_param() {
self.clear_current()?;
self.request(param)?;
}
Ok(())
}
///
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 is_pending(&self) -> bool {
self.pending.load(Ordering::Relaxed) > 0
}
///
pub fn request(
&mut self,
params: DiffParams,
) -> Result<Option<FileDiff>> {
log::trace!("request");
///
pub fn request(
&self,
params: DiffParams,
) -> Result<Option<FileDiff>> {
log::trace!("request {params:?}");
let hash = hash(&params);
let hash = hash(&params);
{
let mut current = self.current.lock()?;
{
let mut current = self.current.lock()?;
if current.0 == hash {
return Ok(current.1.clone());
}
if current.0 == hash {
return Ok(current.1.clone());
}
current.0 = hash;
current.1 = None;
}
current.0 = hash;
current.1 = None;
}
let arc_current = Arc::clone(&self.current);
let arc_last = Arc::clone(&self.last);
let sender = self.sender.clone();
let arc_pending = Arc::clone(&self.pending);
let 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);
self.pending.fetch_add(1, Ordering::Relaxed);
rayon_core::spawn(move || {
let notify = Self::get_diff_helper(
params,
&arc_last,
&arc_current,
hash,
);
rayon_core::spawn(move || {
let notify = Self::get_diff_helper(
&repo,
params,
&arc_last,
&arc_current,
hash,
);
let notify = match notify {
Err(err) => {
log::error!("get_diff_helper error: {}", err);
true
}
Ok(notify) => notify,
};
let notify = match notify {
Err(e) => {
log::error!("get_diff_helper error: {e}");
true
}
Ok(notify) => notify,
};
arc_pending.fetch_sub(1, Ordering::Relaxed);
arc_pending.fetch_sub(1, Ordering::Relaxed);
sender
.send(if notify {
AsyncGitNotification::Diff
} else {
AsyncGitNotification::FinishUnchanged
})
.expect("error sending diff");
});
sender
.send(if notify {
AsyncGitNotification::Diff
} else {
AsyncGitNotification::FinishUnchanged
})
.expect("error sending diff");
});
Ok(None)
}
Ok(None)
}
fn get_diff_helper(
params: DiffParams,
arc_last: &Arc<
Mutex<Option<LastResult<DiffParams, FileDiff>>>,
>,
arc_current: &Arc<Mutex<Request<u64, FileDiff>>>,
hash: u64,
) -> Result<bool> {
let res = match params.diff_type {
DiffType::Stage => {
sync::diff::get_diff(CWD, &params.path, true)?
}
DiffType::WorkDir => {
sync::diff::get_diff(CWD, &params.path, false)?
}
DiffType::Commit(id) => sync::diff::get_diff_commit(
CWD,
id,
params.path.clone(),
)?,
};
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,
&params.path,
true,
Some(params.options),
)?,
DiffType::WorkDir => sync::diff::get_diff(
repo_path,
&params.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 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,
hash,
params,
});
}
{
let mut last = arc_last.lock()?;
*last = Some(LastResult {
result: res,
params,
});
}
Ok(notify)
}
Ok(notify)
}
fn get_last_param(&self) -> Result<Option<DiffParams>> {
Ok(self.last.lock()?.clone().map(|e| e.params))
}
fn get_last_param(&self) -> Result<Option<DiffParams>> {
Ok(self.last.lock()?.clone().map(|e| e.params))
}
fn clear_current(&mut self) -> Result<()> {
let mut current = self.current.lock()?;
current.0 = 0;
current.1 = None;
Ok(())
}
fn clear_current(&self) -> Result<()> {
let mut current = self.current.lock()?;
current.0 = 0;
current.1 = None;
Ok(())
}
}

View file

@ -1,60 +1,350 @@
#![allow(renamed_and_removed_lints, clippy::unknown_clippy_lints)]
use std::{num::TryFromIntError, string::FromUtf8Error};
use std::{
num::TryFromIntError, path::StripPrefixError,
string::FromUtf8Error,
};
use thiserror::Error;
///
#[derive(Error, Debug)]
pub enum Error {
#[error("`{0}`")]
Generic(String),
pub enum GixError {
///
#[error("gix::discover error: {0}")]
Discover(#[from] Box<gix::discover::Error>),
#[error("git: no head found")]
NoHead,
///
#[error("gix::head::peel::to_commit error: {0}")]
HeadPeelToCommit(#[from] gix::head::peel::to_commit::Error),
#[error("git: remote url not found")]
UnknownRemote,
///
#[error("gix::object::find::existing::with_conversion::Error error: {0}")]
ObjectFindExistingWithConversion(
#[from] gix::object::find::existing::with_conversion::Error,
),
#[error("git: inconclusive remotes")]
NoDefaultRemoteFound,
///
#[error("gix::objs::decode::Error error: {0}")]
ObjsDecode(#[from] gix::objs::decode::Error),
#[error("git: work dir error")]
NoWorkDir,
///
#[error("gix::pathspec::init::Error error: {0}")]
PathspecInit(#[from] Box<gix::pathspec::init::Error>),
#[error("git: uncommitted changes")]
UncommittedChanges,
///
#[error("gix::reference::find::existing error: {0}")]
ReferenceFindExisting(
#[from] gix::reference::find::existing::Error,
),
#[error("git: can\u{2019}t run blame on a binary file")]
NoBlameOnBinaryFile,
///
#[error("gix::reference::head_tree_id::Error error: {0}")]
ReferenceHeadTreeId(#[from] gix::reference::head_tree_id::Error),
#[error("binary file")]
BinaryFile,
///
#[error("gix::reference::iter::Error error: {0}")]
ReferenceIter(#[from] gix::reference::iter::Error),
#[error("io error:{0}")]
Io(#[from] std::io::Error),
///
#[error("gix::reference::iter::init::Error error: {0}")]
ReferenceIterInit(#[from] gix::reference::iter::init::Error),
#[error("git error:{0}")]
Git(#[from] git2::Error),
///
#[error("gix::revision::walk error: {0}")]
RevisionWalk(#[from] gix::revision::walk::Error),
#[error("utf8 error:{0}")]
Utf8Conversion(#[from] FromUtf8Error),
///
#[error("gix::status::Error error: {0}")]
Status(#[from] Box<gix::status::Error>),
#[error("TryFromInt error:{0}")]
IntConversion(#[from] TryFromIntError),
///
#[error("gix::status::index_worktree::Error error: {0}")]
StatusIndexWorktree(
#[from] Box<gix::status::index_worktree::Error>,
),
#[error("EasyCast error:{0}")]
EasyCast(#[from] easy_cast::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))
}
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))
}
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))
}
}

View file

@ -1,159 +0,0 @@
use crate::{
error::{Error, Result},
sync::{
cred::BasicAuthCredential,
remotes::{fetch, push::ProgressNotification},
},
AsyncGitNotification, RemoteProgress, CWD,
};
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>,
}
#[derive(Default, Clone, Debug)]
struct FetchState {
request: FetchRequest,
}
///
pub struct AsyncFetch {
state: Arc<Mutex<Option<FetchState>>>,
last_result: Arc<Mutex<Option<(usize, String)>>>,
progress: Arc<Mutex<Option<ProgressNotification>>>,
sender: Sender<AsyncGitNotification>,
}
impl AsyncFetch {
///
pub fn new(sender: &Sender<AsyncGitNotification>) -> Self {
Self {
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(&mut self, params: FetchRequest) -> Result<()> {
log::trace!("request");
if self.is_pending()? {
return Ok(());
}
self.set_request(&params)?;
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();
thread::spawn(move || {
let (progress_sender, receiver) = unbounded();
let handle = RemoteProgress::spawn_receiver_thread(
AsyncGitNotification::Fetch,
sender.clone(),
receiver,
arc_progress,
);
let res = fetch(
CWD,
&params.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::Fetch)
.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 {
request: params.clone(),
});
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(())
}
}

69
asyncgit/src/fetch_job.rs Normal file
View file

@ -0,0 +1,69 @@
//!
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)
}
}

View file

@ -0,0 +1,200 @@
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, &params)
}
JobState::Response(result) => {
JobState::Response(result)
}
});
}
Ok(AsyncGitNotification::CommitFilter)
}
}

View file

@ -1,35 +1,59 @@
//! asyncgit
/*!
`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(
unused_imports,
unused_must_use,
dead_code,
unstable_name_collisions,
unused_assignments
mismatched_lifetime_syntaxes,
unused_imports,
unused_must_use,
dead_code,
unstable_name_collisions,
unused_assignments,
deprecated
)]
#![deny(unstable_name_collisions)]
#![deny(clippy::all, clippy::perf, clippy::nursery, clippy::pedantic)]
#![deny(clippy::filetype_is_file)]
#![deny(clippy::cargo)]
#![deny(clippy::unwrap_used)]
#![deny(clippy::panic)]
#![deny(clippy::match_like_matches_macro)]
#![deny(clippy::needless_update)]
#![allow(clippy::module_name_repetitions)]
#![allow(clippy::must_use_candidate)]
#![allow(clippy::missing_errors_doc)]
//TODO: get this in someday since expect still leads us to crashes sometimes
// #![deny(clippy::expect_used)]
#![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;
mod fetch_job;
mod filter_commits;
mod progress;
mod pull;
mod push;
mod push_tags;
pub mod remote_progress;
@ -38,65 +62,92 @@ mod revlog;
mod status;
pub mod sync;
mod tags;
mod treefiles;
pub use crate::{
blame::{AsyncBlame, BlameParams},
commit_files::AsyncCommitFiles,
diff::{AsyncDiff, DiffParams, DiffType},
fetch::{AsyncFetch, FetchRequest},
push::{AsyncPush, PushRequest},
push_tags::{AsyncPushTags, PushTagsRequest},
remote_progress::{RemoteProgress, RemoteProgressState},
revlog::{AsyncLog, FetchStatus},
status::{AsyncStatus, StatusParams},
sync::{
diff::{DiffLine, DiffLineType, FileDiff},
status::{StatusItem, StatusItemType},
},
tags::AsyncTags,
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},
collections::hash_map::DefaultHasher,
hash::{Hash, Hasher},
};
/// this type is used to communicate events back through the channel
#[derive(Copy, Clone, Debug, PartialEq)]
#[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,
///
CommitFiles,
///
Tags,
///
Push,
///
PushTags,
///
Fetch,
///
Blame,
///
//TODO: this does not belong here
SyntaxHighlighting,
///
//TODO: this does not belong here
RemoteTags,
/// 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,
}
/// current working directory `./`
pub static CWD: &str = "./";
/// 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()
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
}

View file

@ -4,45 +4,51 @@ use easy_cast::{Conv, ConvFloat};
use std::cmp;
///
#[derive(Clone, Debug)]
#[derive(Clone, Copy, Default, Debug, PartialEq, Eq)]
pub struct ProgressPercent {
/// percent 0..100
pub progress: u8,
/// 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::conv_nearest(progress);
Self { progress }
}
///
pub const fn empty() -> Self {
Self { progress: 0 }
}
///
pub const fn full() -> Self {
Self { progress: 100 }
}
///
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::*;
use super::*;
#[test]
fn test_progress_zero_total() {
let prog = ProgressPercent::new(1, 0);
#[test]
fn test_progress_zero_total() {
let prog = ProgressPercent::new(1, 0);
assert_eq!(prog.progress, 100);
}
assert_eq!(prog.progress, 100);
}
#[test]
fn test_progress_rounding() {
let prog = ProgressPercent::new(2, 10);
#[test]
fn test_progress_zero_all() {
let prog = ProgressPercent::new(0, 0);
assert_eq!(prog.progress, 100);
}
assert_eq!(prog.progress, 20);
}
#[test]
fn test_progress_rounding() {
let prog = ProgressPercent::new(2, 10);
assert_eq!(prog.progress, 20);
}
}

163
asyncgit/src/pull.rs Normal file
View file

@ -0,0 +1,163 @@
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(&params)?;
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,
&params.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(())
}
}

View file

@ -1,163 +1,174 @@
use crate::{
error::{Error, Result},
sync::{
cred::BasicAuthCredential, remotes::push::push,
remotes::push::ProgressNotification,
},
AsyncGitNotification, RemoteProgress, CWD,
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,
sync::{Arc, Mutex},
thread,
};
///
#[derive(Default, Clone, Debug)]
pub struct PushRequest {
///
pub remote: String,
///
pub branch: String,
///
pub force: bool,
///
pub basic_credential: Option<BasicAuthCredential>,
///
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 {
request: PushRequest,
}
struct PushState {}
///
pub struct AsyncPush {
state: Arc<Mutex<Option<PushState>>>,
last_result: Arc<Mutex<Option<String>>>,
progress: Arc<Mutex<Option<ProgressNotification>>>,
sender: Sender<AsyncGitNotification>,
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(sender: &Sender<AsyncGitNotification>) -> Self {
Self {
state: Arc::new(Mutex::new(None)),
last_result: Arc::new(Mutex::new(None)),
progress: Arc::new(Mutex::new(None)),
sender: sender.clone(),
}
}
///
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 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 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 progress(&self) -> Result<Option<RemoteProgress>> {
let res = self.progress.lock()?;
Ok(res.as_ref().map(|progress| progress.clone().into()))
}
///
pub fn request(&mut self, params: PushRequest) -> Result<()> {
log::trace!("request");
///
pub fn request(&self, params: PushRequest) -> Result<()> {
log::trace!("request");
if self.is_pending()? {
return Ok(());
}
if self.is_pending()? {
return Ok(());
}
self.set_request(&params)?;
RemoteProgress::set_progress(&self.progress, None)?;
self.set_request(&params)?;
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 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();
thread::spawn(move || {
let (progress_sender, receiver) = unbounded();
let handle = RemoteProgress::spawn_receiver_thread(
AsyncGitNotification::Push,
sender.clone(),
receiver,
arc_progress,
);
let handle = RemoteProgress::spawn_receiver_thread(
AsyncGitNotification::Push,
sender.clone(),
receiver,
arc_progress,
);
let res = push(
CWD,
params.remote.as_str(),
params.branch.as_str(),
params.force,
params.basic_credential.clone(),
Some(progress_sender.clone()),
);
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");
progress_sender
.send(ProgressNotification::Done)
.expect("closing send failed");
handle.join().expect("joining thread failed");
handle.join().expect("joining thread failed");
Self::set_result(&arc_res, res).expect("result error");
Self::set_result(&arc_res, res).expect("result error");
Self::clear_request(&arc_state).expect("clear error");
Self::clear_request(&arc_state).expect("clear error");
sender
.send(AsyncGitNotification::Push)
.expect("error sending push");
});
sender
.send(AsyncGitNotification::Push)
.expect("error sending push");
});
Ok(())
}
Ok(())
}
fn set_request(&self, params: &PushRequest) -> Result<()> {
let mut state = self.state.lock()?;
fn set_request(&self, _params: &PushRequest) -> Result<()> {
let mut state = self.state.lock()?;
if state.is_some() {
return Err(Error::Generic("pending request".into()));
}
if state.is_some() {
return Err(Error::Generic("pending request".into()));
}
*state = Some(PushState {
request: params.clone(),
});
*state = Some(PushState {});
Ok(())
}
Ok(())
}
fn clear_request(
state: &Arc<Mutex<Option<PushState>>>,
) -> Result<()> {
let mut state = state.lock()?;
fn clear_request(
state: &Arc<Mutex<Option<PushState>>>,
) -> Result<()> {
let mut state = state.lock()?;
*state = None;
*state = None;
Ok(())
}
Ok(())
}
fn set_result(
arc_result: &Arc<Mutex<Option<String>>>,
res: Result<()>,
) -> Result<()> {
let mut last_res = arc_result.lock()?;
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())
}
};
*last_res = match res {
Ok(()) => None,
Err(e) => {
log::error!("push error: {e}");
Some(e.to_string())
}
};
Ok(())
}
Ok(())
}
}

View file

@ -1,153 +1,157 @@
use crate::{
error::{Error, Result},
sync::{
cred::BasicAuthCredential,
remotes::tags::{push_tags, PushTagsProgress},
},
AsyncGitNotification, RemoteProgress, CWD,
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,
sync::{Arc, Mutex},
thread,
};
///
#[derive(Default, Clone, Debug)]
pub struct PushTagsRequest {
///
pub remote: String,
///
pub basic_credential: Option<BasicAuthCredential>,
///
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 {
request: PushTagsRequest,
}
struct PushState {}
///
pub struct AsyncPushTags {
state: Arc<Mutex<Option<PushState>>>,
last_result: Arc<Mutex<Option<String>>>,
progress: Arc<Mutex<Option<PushTagsProgress>>>,
sender: Sender<AsyncGitNotification>,
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(sender: &Sender<AsyncGitNotification>) -> Self {
Self {
state: Arc::new(Mutex::new(None)),
last_result: Arc::new(Mutex::new(None)),
progress: Arc::new(Mutex::new(None)),
sender: sender.clone(),
}
}
///
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 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 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 progress(&self) -> Result<Option<PushTagsProgress>> {
let res = self.progress.lock()?;
Ok(*res)
}
///
pub fn request(&mut self, params: PushTagsRequest) -> Result<()> {
log::trace!("request");
///
pub fn request(&self, params: PushTagsRequest) -> Result<()> {
log::trace!("request");
if self.is_pending()? {
return Ok(());
}
if self.is_pending()? {
return Ok(());
}
self.set_request(&params)?;
RemoteProgress::set_progress(&self.progress, None)?;
self.set_request(&params)?;
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 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();
thread::spawn(move || {
let (progress_sender, receiver) = unbounded();
let handle = RemoteProgress::spawn_receiver_thread(
AsyncGitNotification::PushTags,
sender.clone(),
receiver,
arc_progress,
);
let handle = RemoteProgress::spawn_receiver_thread(
AsyncGitNotification::PushTags,
sender.clone(),
receiver,
arc_progress,
);
let res = push_tags(
CWD,
params.remote.as_str(),
params.basic_credential.clone(),
Some(progress_sender),
);
let res = push_tags(
&repo,
params.remote.as_str(),
params.basic_credential.clone(),
Some(progress_sender),
);
handle.join().expect("joining thread failed");
handle.join().expect("joining thread failed");
Self::set_result(&arc_res, res).expect("result error");
Self::set_result(&arc_res, res).expect("result error");
Self::clear_request(&arc_state).expect("clear error");
Self::clear_request(&arc_state).expect("clear error");
sender
.send(AsyncGitNotification::PushTags)
.expect("error sending push");
});
sender
.send(AsyncGitNotification::PushTags)
.expect("error sending push");
});
Ok(())
}
Ok(())
}
fn set_request(&self, params: &PushTagsRequest) -> Result<()> {
let mut state = self.state.lock()?;
fn set_request(&self, _params: &PushTagsRequest) -> Result<()> {
let mut state = self.state.lock()?;
if state.is_some() {
return Err(Error::Generic("pending request".into()));
}
if state.is_some() {
return Err(Error::Generic("pending request".into()));
}
*state = Some(PushState {
request: params.clone(),
});
*state = Some(PushState {});
Ok(())
}
Ok(())
}
fn clear_request(
state: &Arc<Mutex<Option<PushState>>>,
) -> Result<()> {
let mut state = state.lock()?;
fn clear_request(
state: &Arc<Mutex<Option<PushState>>>,
) -> Result<()> {
let mut state = state.lock()?;
*state = None;
*state = None;
Ok(())
}
Ok(())
}
fn set_result(
arc_result: &Arc<Mutex<Option<String>>>,
res: Result<()>,
) -> Result<()> {
let mut last_res = arc_result.lock()?;
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())
}
};
*last_res = match res {
Ok(()) => None,
Err(e) => {
log::error!("push error: {e}");
Some(e.to_string())
}
};
Ok(())
}
Ok(())
}
}

View file

@ -1,151 +1,148 @@
//!
use crate::{
error::Result,
progress::ProgressPercent,
sync::remotes::push::{AsyncProgress, ProgressNotification},
AsyncGitNotification,
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},
time::Duration,
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,
///
PackingAddingObject,
///
PackingDeltafiction,
///
Pushing,
/// fetch progress
Transfer,
/// remote progress done
Done,
}
///
#[derive(Clone, Debug)]
pub struct RemoteProgress {
///
pub state: RemoteProgressState,
///
pub progress: ProgressPercent,
///
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 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 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()?;
pub(crate) fn set_progress<T>(
progress: &Arc<Mutex<Option<T>>>,
state: Option<T>,
) -> Result<()> {
let mut progress = progress.lock()?;
*progress = state;
*progress = state;
Ok(())
}
Ok(())
}
/// spawn thread to listen to progress notifcations 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");
/// 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");
//NOTE: for better debugging
thread::sleep(Duration::from_millis(1));
thread::yield_now();
if update.is_done() {
break;
}
}
Err(e) => {
log::error!(
"remote progress receiver error: {}",
e
);
break;
}
}
})
}
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),
}
}
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),
}
}
}

View file

@ -1,74 +1,88 @@
//!
use crate::{
asyncjob::AsyncJob,
error::Result,
sync::cred::BasicAuthCredential,
sync::remotes::{get_default_remote, tags_missing_remote},
CWD,
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>>),
Request(Option<BasicAuthCredential>),
Response(Result<Vec<String>>),
}
///
#[derive(Clone, Default)]
#[derive(Clone)]
pub struct AsyncRemoteTagsJob {
state: Arc<Mutex<Option<JobState>>>,
state: Arc<Mutex<Option<JobState>>>,
repo: RepoPath,
}
///
impl AsyncRemoteTagsJob {
///
pub fn new(
basic_credential: Option<BasicAuthCredential>,
) -> Self {
Self {
state: Arc::new(Mutex::new(Some(JobState::Request(
basic_credential,
)))),
}
}
///
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),
};
}
}
///
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
}
None
}
}
impl AsyncJob for AsyncRemoteTagsJob {
fn run(&mut self) {
if let Ok(mut state) = self.state.lock() {
*state = state.take().map(|state| match state {
JobState::Request(basic_credential) => {
let result =
get_default_remote(CWD).and_then(|remote| {
tags_missing_remote(
CWD,
&remote,
basic_credential,
)
});
type Notification = AsyncGitNotification;
type Progress = ();
JobState::Response(result)
}
JobState::Response(result) => {
JobState::Response(result)
}
});
}
}
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)
}
}

View file

@ -1,201 +1,405 @@
use crate::{
error::Result,
sync::{utils::repo, CommitId, LogWalker, LogWalkerFilter},
AsyncGitNotification, CWD,
error::Result,
sync::{
gix_repo, repo, CommitId, LogWalker, LogWalkerWithoutFilter,
RepoPath, SharedCommitFilterFn,
},
AsyncGitNotification, Error,
};
use crossbeam_channel::Sender;
use git2::Oid;
use scopetime::scope_time;
use std::{
sync::{
atomic::{AtomicBool, Ordering},
Arc, Mutex,
},
thread,
time::Duration,
sync::{
atomic::{AtomicBool, Ordering},
Arc, Mutex,
},
thread,
time::{Duration, Instant},
};
///
#[derive(PartialEq)]
#[derive(PartialEq, Eq, Debug)]
pub enum FetchStatus {
/// previous fetch still running
Pending,
/// no change expected
NoChange,
/// new walk was started
Started,
/// 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<Vec<CommitId>>>,
sender: Sender<AsyncGitNotification>,
pending: Arc<AtomicBool>,
background: Arc<AtomicBool>,
filter: Option<LogWalkerFilter>,
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_millis(1000);
static SLEEP_BACKGROUND: Duration = Duration::from_secs(1);
impl AsyncLog {
///
pub fn new(
sender: &Sender<AsyncGitNotification>,
filter: Option<LogWalkerFilter>,
) -> Self {
Self {
current: Arc::new(Mutex::new(Vec::new())),
sender: sender.clone(),
pending: Arc::new(AtomicBool::new(false)),
background: Arc::new(AtomicBool::new(false)),
filter,
}
}
///
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(&mut self) -> Result<usize> {
Ok(self.current.lock()?.len())
}
///
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>> {
let list = self.current.lock()?;
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_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!")));
}
///
pub fn position(&self, id: CommitId) -> Result<Option<usize>> {
let list = self.current.lock()?;
let position = list.iter().position(|&x| x == id);
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())
}
Ok(position)
}
///
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!")));
}
///
pub fn is_pending(&self) -> bool {
self.pending.load(Ordering::Relaxed)
}
let list = &self.current.lock()?.commits;
Ok(list.clone())
}
///
pub fn set_background(&mut self) {
self.background.store(true, Ordering::Relaxed);
}
///
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)
}
///
fn current_head(&self) -> Result<CommitId> {
Ok(self
.current
.lock()?
.first()
.map_or(Oid::zero().into(), |f| *f))
}
///
pub fn get_last_duration(&self) -> Result<Duration> {
Ok(self.current.lock()?.duration)
}
///
fn head_changed(&self) -> Result<bool> {
if let Ok(head) = repo(CWD)?.head() {
if let Some(head) = head.target() {
return Ok(head != self.current_head()?.into());
}
}
Ok(false)
}
///
pub fn is_pending(&self) -> bool {
self.pending.load(Ordering::Relaxed)
}
///
pub fn fetch(&mut self) -> Result<FetchStatus> {
self.background.store(false, Ordering::Relaxed);
///
pub fn set_background(&self) {
self.background.store(true, Ordering::Relaxed);
}
if self.is_pending() {
return Ok(FetchStatus::Pending);
}
///
fn current_head(&self) -> Result<Option<CommitId>> {
Ok(*self.current_head.lock()?)
}
if !self.head_changed()? {
return Ok(FetchStatus::NoChange);
}
///
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)
}
self.clear()?;
///
pub fn fetch(&self) -> Result<FetchStatus> {
self.background.store(false, Ordering::Relaxed);
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);
if self.is_pending() {
return Ok(FetchStatus::Pending);
}
self.pending.store(true, Ordering::Relaxed);
if !self.head_changed()? {
return Ok(FetchStatus::NoChange);
}
let filter = self.filter.clone();
self.pending.store(true, Ordering::Relaxed);
rayon_core::spawn(move || {
scope_time!("async::revlog");
self.clear()?;
Self::fetch_helper(
&arc_current,
&arc_background,
&sender,
filter,
)
.expect("failed to fetch");
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();
arc_pending.store(false, Ordering::Relaxed);
if let Ok(head) = repo(&self.repo)?.head() {
*self.current_head.lock()? =
head.target().map(CommitId::new);
}
Self::notify(&sender);
});
rayon_core::spawn(move || {
scope_time!("async::revlog");
Ok(FetchStatus::Started)
}
Self::fetch_helper(
&repo_path,
&arc_current,
&arc_background,
&sender,
filter,
)
.expect("failed to fetch");
fn fetch_helper(
arc_current: &Arc<Mutex<Vec<CommitId>>>,
arc_background: &Arc<AtomicBool>,
sender: &Sender<AsyncGitNotification>,
filter: Option<LogWalkerFilter>,
) -> Result<()> {
let mut entries = Vec::with_capacity(LIMIT_COUNT);
let r = repo(CWD)?;
let mut walker =
LogWalker::new(&r, LIMIT_COUNT)?.filter(filter);
loop {
entries.clear();
let res_is_err = walker.read(&mut entries).is_err();
arc_pending.store(false, Ordering::Relaxed);
if !res_is_err {
let mut current = arc_current.lock()?;
current.extend(entries.iter());
}
Self::notify(&sender);
});
if res_is_err || entries.len() <= 1 {
break;
}
Self::notify(sender);
Ok(FetchStatus::Started)
}
let sleep_duration =
if arc_background.load(Ordering::Relaxed) {
SLEEP_BACKGROUND
} else {
SLEEP_FOREGROUND
};
thread::sleep(sleep_duration);
}
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,
)
},
)
}
Ok(())
}
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();
fn clear(&mut self) -> Result<()> {
self.current.lock()?.clear();
Ok(())
}
let mut entries = vec![CommitId::default(); LIMIT_COUNT];
entries.resize(0, CommitId::default());
fn notify(sender: &Sender<AsyncGitNotification>) {
sender
.send(AsyncGitNotification::Log)
.expect("error sending");
}
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(), ());
}
}

View file

@ -1,169 +1,191 @@
use crate::{
error::Result,
hash,
sync::{self, status::StatusType},
AsyncGitNotification, StatusItem, CWD,
error::Result,
hash,
sync::{
self, status::StatusType, RepoPath, ShowUntrackedFilesConfig,
},
AsyncGitNotification, StatusItem,
};
use crossbeam_channel::Sender;
use std::{
hash::Hash,
sync::{
atomic::{AtomicUsize, Ordering},
Arc, Mutex,
},
time::{SystemTime, UNIX_EPOCH},
hash::Hash,
sync::{
atomic::{AtomicU64, AtomicUsize, Ordering},
Arc, Mutex,
},
};
fn current_tick() -> u128 {
SystemTime::now()
.duration_since(UNIX_EPOCH)
.expect("time before unix epoch!")
.as_millis()
}
#[derive(Default, Hash, Clone)]
pub struct Status {
pub items: Vec<StatusItem>,
pub items: Vec<StatusItem>,
}
///
#[derive(Default, Hash, Copy, Clone, PartialEq)]
#[derive(Default, Hash, Copy, Clone, PartialEq, Eq)]
pub struct StatusParams {
tick: u128,
status_type: StatusType,
status_type: StatusType,
config: Option<ShowUntrackedFilesConfig>,
}
impl StatusParams {
///
pub fn new(status_type: StatusType) -> Self {
Self {
tick: current_tick(),
status_type,
}
}
///
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>,
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(sender: Sender<AsyncGitNotification>) -> Self {
Self {
current: Arc::new(Mutex::new(Request(0, None))),
last: Arc::new(Mutex::new(Status::default())),
sender,
pending: Arc::new(AtomicUsize::new(0)),
}
}
///
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(&mut self) -> Result<Status> {
let last = self.last.lock()?;
Ok(last.clone())
}
///
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 is_pending(&self) -> bool {
self.pending.load(Ordering::Relaxed) > 0
}
///
pub fn fetch(
&mut self,
params: &StatusParams,
) -> Result<Option<Status>> {
if self.is_pending() {
log::trace!("request blocked, still pending");
return Ok(None);
}
///
pub fn fetch(
&self,
params: &StatusParams,
) -> Result<Option<Status>> {
if self.is_pending() {
log::trace!("request blocked, still pending");
return Ok(None);
}
let hash_request = hash(&params);
let generation = self.generation.load(Ordering::Relaxed);
let hash_request = hash(&(params, generation));
log::trace!(
"request: [hash: {}] (type: {:?})",
hash_request,
params.status_type,
);
log::trace!(
"request: [hash: {}] (type: {:?}, gen: {})",
hash_request,
params.status_type,
generation,
);
{
let mut current = self.current.lock()?;
{
let mut current = self.current.lock()?;
if current.0 == hash_request {
return Ok(current.1.clone());
}
if current.0 == hash_request {
return Ok(current.1.clone());
}
current.0 = hash_request;
current.1 = None;
}
current.0 = hash_request;
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 status_type = params.status_type;
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);
self.pending.fetch_add(1, Ordering::Relaxed);
rayon_core::spawn(move || {
let ok = Self::fetch_helper(
status_type,
hash_request,
&arc_current,
&arc_last,
)
.is_ok();
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}");
}
arc_pending.fetch_sub(1, Ordering::Relaxed);
// Increment generation to invalidate cache for next request
arc_generation.fetch_add(1, Ordering::Relaxed);
arc_pending.fetch_sub(1, Ordering::Relaxed);
if ok {
sender
.send(AsyncGitNotification::Status)
.expect("error sending status");
}
});
if let Err(e) = sender.send(AsyncGitNotification::Status)
{
log::error!("send status error: {e}");
}
});
Ok(None)
}
Ok(None)
}
fn fetch_helper(
status_type: StatusType,
hash_request: u64,
arc_current: &Arc<Mutex<Request<u64, Status>>>,
arc_last: &Arc<Mutex<Status>>,
) -> Result<()> {
let res = Self::get_status(status_type)?;
log::trace!(
"status fetched: {} (type: {:?})",
hash_request,
status_type,
);
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 current = arc_current.lock()?;
if current.0 == hash_request {
current.1 = Some(res.clone());
}
}
{
let mut last = arc_last.lock()?;
*last = res;
}
{
let mut last = arc_last.lock()?;
*last = res;
}
Ok(())
}
Ok(())
}
fn get_status(status_type: StatusType) -> Result<Status> {
Ok(Status {
items: sync::status::get_status(CWD, status_type)?,
})
}
fn get_status(
repo: &RepoPath,
status_type: StatusType,
config: Option<ShowUntrackedFilesConfig>,
) -> Result<Status> {
Ok(Status {
items: sync::status::get_status(
repo,
status_type,
config,
)?,
})
}
}

View file

@ -1,10 +1,11 @@
//! Sync git API for fetching a file blame
use super::{utils, CommitId};
use super::{utils, CommitId, RepoPath};
use crate::{
error::{Error, Result},
sync::get_commits_info,
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};
@ -13,205 +14,249 @@ 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,
///
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)>,
///
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: &str,
file_path: &str,
repo_path: &RepoPath,
file_path: &str,
commit_id: Option<CommitId>,
) -> Result<FileBlame> {
scope_time!("blame_file");
scope_time!("blame_file");
let repo = utils::repo(repo_path)?;
let repo = repo(repo_path)?;
let commit_id = utils::get_head_repo(&repo)?;
let commit_id = if let Some(commit_id) = commit_id {
commit_id
} else {
utils::get_head_repo(&repo)?
};
let spec = format!("{}:{}", commit_id.to_string(), file_path);
let spec =
format!("{}:{}", commit_id, fixup_windows_path(file_path));
let object = repo.revparse_single(&spec)?;
let blob = repo.find_blob(object.id())?;
let object = repo.revparse_single(&spec)?;
let blob = repo.find_blob(object.id())?;
if blob.is_binary() {
return Err(Error::NoBlameOnBinaryFile);
}
if blob.is_binary() {
return Err(Error::NoBlameOnBinaryFile);
}
let blame = repo.blame_file(Path::new(file_path), None)?;
let mut opts = BlameOptions::new();
opts.newest_commit(commit_id.into());
let reader = BufReader::new(blob.content());
let blame =
repo.blame_file(Path::new(file_path), Some(&mut opts))?;
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 reader = BufReader::new(blob.content());
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 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 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);
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();
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());
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(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,
};
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());
return (
Some(hunk),
line.unwrap_or_else(|_| "".into()),
);
}
}
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,
};
(None, line.unwrap_or_else(|_| "".into()))
})
.collect();
return (
Some(hunk),
line.unwrap_or_else(|_| String::new()),
);
}
}
let file_blame = FileBlame {
commit_id,
path: file_path.into(),
lines,
};
(None, line.unwrap_or_else(|_| String::new()))
})
.collect();
Ok(file_blame)
let file_blame = FileBlame {
commit_id,
path: file_path.into(),
lines,
};
Ok(file_blame)
}
#[cfg(test)]
mod tests {
use super::*;
use crate::error::Result;
use crate::sync::{
commit, stage_add_file, tests::repo_init_empty,
};
use std::{
fs::{File, OpenOptions},
io::Write,
path::Path,
};
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 = root.as_os_str().to_str().unwrap();
#[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!(matches!(blame_file(&repo_path, "foo"), Err(_)));
assert!(blame_file(repo_path, "foo", None).is_err());
File::create(&root.join(file_path))?
.write_all(b"line 1\n")?;
File::create(root.join(file_path))?.write_all(b"line 1\n")?;
stage_add_file(repo_path, file_path)?;
commit(repo_path, "first commit")?;
stage_add_file(repo_path, file_path)?;
commit(repo_path, "first commit")?;
let blame = blame_file(&repo_path, "foo")?;
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"
));
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))?;
let mut file = OpenOptions::new()
.append(true)
.open(root.join(file_path))?;
file.write(b"line 2\n")?;
file.write(b"line 2\n")?;
stage_add_file(repo_path, file_path)?;
commit(repo_path, "second commit")?;
stage_add_file(repo_path, file_path)?;
commit(repo_path, "second commit")?;
let blame = blame_file(&repo_path, "foo")?;
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"
));
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")?;
file.write(b"line 3\n")?;
let blame = blame_file(&repo_path, "foo")?;
let blame = blame_file(repo_path, "foo", None)?;
assert_eq!(blame.lines.len(), 2);
assert_eq!(blame.lines.len(), 2);
stage_add_file(repo_path, file_path)?;
commit(repo_path, "third commit")?;
stage_add_file(repo_path, file_path)?;
commit(repo_path, "third commit")?;
let blame = blame_file(&repo_path, "foo")?;
let blame = blame_file(repo_path, "foo", None)?;
assert_eq!(blame.lines.len(), 3);
assert_eq!(blame.lines.len(), 3);
Ok(())
}
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());
}
}

View file

@ -2,8 +2,8 @@
use super::BranchType;
use crate::{
error::{Error, Result},
sync::{merge_msg, utils, CommitId},
error::{Error, Result},
sync::{merge_msg, repository::repo, CommitId, RepoPath},
};
use git2::Commit;
use scopetime::scope_time;
@ -12,258 +12,270 @@ use scopetime::scope_time;
/// 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: &str,
branch_name: &str,
repo_path: &RepoPath,
branch_name: &str,
) -> Result<Option<CommitId>> {
scope_time!("merge_upstream_commit");
scope_time!("merge_upstream_commit");
let repo = utils::repo(repo_path)?;
let repo = repo(repo_path)?;
let branch = repo.find_branch(branch_name, BranchType::Local)?;
let upstream = branch.upstream()?;
let branch = repo.find_branch(branch_name, BranchType::Local)?;
let upstream = branch.upstream()?;
let upstream_commit = upstream.get().peel_to_commit()?;
let upstream_commit = upstream.get().peel_to_commit()?;
let annotated_upstream = repo
.reference_to_annotated_commit(&upstream.into_reference())?;
let annotated_upstream = repo
.reference_to_annotated_commit(&upstream.into_reference())?;
let (analysis, pref) =
repo.merge_analysis(&[&annotated_upstream])?;
let (analysis, pref) =
repo.merge_analysis(&[&annotated_upstream])?;
if !analysis.is_normal() {
return Err(Error::Generic(
"normal merge not possible".into(),
));
}
if !analysis.is_normal() {
return Err(Error::Generic(
"normal merge not possible".into(),
));
}
if analysis.is_fast_forward() && pref.is_fastforward_only() {
return Err(Error::Generic(
"ff merge would be possible".into(),
));
}
if analysis.is_fast_forward() && pref.is_fastforward_only() {
return Err(Error::Generic(
"ff merge would be possible".into(),
));
}
//TODO: support merge on unborn?
if analysis.is_unborn() {
return Err(Error::Generic("head is unborn".into()));
}
//TODO: support merge on unborn?
if analysis.is_unborn() {
return Err(Error::Generic("head is unborn".into()));
}
repo.merge(&[&annotated_upstream], None, None)?;
repo.merge(&[&annotated_upstream], None, None)?;
if !repo.index()?.has_conflicts() {
let msg = merge_msg(repo_path)?;
if !repo.index()?.has_conflicts() {
let msg = merge_msg(repo_path)?;
let commit_id =
commit_merge_with_head(&repo, &[upstream_commit], &msg)?;
let commit_id =
commit_merge_with_head(&repo, &[upstream_commit], &msg)?;
return Ok(Some(commit_id));
}
return Ok(Some(commit_id));
}
Ok(None)
Ok(None)
}
pub(crate) fn commit_merge_with_head(
repo: &git2::Repository,
commits: &[Commit],
msg: &str,
repo: &git2::Repository,
commits: &[Commit],
msg: &str,
) -> Result<CommitId> {
let signature =
crate::sync::commit::signature_allow_undefined_name(repo)?;
let mut index = repo.index()?;
let tree_id = index.write_tree()?;
let tree = repo.find_tree(tree_id)?;
let head_commit = repo.find_commit(
crate::sync::utils::get_head_repo(repo)?.into(),
)?;
let signature =
crate::sync::commit::signature_allow_undefined_name(repo)?;
let mut index = repo.index()?;
let tree_id = index.write_tree()?;
let tree = repo.find_tree(tree_id)?;
let head_commit = repo.find_commit(
crate::sync::utils::get_head_repo(repo)?.into(),
)?;
let mut parents = vec![&head_commit];
parents.extend(commits);
let mut parents = vec![&head_commit];
parents.extend(commits);
let commit_id = repo
.commit(
Some("HEAD"),
&signature,
&signature,
msg,
&tree,
parents.as_slice(),
)?
.into();
repo.cleanup_state()?;
Ok(commit_id)
let commit_id = repo
.commit(
Some("HEAD"),
&signature,
&signature,
msg,
&tree,
parents.as_slice(),
)?
.into();
repo.cleanup_state()?;
Ok(commit_id)
}
#[cfg(test)]
mod test {
use git2::Time;
use git2::Time;
use super::*;
use crate::sync::{
branch_compare_upstream,
remotes::{fetch, push::push},
tests::{
debug_cmd_print, get_commit_ids, repo_clone,
repo_init_bare, write_commit_file, write_commit_file_at,
},
RepoState,
};
use super::*;
use crate::sync::{
branch_compare_upstream,
remotes::{fetch, push::push_branch},
tests::{
debug_cmd_print, get_commit_ids, repo_clone,
repo_init_bare, write_commit_file, write_commit_file_at,
},
RepoState,
};
#[test]
fn test_merge_normal() {
let (r1_dir, _repo) = repo_init_bare().unwrap();
#[test]
fn test_merge_normal() {
let (r1_dir, _repo) = repo_init_bare().unwrap();
let (clone1_dir, clone1) =
repo_clone(r1_dir.path().to_str().unwrap()).unwrap();
let (clone1_dir, clone1) =
repo_clone(r1_dir.path().to_str().unwrap()).unwrap();
let (clone2_dir, clone2) =
repo_clone(r1_dir.path().to_str().unwrap()).unwrap();
let (clone2_dir, clone2) =
repo_clone(r1_dir.path().to_str().unwrap()).unwrap();
let clone2_dir = clone2_dir.path().to_str().unwrap();
let clone2_dir = clone2_dir.path().to_str().unwrap();
// clone1
// clone1
let commit1 = write_commit_file_at(
&clone1,
"test.txt",
"test",
"commit1",
Time::new(1, 0),
);
let commit1 = write_commit_file_at(
&clone1,
"test.txt",
"test",
"commit1",
Time::new(1, 0),
);
push(
clone1_dir.path().to_str().unwrap(),
"origin",
"master",
false,
None,
None,
)
.unwrap();
push_branch(
&clone1_dir.path().to_str().unwrap().into(),
"origin",
"master",
false,
false,
None,
None,
)
.unwrap();
// clone2
// clone2
let commit2 = write_commit_file_at(
&clone2,
"test2.txt",
"test",
"commit2",
Time::new(2, 0),
);
let commit2 = write_commit_file_at(
&clone2,
"test2.txt",
"test",
"commit2",
Time::new(2, 0),
);
//push should fail since origin diverged
assert!(push(
clone2_dir, "origin", "master", false, None, None,
)
.is_err());
//push should fail since origin diverged
assert!(push_branch(
&clone2_dir.into(),
"origin",
"master",
false,
false,
None,
None,
)
.is_err());
//lets fetch from origin
let bytes = fetch(clone2_dir, "master", None, None).unwrap();
assert!(bytes > 0);
//lets fetch from origin
let bytes =
fetch(&clone2_dir.into(), "master", None, None).unwrap();
assert!(bytes > 0);
//we should be one commit behind
assert_eq!(
branch_compare_upstream(clone2_dir, "master")
.unwrap()
.behind,
1
);
//we should be one commit behind
assert_eq!(
branch_compare_upstream(&clone2_dir.into(), "master")
.unwrap()
.behind,
1
);
let merge_commit =
merge_upstream_commit(clone2_dir, "master")
.unwrap()
.unwrap();
let merge_commit =
merge_upstream_commit(&clone2_dir.into(), "master")
.unwrap()
.unwrap();
let state = crate::sync::repo_state(clone2_dir).unwrap();
assert_eq!(state, RepoState::Clean);
let state =
crate::sync::repo_state(&clone2_dir.into()).unwrap();
assert_eq!(state, RepoState::Clean);
assert!(!clone2.head_detached().unwrap());
assert!(!clone2.head_detached().unwrap());
let commits = get_commit_ids(&clone2, 10);
assert_eq!(commits.len(), 3);
assert_eq!(commits[0], merge_commit);
assert_eq!(commits[1], commit2);
assert_eq!(commits[2], commit1);
let commits = get_commit_ids(&clone2, 10);
assert_eq!(commits.len(), 3);
assert_eq!(commits[0], merge_commit);
assert_eq!(commits[1], commit2);
assert_eq!(commits[2], commit1);
//verify commit msg
let details =
crate::sync::get_commit_details(clone2_dir, merge_commit)
.unwrap();
assert_eq!(
//verify commit msg
let details = crate::sync::get_commit_details(
&clone2_dir.into(),
merge_commit,
)
.unwrap();
assert_eq!(
details.message.unwrap().combine(),
String::from("Merge remote-tracking branch 'refs/remotes/origin/master'")
);
}
}
#[test]
fn test_merge_normal_non_ff() {
let (r1_dir, _repo) = repo_init_bare().unwrap();
#[test]
fn test_merge_normal_non_ff() {
let (r1_dir, _repo) = repo_init_bare().unwrap();
let (clone1_dir, clone1) =
repo_clone(r1_dir.path().to_str().unwrap()).unwrap();
let (clone1_dir, clone1) =
repo_clone(r1_dir.path().to_str().unwrap()).unwrap();
let (clone2_dir, clone2) =
repo_clone(r1_dir.path().to_str().unwrap()).unwrap();
let (clone2_dir, clone2) =
repo_clone(r1_dir.path().to_str().unwrap()).unwrap();
// clone1
// clone1
write_commit_file(
&clone1,
"test.bin",
"test\nfooo",
"commit1",
);
write_commit_file(
&clone1,
"test.bin",
"test\nfooo",
"commit1",
);
debug_cmd_print(
clone2_dir.path().to_str().unwrap(),
"git status",
);
debug_cmd_print(
&clone2_dir.path().to_str().unwrap().into(),
"git status",
);
push(
clone1_dir.path().to_str().unwrap(),
"origin",
"master",
false,
None,
None,
)
.unwrap();
push_branch(
&clone1_dir.path().to_str().unwrap().into(),
"origin",
"master",
false,
false,
None,
None,
)
.unwrap();
// clone2
// clone2
write_commit_file(
&clone2,
"test.bin",
"foobar\ntest",
"commit2",
);
write_commit_file(
&clone2,
"test.bin",
"foobar\ntest",
"commit2",
);
let bytes = fetch(
clone2_dir.path().to_str().unwrap(),
"master",
None,
None,
)
.unwrap();
assert!(bytes > 0);
let bytes = fetch(
&clone2_dir.path().to_str().unwrap().into(),
"master",
None,
None,
)
.unwrap();
assert!(bytes > 0);
let res = merge_upstream_commit(
clone2_dir.path().to_str().unwrap(),
"master",
)
.unwrap();
let res = merge_upstream_commit(
&clone2_dir.path().to_str().unwrap().into(),
"master",
)
.unwrap();
//this should not have commited cause we left conflicts behind
assert_eq!(res, None);
//this should not have committed cause we left conflicts behind
assert_eq!(res, None);
let state = crate::sync::repo_state(
clone2_dir.path().to_str().unwrap(),
)
.unwrap();
let state = crate::sync::repo_state(
&clone2_dir.path().to_str().unwrap().into(),
)
.unwrap();
//validate the repo is in a merge state now
assert_eq!(state, RepoState::Merge);
//validate the repo is in a merge state now
assert_eq!(state, RepoState::Merge);
//check that we still only have the first commit
let commits = get_commit_ids(&clone1, 10);
assert_eq!(commits.len(), 1);
}
//check that we still only have the first commit
let commits = get_commit_ids(&clone1, 10);
assert_eq!(commits.len(), 1);
}
}

View file

@ -2,141 +2,143 @@
use super::BranchType;
use crate::{
error::{Error, Result},
sync::utils,
error::{Error, Result},
sync::{repository::repo, RepoPath},
};
use scopetime::scope_time;
///
pub fn branch_merge_upstream_fastforward(
repo_path: &str,
branch: &str,
repo_path: &RepoPath,
branch: &str,
) -> Result<()> {
scope_time!("branch_merge_upstream");
scope_time!("branch_merge_upstream");
let repo = utils::repo(repo_path)?;
let repo = repo(repo_path)?;
let branch = repo.find_branch(branch, BranchType::Local)?;
let upstream = branch.upstream()?;
let branch = repo.find_branch(branch, BranchType::Local)?;
let upstream = branch.upstream()?;
let upstream_commit =
upstream.into_reference().peel_to_commit()?;
let upstream_commit =
upstream.into_reference().peel_to_commit()?;
let annotated =
repo.find_annotated_commit(upstream_commit.id())?;
let annotated =
repo.find_annotated_commit(upstream_commit.id())?;
let (analysis, pref) = repo.merge_analysis(&[&annotated])?;
let (analysis, pref) = repo.merge_analysis(&[&annotated])?;
if !analysis.is_fast_forward() {
return Err(Error::Generic(
"fast forward merge not possible".into(),
));
}
if !analysis.is_fast_forward() {
return Err(Error::Generic(
"fast forward merge not possible".into(),
));
}
if pref.is_no_fast_forward() {
return Err(Error::Generic("fast forward not wanted".into()));
}
if pref.is_no_fast_forward() {
return Err(Error::Generic("fast forward not wanted".into()));
}
//TODO: support merge on unborn
if analysis.is_unborn() {
return Err(Error::Generic("head is unborn".into()));
}
//TODO: support merge on unborn
if analysis.is_unborn() {
return Err(Error::Generic("head is unborn".into()));
}
repo.checkout_tree(upstream_commit.as_object(), None)?;
repo.checkout_tree(upstream_commit.as_object(), None)?;
repo.head()?.set_target(annotated.id(), "")?;
repo.head()?.set_target(annotated.id(), "")?;
Ok(())
Ok(())
}
#[cfg(test)]
pub mod test {
use super::*;
use crate::sync::{
remotes::{fetch, push::push},
tests::{
debug_cmd_print, get_commit_ids, repo_clone,
repo_init_bare, write_commit_file,
},
};
mod test {
use super::*;
use crate::sync::{
remotes::{fetch, push::push_branch},
tests::{
debug_cmd_print, get_commit_ids, repo_clone,
repo_init_bare, write_commit_file,
},
};
#[test]
fn test_merge_fastforward() {
let (r1_dir, _repo) = repo_init_bare().unwrap();
#[test]
fn test_merge_fastforward() {
let (r1_dir, _repo) = repo_init_bare().unwrap();
let (clone1_dir, clone1) =
repo_clone(r1_dir.path().to_str().unwrap()).unwrap();
let (clone1_dir, clone1) =
repo_clone(r1_dir.path().to_str().unwrap()).unwrap();
let (clone2_dir, clone2) =
repo_clone(r1_dir.path().to_str().unwrap()).unwrap();
let (clone2_dir, clone2) =
repo_clone(r1_dir.path().to_str().unwrap()).unwrap();
// clone1
// clone1
let commit1 =
write_commit_file(&clone1, "test.txt", "test", "commit1");
let commit1 =
write_commit_file(&clone1, "test.txt", "test", "commit1");
push(
clone1_dir.path().to_str().unwrap(),
"origin",
"master",
false,
None,
None,
)
.unwrap();
push_branch(
&clone1_dir.path().to_str().unwrap().into(),
"origin",
"master",
false,
false,
None,
None,
)
.unwrap();
// clone2
debug_cmd_print(
clone2_dir.path().to_str().unwrap(),
"git pull --ff",
);
// clone2
debug_cmd_print(
&clone2_dir.path().to_str().unwrap().into(),
"git pull --ff",
);
let commit2 = write_commit_file(
&clone2,
"test2.txt",
"test",
"commit2",
);
let commit2 = write_commit_file(
&clone2,
"test2.txt",
"test",
"commit2",
);
push(
clone2_dir.path().to_str().unwrap(),
"origin",
"master",
false,
None,
None,
)
.unwrap();
push_branch(
&clone2_dir.path().to_str().unwrap().into(),
"origin",
"master",
false,
false,
None,
None,
)
.unwrap();
// clone1 again
// clone1 again
let bytes = fetch(
clone1_dir.path().to_str().unwrap(),
"master",
None,
None,
)
.unwrap();
assert!(bytes > 0);
let bytes = fetch(
&clone1_dir.path().to_str().unwrap().into(),
"master",
None,
None,
)
.unwrap();
assert!(bytes > 0);
let bytes = fetch(
clone1_dir.path().to_str().unwrap(),
"master",
None,
None,
)
.unwrap();
assert_eq!(bytes, 0);
let bytes = fetch(
&clone1_dir.path().to_str().unwrap().into(),
"master",
None,
None,
)
.unwrap();
assert_eq!(bytes, 0);
branch_merge_upstream_fastforward(
clone1_dir.path().to_str().unwrap(),
"master",
)
.unwrap();
branch_merge_upstream_fastforward(
&clone1_dir.path().to_str().unwrap().into(),
"master",
)
.unwrap();
let commits = get_commit_ids(&clone1, 10);
assert_eq!(commits.len(), 2);
assert_eq!(commits[1], commit1);
assert_eq!(commits[0], commit2);
}
let commits = get_commit_ids(&clone1, 10);
assert_eq!(commits.len(), 2);
assert_eq!(commits[1], commit1);
assert_eq!(commits[0], commit2);
}
}

View file

@ -1,329 +1,356 @@
//! merging from upstream (rebase)
use crate::{
error::{Error, Result},
sync::utils,
error::{Error, Result},
sync::{
rebase::conflict_free_rebase, repository::repo, CommitId,
RepoPath,
},
};
use git2::BranchType;
use scopetime::scope_time;
/// trys merging current branch with its upstrema using rebase
/// tries merging current branch with its upstream using rebase
pub fn merge_upstream_rebase(
repo_path: &str,
branch_name: &str,
) -> Result<()> {
scope_time!("merge_upstream_rebase");
repo_path: &RepoPath,
branch_name: &str,
) -> Result<CommitId> {
scope_time!("merge_upstream_rebase");
let repo = utils::repo(repo_path)?;
if super::get_branch_name_repo(&repo)? != branch_name {
return Err(Error::Generic(String::from(
"can only rebase in head branch",
)));
}
let repo = repo(repo_path)?;
if super::get_branch_name_repo(&repo)? != branch_name {
return Err(Error::Generic(String::from(
"can only rebase in head branch",
)));
}
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.find_annotated_commit(upstream_commit.id())?;
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.find_annotated_commit(upstream_commit.id())?;
let mut rebase =
repo.rebase(None, Some(&annotated_upstream), None, None)?;
let signature =
crate::sync::commit::signature_allow_undefined_name(&repo)?;
while let Some(op) = rebase.next() {
let _op = op?;
// dbg!(op.id());
if repo.index()?.has_conflicts() {
rebase.abort()?;
return Err(Error::Generic(String::from(
"conflicts while merging",
)));
}
rebase.commit(None, &signature, None)?;
}
if repo.index()?.has_conflicts() {
rebase.abort()?;
return Err(Error::Generic(String::from(
"conflicts while merging",
)));
}
rebase.finish(Some(&signature))?;
Ok(())
conflict_free_rebase(&repo, &annotated_upstream)
}
#[cfg(test)]
mod test {
use super::*;
use crate::sync::{
branch_compare_upstream, get_commits_info,
remotes::{fetch, push::push},
tests::{
debug_cmd_print, get_commit_ids, repo_clone,
repo_init_bare, write_commit_file, write_commit_file_at,
},
RepoState,
};
use git2::{Repository, Time};
fn get_commit_msgs(r: &Repository) -> Vec<String> {
let commits = get_commit_ids(r, 10);
get_commits_info(
r.workdir().unwrap().to_str().unwrap(),
&commits,
10,
)
.unwrap()
.into_iter()
.map(|c| c.message)
.collect()
}
#[test]
fn test_merge_normal() {
let (r1_dir, _repo) = repo_init_bare().unwrap();
let (clone1_dir, clone1) =
repo_clone(r1_dir.path().to_str().unwrap()).unwrap();
use super::*;
use crate::sync::{
branch_compare_upstream, get_commits_info,
remotes::{fetch, push::push_branch},
tests::{
debug_cmd_print, get_commit_ids, repo_clone,
repo_init_bare, write_commit_file, write_commit_file_at,
},
RepoState,
};
use git2::{Repository, Time};
fn get_commit_msgs(r: &Repository) -> Vec<String> {
let commits = get_commit_ids(r, 10);
get_commits_info(
&r.workdir().unwrap().to_str().unwrap().into(),
&commits,
10,
)
.unwrap()
.into_iter()
.map(|c| c.message)
.collect()
}
#[test]
fn test_merge_normal() {
let (r1_dir, _repo) = repo_init_bare().unwrap();
let (clone1_dir, clone1) =
repo_clone(r1_dir.path().to_str().unwrap()).unwrap();
let clone1_dir = clone1_dir.path().to_str().unwrap();
// clone1
let _commit1 = write_commit_file_at(
&clone1,
"test.txt",
"test",
"commit1",
git2::Time::new(0, 0),
);
assert!(!clone1.head_detached().unwrap());
push_branch(
&clone1_dir.into(),
"origin",
"master",
false,
false,
None,
None,
)
.unwrap();
assert!(!clone1.head_detached().unwrap());
// clone2
let (clone2_dir, clone2) =
repo_clone(r1_dir.path().to_str().unwrap()).unwrap();
let clone2_dir = clone2_dir.path().to_str().unwrap();
let _commit2 = write_commit_file_at(
&clone2,
"test2.txt",
"test",
"commit2",
git2::Time::new(1, 0),
);
assert!(!clone2.head_detached().unwrap());
push_branch(
&clone2_dir.into(),
"origin",
"master",
false,
false,
None,
None,
)
.unwrap();
assert!(!clone2.head_detached().unwrap());
// clone1
let _commit3 = write_commit_file_at(
&clone1,
"test3.txt",
"test",
"commit3",
git2::Time::new(2, 0),
);
assert!(!clone1.head_detached().unwrap());
//lets fetch from origin
let bytes =
fetch(&clone1_dir.into(), "master", None, None).unwrap();
assert!(bytes > 0);
//we should be one commit behind
assert_eq!(
branch_compare_upstream(&clone1_dir.into(), "master")
.unwrap()
.behind,
1
);
// debug_cmd_print(clone1_dir, "git status");
assert!(!clone1.head_detached().unwrap());
merge_upstream_rebase(&clone1_dir.into(), "master").unwrap();
debug_cmd_print(&clone1_dir.into(), "git log");
let state =
crate::sync::repo_state(&clone1_dir.into()).unwrap();
assert_eq!(state, RepoState::Clean);
let commits = get_commit_msgs(&clone1);
assert_eq!(
commits,
vec![
String::from("commit3"),
String::from("commit2"),
String::from("commit1")
]
);
assert!(!clone1.head_detached().unwrap());
}
#[test]
fn test_merge_multiple() {
let (r1_dir, _repo) = repo_init_bare().unwrap();
let (clone1_dir, clone1) =
repo_clone(r1_dir.path().to_str().unwrap()).unwrap();
let clone1_dir = clone1_dir.path().to_str().unwrap();
// clone1
write_commit_file_at(
&clone1,
"test.txt",
"test",
"commit1",
Time::new(0, 0),
);
push_branch(
&clone1_dir.into(),
"origin",
"master",
false,
false,
None,
None,
)
.unwrap();
// clone2
let (clone2_dir, clone2) =
repo_clone(r1_dir.path().to_str().unwrap()).unwrap();
let clone2_dir = clone2_dir.path().to_str().unwrap();
write_commit_file_at(
&clone2,
"test2.txt",
"test",
"commit2",
Time::new(1, 0),
);
push_branch(
&clone2_dir.into(),
"origin",
"master",
false,
false,
None,
None,
)
.unwrap();
// clone1
write_commit_file_at(
&clone1,
"test3.txt",
"test",
"commit3",
Time::new(2, 0),
);
write_commit_file_at(
&clone1,
"test4.txt",
"test",
"commit4",
Time::new(3, 0),
);
//lets fetch from origin
fetch(&clone1_dir.into(), "master", None, None).unwrap();
merge_upstream_rebase(&clone1_dir.into(), "master").unwrap();
debug_cmd_print(&clone1_dir.into(), "git log");
let state =
crate::sync::repo_state(&clone1_dir.into()).unwrap();
assert_eq!(state, RepoState::Clean);
let commits = get_commit_msgs(&clone1);
assert_eq!(
commits,
vec![
String::from("commit4"),
String::from("commit3"),
String::from("commit2"),
String::from("commit1")
]
);
assert!(!clone1.head_detached().unwrap());
}
#[test]
fn test_merge_conflict() {
let (r1_dir, _repo) = repo_init_bare().unwrap();
let (clone1_dir, clone1) =
repo_clone(r1_dir.path().to_str().unwrap()).unwrap();
let clone1_dir = clone1_dir.path().to_str().unwrap();
// clone1
let _commit1 =
write_commit_file(&clone1, "test.txt", "test", "commit1");
push_branch(
&clone1_dir.into(),
"origin",
"master",
false,
false,
None,
None,
)
.unwrap();
// clone2
let (clone2_dir, clone2) =
repo_clone(r1_dir.path().to_str().unwrap()).unwrap();
let clone2_dir = clone2_dir.path().to_str().unwrap();
let _commit2 = write_commit_file(
&clone2,
"test2.txt",
"test",
"commit2",
);
push_branch(
&clone2_dir.into(),
"origin",
"master",
false,
false,
None,
None,
)
.unwrap();
// clone1
let _commit3 =
write_commit_file(&clone1, "test2.txt", "foo", "commit3");
let bytes =
fetch(&clone1_dir.into(), "master", None, None).unwrap();
assert!(bytes > 0);
assert_eq!(
branch_compare_upstream(&clone1_dir.into(), "master")
.unwrap()
.behind,
1
);
let res = merge_upstream_rebase(&clone1_dir.into(), "master");
assert!(res.is_err());
let state =
crate::sync::repo_state(&clone1_dir.into()).unwrap();
assert_eq!(state, RepoState::Clean);
let clone1_dir = clone1_dir.path().to_str().unwrap();
// clone1
let _commit1 = write_commit_file_at(
&clone1,
"test.txt",
"test",
"commit1",
git2::Time::new(0, 0),
);
assert_eq!(clone1.head_detached().unwrap(), false);
push(clone1_dir, "origin", "master", false, None, None)
.unwrap();
assert_eq!(clone1.head_detached().unwrap(), false);
// clone2
let (clone2_dir, clone2) =
repo_clone(r1_dir.path().to_str().unwrap()).unwrap();
let clone2_dir = clone2_dir.path().to_str().unwrap();
let _commit2 = write_commit_file_at(
&clone2,
"test2.txt",
"test",
"commit2",
git2::Time::new(1, 0),
);
assert_eq!(clone2.head_detached().unwrap(), false);
push(clone2_dir, "origin", "master", false, None, None)
.unwrap();
assert_eq!(clone2.head_detached().unwrap(), false);
// clone1
let _commit3 = write_commit_file_at(
&clone1,
"test3.txt",
"test",
"commit3",
git2::Time::new(2, 0),
);
assert_eq!(clone1.head_detached().unwrap(), false);
//lets fetch from origin
let bytes = fetch(clone1_dir, "master", None, None).unwrap();
assert!(bytes > 0);
//we should be one commit behind
assert_eq!(
branch_compare_upstream(clone1_dir, "master")
.unwrap()
.behind,
1
);
// debug_cmd_print(clone1_dir, "git status");
assert_eq!(clone1.head_detached().unwrap(), false);
merge_upstream_rebase(clone1_dir, "master").unwrap();
debug_cmd_print(clone1_dir, "git log");
let state = crate::sync::repo_state(clone1_dir).unwrap();
assert_eq!(state, RepoState::Clean);
let commits = get_commit_msgs(&clone1);
assert_eq!(
commits,
vec![
String::from("commit3"),
String::from("commit2"),
String::from("commit1")
]
);
assert_eq!(clone1.head_detached().unwrap(), false);
}
#[test]
fn test_merge_multiple() {
let (r1_dir, _repo) = repo_init_bare().unwrap();
let (clone1_dir, clone1) =
repo_clone(r1_dir.path().to_str().unwrap()).unwrap();
let clone1_dir = clone1_dir.path().to_str().unwrap();
// clone1
write_commit_file_at(
&clone1,
"test.txt",
"test",
"commit1",
Time::new(0, 0),
);
push(clone1_dir, "origin", "master", false, None, None)
.unwrap();
// clone2
let (clone2_dir, clone2) =
repo_clone(r1_dir.path().to_str().unwrap()).unwrap();
let clone2_dir = clone2_dir.path().to_str().unwrap();
write_commit_file_at(
&clone2,
"test2.txt",
"test",
"commit2",
Time::new(1, 0),
);
push(clone2_dir, "origin", "master", false, None, None)
.unwrap();
// clone1
write_commit_file_at(
&clone1,
"test3.txt",
"test",
"commit3",
Time::new(2, 0),
);
write_commit_file_at(
&clone1,
"test4.txt",
"test",
"commit4",
Time::new(3, 0),
);
//lets fetch from origin
fetch(clone1_dir, "master", None, None).unwrap();
merge_upstream_rebase(clone1_dir, "master").unwrap();
debug_cmd_print(clone1_dir, "git log");
let state = crate::sync::repo_state(clone1_dir).unwrap();
assert_eq!(state, RepoState::Clean);
let commits = get_commit_msgs(&clone1);
assert_eq!(
commits,
vec![
String::from("commit4"),
String::from("commit3"),
String::from("commit2"),
String::from("commit1")
]
);
assert_eq!(clone1.head_detached().unwrap(), false);
}
#[test]
fn test_merge_conflict() {
let (r1_dir, _repo) = repo_init_bare().unwrap();
let (clone1_dir, clone1) =
repo_clone(r1_dir.path().to_str().unwrap()).unwrap();
let clone1_dir = clone1_dir.path().to_str().unwrap();
// clone1
let _commit1 =
write_commit_file(&clone1, "test.txt", "test", "commit1");
push(clone1_dir, "origin", "master", false, None, None)
.unwrap();
// clone2
let (clone2_dir, clone2) =
repo_clone(r1_dir.path().to_str().unwrap()).unwrap();
let clone2_dir = clone2_dir.path().to_str().unwrap();
let _commit2 = write_commit_file(
&clone2,
"test2.txt",
"test",
"commit2",
);
push(clone2_dir, "origin", "master", false, None, None)
.unwrap();
// clone1
let _commit3 =
write_commit_file(&clone1, "test2.txt", "foo", "commit3");
let bytes = fetch(clone1_dir, "master", None, None).unwrap();
assert!(bytes > 0);
assert_eq!(
branch_compare_upstream(clone1_dir, "master")
.unwrap()
.behind,
1
);
let res = merge_upstream_rebase(clone1_dir, "master");
assert!(res.is_err());
let state = crate::sync::repo_state(clone1_dir).unwrap();
assert_eq!(state, RepoState::Clean);
let commits = get_commit_msgs(&clone1);
assert_eq!(
commits,
vec![String::from("commit3"), String::from("commit1")]
);
}
let commits = get_commit_msgs(&clone1);
assert_eq!(
commits,
vec![String::from("commit3"), String::from("commit1")]
);
}
}

File diff suppressed because it is too large Load diff

View file

@ -1,67 +1,71 @@
//! renaming of branches
use crate::{error::Result, sync::utils};
use crate::{
error::Result,
sync::{repository::repo, RepoPath},
};
use scopetime::scope_time;
/// Rename the branch reference
pub fn rename_branch(
repo_path: &str,
branch_ref: &str,
new_name: &str,
repo_path: &RepoPath,
branch_ref: &str,
new_name: &str,
) -> Result<()> {
scope_time!("delete_branch");
scope_time!("rename_branch");
let repo = utils::repo(repo_path)?;
let branch_as_ref = repo.find_reference(branch_ref)?;
let mut branch = git2::Branch::wrap(branch_as_ref);
branch.rename(new_name, true)?;
let repo = repo(repo_path)?;
let branch_as_ref = repo.find_reference(branch_ref)?;
let mut branch = git2::Branch::wrap(branch_as_ref);
branch.rename(new_name, true)?;
Ok(())
Ok(())
}
#[cfg(test)]
mod test {
use super::super::*;
use super::rename_branch;
use crate::sync::tests::repo_init;
use super::super::{checkout_branch, create_branch, RepoPath};
use super::rename_branch;
use crate::sync::tests::repo_init;
#[test]
fn test_rename_branch() {
let (_td, repo) = repo_init().unwrap();
let root = repo.path().parent().unwrap();
let repo_path = root.as_os_str().to_str().unwrap();
#[test]
fn test_rename_branch() {
let (_td, repo) = repo_init().unwrap();
let root = repo.path().parent().unwrap();
let repo_path: &RepoPath =
&root.as_os_str().to_str().unwrap().into();
create_branch(repo_path, "branch1").unwrap();
create_branch(repo_path, "branch1").unwrap();
checkout_branch(repo_path, "refs/heads/branch1").unwrap();
checkout_branch(repo_path, "branch1").unwrap();
assert_eq!(
repo.branches(None)
.unwrap()
.nth(0)
.unwrap()
.unwrap()
.0
.name()
.unwrap()
.unwrap(),
"branch1"
);
assert_eq!(
repo.branches(None)
.unwrap()
.next()
.unwrap()
.unwrap()
.0
.name()
.unwrap()
.unwrap(),
"branch1"
);
rename_branch(repo_path, "refs/heads/branch1", "AnotherName")
.unwrap();
rename_branch(repo_path, "refs/heads/branch1", "AnotherName")
.unwrap();
assert_eq!(
repo.branches(None)
.unwrap()
.nth(0)
.unwrap()
.unwrap()
.0
.name()
.unwrap()
.unwrap(),
"AnotherName"
);
}
assert_eq!(
repo.branches(None)
.unwrap()
.next()
.unwrap()
.unwrap()
.0
.name()
.unwrap()
.unwrap(),
"AnotherName"
);
}
}

View file

@ -1,338 +1,548 @@
use super::{utils::repo, CommitId};
use crate::{error::Result, sync::utils::get_head_repo};
use git2::{ErrorCode, ObjectType, Repository, Signature};
//! Git Api for Commits
use super::{CommitId, RepoPath};
use crate::sync::sign::{SignBuilder, SignError};
use crate::{
error::{Error, Result},
sync::{repository::repo, utils::get_head_repo},
};
use git2::{
message_prettify, ErrorCode, ObjectType, Repository, Signature,
};
use scopetime::scope_time;
///
pub fn amend(
repo_path: &str,
id: CommitId,
msg: &str,
repo_path: &RepoPath,
id: CommitId,
msg: &str,
) -> Result<CommitId> {
scope_time!("amend");
scope_time!("amend");
let repo = repo(repo_path)?;
let commit = repo.find_commit(id.into())?;
let repo = repo(repo_path)?;
let config = repo.config()?;
let mut index = repo.index()?;
let tree_id = index.write_tree()?;
let tree = repo.find_tree(tree_id)?;
let commit = repo.find_commit(id.into())?;
let new_id = commit.amend(
Some("HEAD"),
None,
None,
None,
Some(msg),
Some(&tree),
)?;
let mut index = repo.index()?;
let tree_id = index.write_tree()?;
let tree = repo.find_tree(tree_id)?;
Ok(CommitId::new(new_id))
if config.get_bool("commit.gpgsign").unwrap_or(false) {
// HACK: we undo the last commit and create a new one
use crate::sync::utils::undo_last_commit;
let head = get_head_repo(&repo)?;
if head == commit.id().into() {
undo_last_commit(repo_path)?;
return self::commit(repo_path, msg);
}
return Err(Error::SignAmendNonLastCommit);
}
let committer = signature_allow_undefined_name(&repo)?;
let new_id = commit.amend(
Some("HEAD"),
None,
Some(&committer), // Passing a value will overwrite the committer.
None,
Some(msg),
Some(&tree),
)?;
Ok(CommitId::new(new_id))
}
/// Wrap `Repository::signature` to allow unknown user.name.
///
/// See <https://github.com/extrawurst/gitui/issues/79>.
#[allow(clippy::redundant_pub_crate)]
/// See <https://github.com/gitui-org/gitui/issues/79>.
pub(crate) fn signature_allow_undefined_name(
repo: &Repository,
repo: &Repository,
) -> std::result::Result<Signature<'_>, git2::Error> {
let signature = repo.signature();
let signature = repo.signature();
if let Err(ref e) = signature {
if e.code() == ErrorCode::NotFound {
let config = repo.config()?;
if let Err(ref e) = signature {
if e.code() == ErrorCode::NotFound {
let config = repo.config()?;
if let (Err(_), Ok(email_entry)) = (
config.get_entry("user.name"),
config.get_entry("user.email"),
) {
if let Some(email) = email_entry.value() {
return Signature::now("unknown", email);
}
};
}
}
if let (Err(_), Ok(email_entry)) = (
config.get_entry("user.name"),
config.get_entry("user.email"),
) {
if let Some(email) = email_entry.value() {
return Signature::now("unknown", email);
}
};
}
}
signature
signature
}
/// this does not run any git hooks
pub fn commit(repo_path: &str, msg: &str) -> Result<CommitId> {
scope_time!("commit");
/// this does not run any git hooks, git-hooks have to be executed manually, checkout `hooks_commit_msg` for example
pub fn commit(repo_path: &RepoPath, msg: &str) -> Result<CommitId> {
scope_time!("commit");
let repo = repo(repo_path)?;
let repo = repo(repo_path)?;
let config = repo.config()?;
let signature = signature_allow_undefined_name(&repo)?;
let mut index = repo.index()?;
let tree_id = index.write_tree()?;
let tree = repo.find_tree(tree_id)?;
let signature = signature_allow_undefined_name(&repo)?;
let mut index = repo.index()?;
let tree_id = index.write_tree()?;
let tree = repo.find_tree(tree_id)?;
let parents = if let Ok(id) = get_head_repo(&repo) {
vec![repo.find_commit(id.into())?]
} else {
Vec::new()
};
let parents = if let Ok(id) = get_head_repo(&repo) {
vec![repo.find_commit(id.into())?]
} else {
Vec::new()
};
let parents = parents.iter().collect::<Vec<_>>();
let parents = parents.iter().collect::<Vec<_>>();
let commit_id = if config
.get_bool("commit.gpgsign")
.unwrap_or(false)
{
let buffer = repo.commit_create_buffer(
&signature,
&signature,
msg,
&tree,
parents.as_slice(),
)?;
Ok(repo
.commit(
Some("HEAD"),
&signature,
&signature,
msg,
&tree,
parents.as_slice(),
)?
.into())
let commit = std::str::from_utf8(&buffer).map_err(|_e| {
SignError::Shellout("utf8 conversion error".to_string())
})?;
let signer = SignBuilder::from_gitconfig(&repo, &config)?;
let (signature, signature_field) = signer.sign(&buffer)?;
let commit_id = repo.commit_signed(
commit,
&signature,
signature_field.as_deref(),
)?;
// manually advance to the new commit ID
// repo.commit does that on its own, repo.commit_signed does not
// if there is no head, read default branch or default to "master"
if let Ok(mut head) = repo.head() {
head.set_target(commit_id, msg)?;
} else {
let default_branch_name = config
.get_str("init.defaultBranch")
.unwrap_or("master");
repo.reference(
&format!("refs/heads/{default_branch_name}"),
commit_id,
true,
msg,
)?;
}
commit_id
} else {
repo.commit(
Some("HEAD"),
&signature,
&signature,
msg,
&tree,
parents.as_slice(),
)?
};
Ok(commit_id.into())
}
/// Tag a commit.
///
/// This function will return an `Err(…)` variant if the tags name is refused
/// by git or if the tag already exists.
pub fn tag(
repo_path: &str,
commit_id: &CommitId,
tag: &str,
pub fn tag_commit(
repo_path: &RepoPath,
commit_id: &CommitId,
tag: &str,
message: Option<&str>,
) -> Result<CommitId> {
scope_time!("tag");
scope_time!("tag_commit");
let repo = repo(repo_path)?;
let repo = repo(repo_path)?;
let signature = signature_allow_undefined_name(&repo)?;
let object_id = commit_id.get_oid();
let target =
repo.find_object(object_id, Some(ObjectType::Commit))?;
let object_id = commit_id.get_oid();
let target =
repo.find_object(object_id, Some(ObjectType::Commit))?;
Ok(repo.tag(tag, &target, &signature, "", false)?.into())
let c = if let Some(message) = message {
let signature = signature_allow_undefined_name(&repo)?;
repo.tag(tag, &target, &signature, message, false)?.into()
} else {
repo.tag_lightweight(tag, &target, false)?.into()
};
Ok(c)
}
/// Loads the comment prefix from config & uses it to prettify commit messages
pub fn commit_message_prettify(
repo_path: &RepoPath,
message: String,
) -> Result<String> {
let comment_char = repo(repo_path)?
.config()?
.get_string("core.commentChar")
.ok()
.and_then(|char_string| char_string.chars().next())
.unwrap_or('#') as u8;
Ok(message_prettify(message, Some(comment_char))?)
}
#[cfg(test)]
mod tests {
use crate::error::Result;
use crate::sync::tags::Tag;
use crate::sync::RepoPath;
use crate::sync::{
commit, get_commit_details, get_commit_files, stage_add_file,
tags::get_tags,
tests::{get_statuses, repo_init, repo_init_empty},
utils::get_head,
LogWalker,
};
use commit::{amend, commit_message_prettify, tag_commit};
use git2::Repository;
use std::{fs::File, io::Write, path::Path};
use crate::error::Result;
use crate::sync::{
commit, get_commit_details, get_commit_files, stage_add_file,
tags::get_tags,
tests::{get_statuses, repo_init, repo_init_empty},
utils::get_head,
LogWalker,
};
use commit::{amend, tag};
use git2::Repository;
use std::{fs::File, io::Write, path::Path};
fn count_commits(repo: &Repository, max: usize) -> usize {
let mut items = Vec::new();
let mut walk = LogWalker::new(repo, max).unwrap();
walk.read(&mut items).unwrap();
items.len()
}
fn count_commits(repo: &Repository, max: usize) -> usize {
let mut items = Vec::new();
let mut walk = LogWalker::new(&repo, max).unwrap();
walk.read(&mut items).unwrap();
items.len()
}
#[test]
fn test_commit() {
let file_path = Path::new("foo");
let (_td, repo) = repo_init().unwrap();
let root = repo.path().parent().unwrap();
let repo_path: &RepoPath =
&root.as_os_str().to_str().unwrap().into();
#[test]
fn test_commit() {
let file_path = Path::new("foo");
let (_td, repo) = repo_init().unwrap();
let root = repo.path().parent().unwrap();
let repo_path = root.as_os_str().to_str().unwrap();
File::create(root.join(file_path))
.unwrap()
.write_all(b"test\nfoo")
.unwrap();
File::create(&root.join(file_path))
.unwrap()
.write_all(b"test\nfoo")
.unwrap();
assert_eq!(get_statuses(repo_path), (1, 0));
assert_eq!(get_statuses(repo_path), (1, 0));
stage_add_file(repo_path, file_path).unwrap();
stage_add_file(repo_path, file_path).unwrap();
assert_eq!(get_statuses(repo_path), (0, 1));
assert_eq!(get_statuses(repo_path), (0, 1));
commit(repo_path, "commit msg").unwrap();
commit(repo_path, "commit msg").unwrap();
assert_eq!(get_statuses(repo_path), (0, 0));
}
assert_eq!(get_statuses(repo_path), (0, 0));
}
#[test]
fn test_commit_in_empty_repo() {
let file_path = Path::new("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();
#[test]
fn test_commit_in_empty_repo() {
let file_path = Path::new("foo");
let (_td, repo) = repo_init_empty().unwrap();
let root = repo.path().parent().unwrap();
let repo_path = root.as_os_str().to_str().unwrap();
assert_eq!(get_statuses(repo_path), (0, 0));
assert_eq!(get_statuses(repo_path), (0, 0));
File::create(root.join(file_path))
.unwrap()
.write_all(b"test\nfoo")
.unwrap();
File::create(&root.join(file_path))
.unwrap()
.write_all(b"test\nfoo")
.unwrap();
assert_eq!(get_statuses(repo_path), (1, 0));
assert_eq!(get_statuses(repo_path), (1, 0));
stage_add_file(repo_path, file_path).unwrap();
stage_add_file(repo_path, file_path).unwrap();
assert_eq!(get_statuses(repo_path), (0, 1));
assert_eq!(get_statuses(repo_path), (0, 1));
commit(repo_path, "commit msg").unwrap();
commit(repo_path, "commit msg").unwrap();
assert_eq!(get_statuses(repo_path), (0, 0));
}
assert_eq!(get_statuses(repo_path), (0, 0));
}
#[test]
fn test_amend() -> Result<()> {
let file_path1 = Path::new("foo");
let file_path2 = Path::new("foo2");
let (_td, repo) = repo_init_empty()?;
let root = repo.path().parent().unwrap();
let repo_path: &RepoPath =
&root.as_os_str().to_str().unwrap().into();
#[test]
fn test_amend() -> Result<()> {
let file_path1 = Path::new("foo");
let file_path2 = Path::new("foo2");
let (_td, repo) = repo_init_empty()?;
let root = repo.path().parent().unwrap();
let repo_path = root.as_os_str().to_str().unwrap();
File::create(root.join(file_path1))?.write_all(b"test1")?;
File::create(&root.join(file_path1))?.write_all(b"test1")?;
stage_add_file(repo_path, file_path1)?;
let id = commit(repo_path, "commit msg")?;
stage_add_file(repo_path, file_path1)?;
let id = commit(repo_path, "commit msg")?;
assert_eq!(count_commits(&repo, 10), 1);
assert_eq!(count_commits(&repo, 10), 1);
File::create(root.join(file_path2))?.write_all(b"test2")?;
File::create(&root.join(file_path2))?.write_all(b"test2")?;
stage_add_file(repo_path, file_path2)?;
stage_add_file(repo_path, file_path2)?;
let new_id = amend(repo_path, id, "amended")?;
let new_id = amend(repo_path, id, "amended")?;
assert_eq!(count_commits(&repo, 10), 1);
assert_eq!(count_commits(&repo, 10), 1);
let details = get_commit_details(repo_path, new_id)?;
assert_eq!(details.message.unwrap().subject, "amended");
let details = get_commit_details(repo_path, new_id)?;
assert_eq!(details.message.unwrap().subject, "amended");
let files = get_commit_files(repo_path, new_id, None)?;
let files = get_commit_files(repo_path, new_id)?;
assert_eq!(files.len(), 2);
assert_eq!(files.len(), 2);
let head = get_head(repo_path)?;
let head = get_head(repo_path)?;
assert_eq!(head, new_id);
assert_eq!(head, new_id);
Ok(())
}
Ok(())
}
#[test]
fn test_amend_with_different_user() {
let file_path1 = Path::new("foo");
let file_path2 = Path::new("foo2");
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();
#[test]
fn test_tag() -> Result<()> {
let file_path = Path::new("foo");
let (_td, repo) = repo_init_empty().unwrap();
let root = repo.path().parent().unwrap();
let repo_path = root.as_os_str().to_str().unwrap();
File::create(root.join(file_path1))
.unwrap()
.write_all(b"test1")
.unwrap();
File::create(&root.join(file_path))?
.write_all(b"test\nfoo")?;
stage_add_file(repo_path, file_path1).unwrap();
let id = commit(repo_path, "commit msg").unwrap();
stage_add_file(repo_path, file_path)?;
let amended_details =
get_commit_details(repo_path, id).unwrap();
let new_id = commit(repo_path, "commit msg")?;
assert_eq!(amended_details.committer, None);
tag(repo_path, &new_id, "tag")?;
File::create(root.join(file_path2))
.unwrap()
.write_all(b"test2")
.unwrap();
assert_eq!(
get_tags(repo_path).unwrap()[&new_id],
vec!["tag"]
);
stage_add_file(repo_path, file_path2).unwrap();
assert!(matches!(tag(repo_path, &new_id, "tag"), Err(_)));
repo.config()
.unwrap()
.set_str("user.name", "changed name")
.unwrap();
repo.config()
.unwrap()
.set_str("user.email", "changed@example.com")
.unwrap();
assert_eq!(
get_tags(repo_path).unwrap()[&new_id],
vec!["tag"]
);
let new_id = amend(repo_path, id, "amended").unwrap();
tag(repo_path, &new_id, "second-tag")?;
let amended_details =
get_commit_details(repo_path, new_id).unwrap();
assert_eq!(amended_details.author.name, "name");
assert_eq!(amended_details.author.email, "email");
let committer = amended_details.committer.unwrap();
assert_eq!(committer.name, "changed name");
assert_eq!(committer.email, "changed@example.com");
}
assert_eq!(
get_tags(repo_path).unwrap()[&new_id],
vec!["second-tag", "tag"]
);
#[test]
fn test_tag() -> Result<()> {
let file_path = Path::new("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();
Ok(())
}
File::create(root.join(file_path))?
.write_all(b"test\nfoo")?;
/// Beware: this test has to be run with a `$HOME/.gitconfig` that has
/// `user.email` not set. Otherwise, git falls back to the value of
/// `user.email` in `$HOME/.gitconfig` and this test fails.
///
/// As of February 2021, `repo_init_empty` sets all git config locations
/// to an empty temporary directory, so this constraint is met.
#[test]
fn test_empty_email() -> Result<()> {
let file_path = Path::new("foo");
let (_td, repo) = repo_init_empty().unwrap();
let root = repo.path().parent().unwrap();
let repo_path = root.as_os_str().to_str().unwrap();
stage_add_file(repo_path, file_path)?;
File::create(&root.join(file_path))?
.write_all(b"test\nfoo")?;
let new_id = commit(repo_path, "commit msg")?;
stage_add_file(repo_path, file_path)?;
tag_commit(repo_path, &new_id, "tag", None)?;
repo.config()?.remove("user.email")?;
assert_eq!(
get_tags(repo_path).unwrap()[&new_id],
vec![Tag::new("tag")]
);
let error = commit(repo_path, "commit msg");
assert!(tag_commit(repo_path, &new_id, "tag", None).is_err());
assert!(matches!(error, Err(_)));
assert_eq!(
get_tags(repo_path).unwrap()[&new_id],
vec![Tag::new("tag")]
);
repo.config()?.set_str("user.email", "email")?;
tag_commit(repo_path, &new_id, "second-tag", None)?;
let success = commit(repo_path, "commit msg");
assert_eq!(
get_tags(repo_path).unwrap()[&new_id],
vec![Tag::new("second-tag"), Tag::new("tag")]
);
assert!(matches!(success, Ok(_)));
assert_eq!(count_commits(&repo, 10), 1);
Ok(())
}
let details =
get_commit_details(repo_path, success.unwrap()).unwrap();
#[test]
fn test_tag_with_message() -> Result<()> {
let file_path = Path::new("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();
assert_eq!(details.author.name, "name");
assert_eq!(details.author.email, "email");
File::create(root.join(file_path))?
.write_all(b"test\nfoo")?;
Ok(())
}
stage_add_file(repo_path, file_path)?;
/// See comment to `test_empty_email`.
#[test]
fn test_empty_name() -> Result<()> {
let file_path = Path::new("foo");
let (_td, repo) = repo_init_empty().unwrap();
let root = repo.path().parent().unwrap();
let repo_path = root.as_os_str().to_str().unwrap();
let new_id = commit(repo_path, "commit msg")?;
File::create(&root.join(file_path))?
.write_all(b"test\nfoo")?;
tag_commit(repo_path, &new_id, "tag", Some("tag-message"))?;
stage_add_file(repo_path, file_path)?;
assert_eq!(
get_tags(repo_path).unwrap()[&new_id][0]
.annotation
.as_ref()
.unwrap(),
"tag-message"
);
repo.config()?.remove("user.name")?;
Ok(())
}
let mut success = commit(repo_path, "commit msg");
/// Beware: this test has to be run with a `$HOME/.gitconfig` that has
/// `user.email` not set. Otherwise, git falls back to the value of
/// `user.email` in `$HOME/.gitconfig` and this test fails.
///
/// As of February 2021, `repo_init_empty` sets all git config locations
/// to an empty temporary directory, so this constraint is met.
#[test]
fn test_empty_email() -> Result<()> {
let file_path = Path::new("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();
assert!(matches!(success, Ok(_)));
assert_eq!(count_commits(&repo, 10), 1);
File::create(root.join(file_path))?
.write_all(b"test\nfoo")?;
let mut details =
get_commit_details(repo_path, success.unwrap()).unwrap();
stage_add_file(repo_path, file_path)?;
assert_eq!(details.author.name, "unknown");
assert_eq!(details.author.email, "email");
repo.config()?.remove("user.email")?;
repo.config()?.set_str("user.name", "name")?;
let error = commit(repo_path, "commit msg");
success = commit(repo_path, "commit msg");
assert!(error.is_err());
assert!(matches!(success, Ok(_)));
assert_eq!(count_commits(&repo, 10), 2);
repo.config()?.set_str("user.email", "email")?;
details =
get_commit_details(repo_path, success.unwrap()).unwrap();
let success = commit(repo_path, "commit msg");
assert_eq!(details.author.name, "name");
assert_eq!(details.author.email, "email");
assert!(success.is_ok());
assert_eq!(count_commits(&repo, 10), 1);
Ok(())
}
let details =
get_commit_details(repo_path, success.unwrap()).unwrap();
assert_eq!(details.author.name, "name");
assert_eq!(details.author.email, "email");
Ok(())
}
/// See comment to `test_empty_email`.
#[test]
fn test_empty_name() -> Result<()> {
let file_path = Path::new("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();
File::create(root.join(file_path))?
.write_all(b"test\nfoo")?;
stage_add_file(repo_path, file_path)?;
repo.config()?.remove("user.name")?;
let mut success = commit(repo_path, "commit msg");
assert!(success.is_ok());
assert_eq!(count_commits(&repo, 10), 1);
let mut details =
get_commit_details(repo_path, success.unwrap()).unwrap();
assert_eq!(details.author.name, "unknown");
assert_eq!(details.author.email, "email");
repo.config()?.set_str("user.name", "name")?;
success = commit(repo_path, "commit msg");
assert!(success.is_ok());
assert_eq!(count_commits(&repo, 10), 2);
details =
get_commit_details(repo_path, success.unwrap()).unwrap();
assert_eq!(details.author.name, "name");
assert_eq!(details.author.email, "email");
Ok(())
}
#[test]
fn test_empty_comment_char() -> Result<()> {
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();
let message = commit_message_prettify(
repo_path,
"#This is a test message\nTest".to_owned(),
)?;
assert_eq!(message, "Test\n");
Ok(())
}
#[test]
fn test_with_comment_char() -> Result<()> {
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();
repo.config()?.set_str("core.commentChar", ";")?;
let message = commit_message_prettify(
repo_path,
";This is a test message\nTest".to_owned(),
)?;
assert_eq!(message, "Test\n");
Ok(())
}
}

View file

@ -1,171 +1,219 @@
use super::{commits_info::get_message, utils::repo, CommitId};
use crate::error::Result;
use super::{commits_info::get_message, CommitId, RepoPath};
use crate::{error::Result, sync::repository::repo};
use git2::Signature;
use scopetime::scope_time;
///
#[derive(Debug, PartialEq, Default, Clone)]
#[derive(Debug, PartialEq, Eq, Default, Clone)]
pub struct CommitSignature {
///
pub name: String,
///
pub email: String,
/// time in secs since Unix epoch
pub time: i64,
///
pub name: String,
///
pub email: String,
/// time in secs since Unix epoch
pub time: i64,
}
impl CommitSignature {
/// convert from git2-rs `Signature`
pub fn from(s: &Signature<'_>) -> Self {
Self {
name: s.name().unwrap_or("").to_string(),
email: s.email().unwrap_or("").to_string(),
/// convert from git2-rs `Signature`
pub fn from(s: &Signature<'_>) -> Self {
Self {
name: s.name().unwrap_or("").to_string(),
email: s.email().unwrap_or("").to_string(),
time: s.when().seconds(),
}
}
time: s.when().seconds(),
}
}
}
///
#[derive(Default, Clone)]
pub struct CommitMessage {
/// first line
pub subject: String,
/// remaining lines if more than one
pub body: Option<String>,
/// first line
pub subject: String,
/// remaining lines if more than one
pub body: Option<String>,
}
impl CommitMessage {
///
pub fn from(s: &str) -> Self {
let mut lines = s.lines();
let subject = lines.next().map_or_else(
String::new,
std::string::ToString::to_string,
);
///
pub fn from(s: &str) -> Self {
let mut lines = s.lines();
let subject = lines.next().map_or_else(
String::new,
std::string::ToString::to_string,
);
let body: Vec<String> =
lines.map(std::string::ToString::to_string).collect();
let body: Vec<String> =
lines.map(std::string::ToString::to_string).collect();
Self {
subject,
body: if body.is_empty() {
None
} else {
Some(body.join("\n"))
},
}
}
Self {
subject,
body: if body.is_empty() {
None
} else {
Some(body.join("\n"))
},
}
}
///
pub fn combine(self) -> String {
if let Some(body) = self.body {
format!("{}\n{}", self.subject, body)
} else {
self.subject
}
}
///
pub fn combine(self) -> String {
if let Some(body) = self.body {
format!("{}\n{body}", self.subject)
} else {
self.subject
}
}
}
///
#[derive(Default, Clone)]
pub struct CommitDetails {
///
pub author: CommitSignature,
/// committer when differs to `author` otherwise None
pub committer: Option<CommitSignature>,
///
pub message: Option<CommitMessage>,
///
pub hash: String,
///
pub author: CommitSignature,
/// committer when differs to `author` otherwise None
pub committer: Option<CommitSignature>,
///
pub message: Option<CommitMessage>,
///
pub hash: String,
}
impl CommitDetails {
///
pub fn short_hash(&self) -> &str {
&self.hash[0..7]
}
}
/// Get the author of a commit.
pub fn get_author_of_commit<'a>(
commit: &'a git2::Commit<'a>,
mailmap: &git2::Mailmap,
) -> git2::Signature<'a> {
match commit.author_with_mailmap(mailmap) {
Ok(author) => author,
Err(e) => {
log::error!(
"Couldn't get author with mailmap for {} (message: {:?}): {e}",
commit.id(),
commit.message(),
);
commit.author()
}
}
}
/// Get the committer of a commit.
pub fn get_committer_of_commit<'a>(
commit: &'a git2::Commit<'a>,
mailmap: &git2::Mailmap,
) -> git2::Signature<'a> {
match commit.committer_with_mailmap(mailmap) {
Ok(committer) => committer,
Err(e) => {
log::error!(
"Couldn't get committer with mailmap for {} (message: {:?}): {e}",
commit.id(),
commit.message(),
);
commit.committer()
}
}
}
///
pub fn get_commit_details(
repo_path: &str,
id: CommitId,
repo_path: &RepoPath,
id: CommitId,
) -> Result<CommitDetails> {
scope_time!("get_commit_details");
scope_time!("get_commit_details");
let repo = repo(repo_path)?;
let repo = repo(repo_path)?;
let mailmap = repo.mailmap()?;
let commit = repo.find_commit(id.into())?;
let commit = repo.find_commit(id.into())?;
let author = CommitSignature::from(&commit.author());
let committer = CommitSignature::from(&commit.committer());
let committer = if author == committer {
None
} else {
Some(committer)
};
let author = CommitSignature::from(&get_author_of_commit(
&commit, &mailmap,
));
let committer = CommitSignature::from(&get_committer_of_commit(
&commit, &mailmap,
));
let msg =
CommitMessage::from(get_message(&commit, None).as_str());
let committer = if author == committer {
None
} else {
Some(committer)
};
let details = CommitDetails {
author,
committer,
message: Some(msg),
hash: id.to_string(),
};
let msg =
CommitMessage::from(get_message(&commit, None).as_str());
Ok(details)
let details = CommitDetails {
author,
committer,
message: Some(msg),
hash: id.to_string(),
};
Ok(details)
}
#[cfg(test)]
mod tests {
use super::{get_commit_details, CommitMessage};
use crate::{
error::Result,
sync::{
commit, stage_add_file, tests::repo_init_empty, RepoPath,
},
};
use std::{fs::File, io::Write, path::Path};
use super::{get_commit_details, CommitMessage};
use crate::error::Result;
use crate::sync::{
commit, stage_add_file, tests::repo_init_empty,
};
use std::{fs::File, io::Write, path::Path};
#[test]
fn test_msg_invalid_utf8() -> Result<()> {
let file_path = Path::new("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();
#[test]
fn test_msg_invalid_utf8() -> Result<()> {
let file_path = Path::new("foo");
let (_td, repo) = repo_init_empty().unwrap();
let root = repo.path().parent().unwrap();
let repo_path = root.as_os_str().to_str().unwrap();
File::create(root.join(file_path))?.write_all(b"a")?;
stage_add_file(repo_path, file_path).unwrap();
File::create(&root.join(file_path))?.write_all(b"a")?;
stage_add_file(repo_path, file_path).unwrap();
let msg = invalidstring::invalid_utf8("test msg");
let id = commit(repo_path, msg.as_str()).unwrap();
let msg = invalidstring::invalid_utf8("test msg");
let id = commit(repo_path, msg.as_str()).unwrap();
let res = get_commit_details(repo_path, id).unwrap();
let res = get_commit_details(repo_path, id).unwrap();
assert!(res
.message
.as_ref()
.unwrap()
.subject
.starts_with("test msg"));
dbg!(&res.message.as_ref().unwrap().subject);
assert_eq!(
res.message
.as_ref()
.unwrap()
.subject
.starts_with("test msg"),
true
);
Ok(())
}
Ok(())
}
#[test]
fn test_msg_linefeeds() -> Result<()> {
let msg = CommitMessage::from("foo\nbar\r\ntest");
#[test]
fn test_msg_linefeeds() -> Result<()> {
let msg = CommitMessage::from("foo\nbar\r\ntest");
assert_eq!(msg.subject, String::from("foo"),);
assert_eq!(msg.body, Some(String::from("bar\ntest")),);
assert_eq!(msg.subject, String::from("foo"),);
assert_eq!(msg.body, Some(String::from("bar\ntest")),);
Ok(())
}
Ok(())
}
#[test]
fn test_commit_message_combine() -> Result<()> {
let msg = CommitMessage::from("foo\nbar\r\ntest");
#[test]
fn test_commit_message_combine() -> Result<()> {
let msg = CommitMessage::from("foo\nbar\r\ntest");
assert_eq!(msg.combine(), String::from("foo\nbar\ntest"));
assert_eq!(msg.combine(), String::from("foo\nbar\ntest"));
Ok(())
}
Ok(())
}
}

View file

@ -1,173 +1,269 @@
use super::{stash::is_stash_commit, utils::repo, CommitId};
//! Functions for getting infos about files in commits
use super::{diff::DiffOptions, CommitId, RepoPath};
use crate::{
error::Error, error::Result, StatusItem, StatusItemType,
error::Result,
sync::{get_stashes, repository::repo},
StatusItem, StatusItemType,
};
use git2::{Diff, DiffDelta, DiffOptions, Repository};
use git2::{Diff, Repository};
use scopetime::scope_time;
use std::collections::HashSet;
/// struct containing a new and an old version
#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
pub struct OldNew<T> {
/// The old version
pub old: T,
/// The new version
pub new: T,
}
/// Sort two commits.
pub fn sort_commits(
repo: &Repository,
commits: (CommitId, CommitId),
) -> Result<OldNew<CommitId>> {
if repo.graph_descendant_of(
commits.0.get_oid(),
commits.1.get_oid(),
)? {
Ok(OldNew {
old: commits.1,
new: commits.0,
})
} else {
Ok(OldNew {
old: commits.0,
new: commits.1,
})
}
}
/// get all files that are part of a commit
pub fn get_commit_files(
repo_path: &str,
id: CommitId,
repo_path: &RepoPath,
id: CommitId,
other: Option<CommitId>,
) -> Result<Vec<StatusItem>> {
scope_time!("get_commit_files");
scope_time!("get_commit_files");
let repo = repo(repo_path)?;
let repo = repo(repo_path)?;
let diff = get_commit_diff(&repo, id, None)?;
let diff = if let Some(other) = other {
get_compare_commits_diff(
&repo,
sort_commits(&repo, (id, other))?,
None,
None,
)?
} else {
get_commit_diff(
&repo,
id,
None,
None,
Some(&get_stashes(repo_path)?.into_iter().collect()),
)?
};
let mut res = Vec::new();
let res = diff
.deltas()
.map(|delta| {
let status = StatusItemType::from(delta.status());
diff.foreach(
&mut |delta: DiffDelta<'_>, _progress| {
res.push(StatusItem {
path: delta
.new_file()
.path()
.map(|p| p.to_str().unwrap_or("").to_string())
.unwrap_or_default(),
status: StatusItemType::from(delta.status()),
});
true
},
None,
None,
None,
)?;
StatusItem {
path: delta
.new_file()
.path()
.map(|p| p.to_str().unwrap_or("").to_string())
.unwrap_or_default(),
status,
}
})
.collect::<Vec<_>>();
Ok(res)
Ok(res)
}
#[allow(clippy::redundant_pub_crate)]
pub(crate) fn get_commit_diff(
repo: &Repository,
id: CommitId,
pathspec: Option<String>,
/// get diff of two arbitrary commits
#[allow(clippy::needless_pass_by_value)]
pub fn get_compare_commits_diff(
repo: &Repository,
ids: OldNew<CommitId>,
pathspec: Option<String>,
options: Option<DiffOptions>,
) -> Result<Diff<'_>> {
// scope_time!("get_commit_diff");
// scope_time!("get_compare_commits_diff");
let commits = OldNew {
old: repo.find_commit(ids.old.into())?,
new: repo.find_commit(ids.new.into())?,
};
let commit = repo.find_commit(id.into())?;
let commit_tree = commit.tree()?;
let parent = if commit.parent_count() > 0 {
Some(repo.find_commit(commit.parent_id(0)?)?.tree()?)
} else {
None
};
let trees = OldNew {
old: commits.old.tree()?,
new: commits.new.tree()?,
};
let mut opts = DiffOptions::new();
if let Some(p) = &pathspec {
opts.pathspec(p.clone());
}
opts.show_binary(true);
let mut opts = git2::DiffOptions::new();
if let Some(options) = options {
opts.context_lines(options.context);
opts.ignore_whitespace(options.ignore_whitespace);
opts.interhunk_lines(options.interhunk_lines);
}
if let Some(p) = &pathspec {
opts.pathspec(p.clone());
}
let mut diff = repo.diff_tree_to_tree(
parent.as_ref(),
Some(&commit_tree),
Some(&mut opts),
)?;
let diff: Diff<'_> = repo.diff_tree_to_tree(
Some(&trees.old),
Some(&trees.new),
Some(&mut opts),
)?;
if is_stash_commit(
repo.path().to_str().map_or_else(
|| Err(Error::Generic("repo path utf8 err".to_owned())),
Ok,
)?,
&id,
)? {
if let Ok(untracked_commit) = commit.parent_id(2) {
let untracked_diff = get_commit_diff(
repo,
CommitId::new(untracked_commit),
pathspec,
)?;
Ok(diff)
}
diff.merge(&untracked_diff)?;
}
}
/// get diff of a commit to its first parent
pub(crate) fn get_commit_diff<'a>(
repo: &'a Repository,
id: CommitId,
pathspec: Option<String>,
options: Option<DiffOptions>,
stashes: Option<&HashSet<CommitId>>,
) -> Result<Diff<'a>> {
// scope_time!("get_commit_diff");
Ok(diff)
let commit = repo.find_commit(id.into())?;
let commit_tree = commit.tree()?;
let parent = if commit.parent_count() > 0 {
repo.find_commit(commit.parent_id(0)?)
.ok()
.and_then(|c| c.tree().ok())
} else {
None
};
let mut opts = git2::DiffOptions::new();
if let Some(options) = options {
opts.context_lines(options.context);
opts.ignore_whitespace(options.ignore_whitespace);
opts.interhunk_lines(options.interhunk_lines);
}
if let Some(p) = &pathspec {
opts.pathspec(p.clone());
}
opts.show_binary(true);
let mut diff = repo.diff_tree_to_tree(
parent.as_ref(),
Some(&commit_tree),
Some(&mut opts),
)?;
if stashes.is_some_and(|stashes| stashes.contains(&id)) {
if let Ok(untracked_commit) = commit.parent_id(2) {
let untracked_diff = get_commit_diff(
repo,
CommitId::new(untracked_commit),
pathspec,
options,
stashes,
)?;
diff.merge(&untracked_diff)?;
}
}
Ok(diff)
}
#[cfg(test)]
mod tests {
use super::get_commit_files;
use crate::{
error::Result,
sync::{
commit, stage_add_file, stash_save,
tests::{get_statuses, repo_init},
},
StatusItemType,
};
use std::{fs::File, io::Write, path::Path};
use super::get_commit_files;
use crate::{
error::Result,
sync::{
commit, stage_add_file, stash_save,
tests::{get_statuses, repo_init},
RepoPath,
},
StatusItemType,
};
use std::{fs::File, io::Write, path::Path};
#[test]
fn test_smoke() -> Result<()> {
let file_path = Path::new("file1.txt");
let (_td, repo) = repo_init()?;
let root = repo.path().parent().unwrap();
let repo_path = root.as_os_str().to_str().unwrap();
#[test]
fn test_smoke() -> Result<()> {
let file_path = Path::new("file1.txt");
let (_td, repo) = repo_init()?;
let root = repo.path().parent().unwrap();
let repo_path: &RepoPath =
&root.as_os_str().to_str().unwrap().into();
File::create(&root.join(file_path))?
.write_all(b"test file1 content")?;
File::create(root.join(file_path))?
.write_all(b"test file1 content")?;
stage_add_file(repo_path, file_path)?;
stage_add_file(repo_path, file_path)?;
let id = commit(repo_path, "commit msg")?;
let id = commit(repo_path, "commit msg")?;
let diff = get_commit_files(repo_path, id)?;
let diff = get_commit_files(repo_path, id, None)?;
assert_eq!(diff.len(), 1);
assert_eq!(diff[0].status, StatusItemType::New);
assert_eq!(diff.len(), 1);
assert_eq!(diff[0].status, StatusItemType::New);
Ok(())
}
Ok(())
}
#[test]
fn test_stashed_untracked() -> Result<()> {
let file_path = Path::new("file1.txt");
let (_td, repo) = repo_init()?;
let root = repo.path().parent().unwrap();
let repo_path = root.as_os_str().to_str().unwrap();
#[test]
fn test_stashed_untracked() -> Result<()> {
let file_path = Path::new("file1.txt");
let (_td, repo) = repo_init()?;
let root = repo.path().parent().unwrap();
let repo_path: &RepoPath =
&root.as_os_str().to_str().unwrap().into();
File::create(&root.join(file_path))?
.write_all(b"test file1 content")?;
File::create(root.join(file_path))?
.write_all(b"test file1 content")?;
let id = stash_save(repo_path, None, true, false)?;
let id = stash_save(repo_path, None, true, false)?;
let diff = get_commit_files(repo_path, id)?;
let diff = get_commit_files(repo_path, id, None)?;
assert_eq!(diff.len(), 1);
assert_eq!(diff[0].status, StatusItemType::New);
assert_eq!(diff.len(), 1);
assert_eq!(diff[0].status, StatusItemType::New);
Ok(())
}
Ok(())
}
#[test]
fn test_stashed_untracked_and_modified() -> Result<()> {
let file_path1 = Path::new("file1.txt");
let file_path2 = Path::new("file2.txt");
let (_td, repo) = repo_init()?;
let root = repo.path().parent().unwrap();
let repo_path = root.as_os_str().to_str().unwrap();
#[test]
fn test_stashed_untracked_and_modified() -> Result<()> {
let file_path1 = Path::new("file1.txt");
let file_path2 = Path::new("file2.txt");
let (_td, repo) = repo_init()?;
let root = repo.path().parent().unwrap();
let repo_path: &RepoPath =
&root.as_os_str().to_str().unwrap().into();
File::create(&root.join(file_path1))?.write_all(b"test")?;
stage_add_file(repo_path, file_path1)?;
commit(repo_path, "c1")?;
File::create(root.join(file_path1))?.write_all(b"test")?;
stage_add_file(repo_path, file_path1)?;
commit(repo_path, "c1")?;
File::create(&root.join(file_path1))?
.write_all(b"modified")?;
File::create(&root.join(file_path2))?.write_all(b"new")?;
File::create(root.join(file_path1))?
.write_all(b"modified")?;
File::create(root.join(file_path2))?.write_all(b"new")?;
assert_eq!(get_statuses(repo_path), (2, 0));
assert_eq!(get_statuses(repo_path), (2, 0));
let id = stash_save(repo_path, None, true, false)?;
let id = stash_save(repo_path, None, true, false)?;
let diff = get_commit_files(repo_path, id)?;
let diff = get_commit_files(repo_path, id, None)?;
assert_eq!(diff.len(), 2);
assert_eq!(diff[0].status, StatusItemType::Modified);
assert_eq!(diff[1].status, StatusItemType::New);
assert_eq!(diff.len(), 2);
assert_eq!(diff[0].status, StatusItemType::Modified);
assert_eq!(diff[1].status, StatusItemType::New);
Ok(())
}
Ok(())
}
}

View file

@ -0,0 +1,224 @@
use super::{
commit_details::get_author_of_commit,
commit_files::get_commit_diff, CommitId,
};
use crate::error::Result;
use bitflags::bitflags;
use fuzzy_matcher::FuzzyMatcher;
use git2::{Diff, Repository};
use std::sync::Arc;
///
pub type SharedCommitFilterFn = Arc<
Box<dyn Fn(&Repository, &CommitId) -> Result<bool> + Send + Sync>,
>;
///
pub fn diff_contains_file(file_path: String) -> SharedCommitFilterFn {
Arc::new(Box::new(
move |repo: &Repository,
commit_id: &CommitId|
-> Result<bool> {
let diff = get_commit_diff(
repo,
*commit_id,
Some(file_path.clone()),
None,
None,
)?;
let contains_file = diff.deltas().len() > 0;
Ok(contains_file)
},
))
}
bitflags! {
///
#[derive(Debug, Clone, Copy)]
pub struct SearchFields: u32 {
///
const MESSAGE_SUMMARY = 1 << 0;
///
const MESSAGE_BODY = 1 << 1;
///
const FILENAMES = 1 << 2;
///
const AUTHORS = 1 << 3;
//TODO:
// const COMMIT_HASHES = 1 << 3;
// ///
// const DATES = 1 << 4;
// ///
// const DIFFS = 1 << 5;
}
}
impl Default for SearchFields {
fn default() -> Self {
Self::MESSAGE_SUMMARY
}
}
bitflags! {
///
#[derive(Debug, Clone, Copy)]
pub struct SearchOptions: u32 {
///
const CASE_SENSITIVE = 1 << 0;
///
const FUZZY_SEARCH = 1 << 1;
}
}
impl Default for SearchOptions {
fn default() -> Self {
Self::empty()
}
}
///
#[derive(Default, Debug, Clone)]
pub struct LogFilterSearchOptions {
///
pub search_pattern: String,
///
pub fields: SearchFields,
///
pub options: SearchOptions,
}
///
#[derive(Default)]
pub struct LogFilterSearch {
///
pub matcher: fuzzy_matcher::skim::SkimMatcherV2,
///
pub options: LogFilterSearchOptions,
}
impl LogFilterSearch {
///
pub fn new(options: LogFilterSearchOptions) -> Self {
let mut options = options;
if !options.options.contains(SearchOptions::CASE_SENSITIVE) {
options.search_pattern =
options.search_pattern.to_lowercase();
}
Self {
matcher: fuzzy_matcher::skim::SkimMatcherV2::default(),
options,
}
}
fn match_diff(&self, diff: &Diff<'_>) -> bool {
diff.deltas().any(|delta| {
if delta
.new_file()
.path()
.and_then(|file| file.as_os_str().to_str())
.is_some_and(|file| self.match_text(file))
{
return true;
}
delta
.old_file()
.path()
.and_then(|file| file.as_os_str().to_str())
.is_some_and(|file| self.match_text(file))
})
}
///
pub fn match_text(&self, text: &str) -> bool {
if self.options.options.contains(SearchOptions::FUZZY_SEARCH)
{
self.matcher
.fuzzy_match(
text,
self.options.search_pattern.as_str(),
)
.is_some()
} else if self
.options
.options
.contains(SearchOptions::CASE_SENSITIVE)
{
text.contains(self.options.search_pattern.as_str())
} else {
text.to_lowercase()
.contains(self.options.search_pattern.as_str())
}
}
}
///
pub fn filter_commit_by_search(
filter: LogFilterSearch,
) -> SharedCommitFilterFn {
Arc::new(Box::new(
move |repo: &Repository,
commit_id: &CommitId|
-> Result<bool> {
let mailmap = repo.mailmap()?;
let commit = repo.find_commit((*commit_id).into())?;
let msg_summary_match = filter
.options
.fields
.contains(SearchFields::MESSAGE_SUMMARY)
.then(|| {
commit.summary().map(|msg| filter.match_text(msg))
})
.flatten()
.unwrap_or_default();
let msg_body_match = filter
.options
.fields
.contains(SearchFields::MESSAGE_BODY)
.then(|| {
commit.body().map(|msg| filter.match_text(msg))
})
.flatten()
.unwrap_or_default();
let file_match = filter
.options
.fields
.contains(SearchFields::FILENAMES)
.then(|| {
get_commit_diff(
repo, *commit_id, None, None, None,
)
.ok()
})
.flatten()
.is_some_and(|diff| filter.match_diff(&diff));
let authors_match = if filter
.options
.fields
.contains(SearchFields::AUTHORS)
{
let author = get_author_of_commit(&commit, &mailmap);
[author.email(), author.name()].iter().any(
|opt_haystack| {
opt_haystack.is_some_and(|haystack| {
filter.match_text(haystack)
})
},
)
} else {
false
};
Ok(msg_summary_match
|| msg_body_match
|| file_match
|| authors_match)
},
))
}

View file

@ -0,0 +1,51 @@
use super::{CommitId, RepoPath};
use crate::{
error::Result,
sync::{repository::repo, utils::read_file},
};
use scopetime::scope_time;
const GIT_REVERT_HEAD_FILE: &str = "REVERT_HEAD";
///
pub fn revert_commit(
repo_path: &RepoPath,
commit: CommitId,
) -> Result<()> {
scope_time!("revert");
let repo = repo(repo_path)?;
let commit = repo.find_commit(commit.into())?;
repo.revert(&commit, None)?;
Ok(())
}
///
pub fn revert_head(repo_path: &RepoPath) -> Result<CommitId> {
scope_time!("revert_head");
let path = repo(repo_path)?.path().join(GIT_REVERT_HEAD_FILE);
let file_content = read_file(&path)?;
let id = git2::Oid::from_str(file_content.trim())?;
Ok(id.into())
}
///
pub fn commit_revert(
repo_path: &RepoPath,
msg: &str,
) -> Result<CommitId> {
scope_time!("commit_revert");
let id = crate::sync::commit(repo_path, msg)?;
repo(repo_path)?.cleanup_state()?;
Ok(id)
}

View file

@ -1,216 +1,349 @@
use super::utils::repo;
use crate::error::Result;
use std::fmt::Display;
use super::RepoPath;
use crate::{
error::Result,
sync::{
commit_details::get_author_of_commit,
repository::{gix_repo, repo},
},
};
use git2::{Commit, Error, Oid};
use scopetime::scope_time;
use unicode_truncate::UnicodeTruncateStr;
/// identifies a single commit
#[derive(
Debug, Copy, Clone, PartialEq, Eq, Hash, Ord, PartialOrd,
Debug, Copy, Clone, PartialEq, Eq, Hash, Ord, PartialOrd,
)]
pub struct CommitId(Oid);
impl CommitId {
/// create new `CommitId`
pub const fn new(id: Oid) -> Self {
Self(id)
}
///
pub(crate) const fn get_oid(self) -> Oid {
self.0
}
/// 7 chars short hash
pub fn get_short_string(&self) -> String {
self.to_string().chars().take(7).collect()
}
impl Default for CommitId {
fn default() -> Self {
Self(Oid::zero())
}
}
//TODO: remove once clippy fixed: https://github.com/rust-lang/rust-clippy/issues/6983
#[allow(clippy::wrong_self_convention)]
impl ToString for CommitId {
fn to_string(&self) -> String {
self.0.to_string()
}
impl CommitId {
/// create new `CommitId`
pub const fn new(id: Oid) -> Self {
Self(id)
}
///
pub(crate) const fn get_oid(self) -> Oid {
self.0
}
/// 7 chars short hash
pub fn get_short_string(&self) -> String {
self.to_string().chars().take(7).collect()
}
/// Tries to retrieve the `CommitId` form the revision if exists in the given repository
pub fn from_revision(
repo_path: &RepoPath,
revision: &str,
) -> Result<Self> {
scope_time!("CommitId::from_revision");
let repo = repo(repo_path)?;
let commit_obj = repo.revparse_single(revision)?;
Ok(commit_obj.id().into())
}
/// Tries to convert a &str representation of a commit id into
/// a `CommitId`
pub fn from_str_unchecked(commit_id_str: &str) -> Result<Self> {
match Oid::from_str(commit_id_str) {
Err(e) => Err(crate::Error::Generic(format!(
"Could not convert {}",
e.message()
))),
Ok(v) => Ok(Self::new(v)),
}
}
}
impl Display for CommitId {
fn fmt(
&self,
f: &mut std::fmt::Formatter<'_>,
) -> std::fmt::Result {
write!(f, "{}", self.0)
}
}
impl From<CommitId> for Oid {
fn from(id: CommitId) -> Self {
id.0
}
fn from(id: CommitId) -> Self {
id.0
}
}
impl From<Oid> for CommitId {
fn from(id: Oid) -> Self {
Self::new(id)
}
fn from(id: Oid) -> Self {
Self::new(id)
}
}
impl From<gix::ObjectId> for CommitId {
fn from(object_id: gix::ObjectId) -> Self {
#[allow(clippy::expect_used)]
let oid = Oid::from_bytes(object_id.as_bytes()).expect("`Oid::from_bytes(object_id.as_bytes())` is expected to never fail");
Self::new(oid)
}
}
impl From<gix::Commit<'_>> for CommitId {
fn from(commit: gix::Commit<'_>) -> Self {
#[allow(clippy::expect_used)]
let oid = Oid::from_bytes(commit.id().as_bytes()).expect("`Oid::from_bytes(commit.id().as_bytes())` is expected to never fail");
Self::new(oid)
}
}
impl From<CommitId> for gix::ObjectId {
fn from(id: CommitId) -> Self {
Self::from_bytes_or_panic(id.0.as_bytes())
}
}
///
#[derive(Debug)]
#[derive(Debug, Clone)]
pub struct CommitInfo {
///
pub message: String,
///
pub time: i64,
///
pub author: String,
///
pub id: CommitId,
///
pub message: String,
///
pub time: i64,
///
pub author: String,
///
pub id: CommitId,
}
///
pub fn get_commits_info(
repo_path: &str,
ids: &[CommitId],
message_length_limit: usize,
repo_path: &RepoPath,
ids: &[CommitId],
message_length_limit: usize,
) -> Result<Vec<CommitInfo>> {
scope_time!("get_commits_info");
scope_time!("get_commits_info");
let repo = repo(repo_path)?;
let repo = repo(repo_path)?;
let mailmap = repo.mailmap()?;
let commits = ids
.iter()
.map(|id| repo.find_commit((*id).into()))
.collect::<std::result::Result<Vec<Commit>, Error>>()?
.into_iter();
let commits = ids
.iter()
.map(|id| repo.find_commit((*id).into()))
.collect::<std::result::Result<Vec<Commit>, Error>>()?
.into_iter();
let res = commits
.map(|c: Commit| {
let message = get_message(&c, Some(message_length_limit));
let author = c.author().name().map_or_else(
|| String::from("<unknown>"),
String::from,
);
CommitInfo {
message,
author,
time: c.time().seconds(),
id: CommitId(c.id()),
}
})
.collect::<Vec<_>>();
let res = commits
.map(|c: Commit| {
let message = get_message(&c, Some(message_length_limit));
let author = get_author_of_commit(&c, &mailmap)
.name()
.map_or_else(
|| String::from("<unknown>"),
String::from,
);
CommitInfo {
message,
author,
time: c.time().seconds(),
id: CommitId(c.id()),
}
})
.collect::<Vec<_>>();
Ok(res)
Ok(res)
}
///
pub fn get_commit_info(
repo_path: &str,
commit_id: &CommitId,
repo_path: &RepoPath,
commit_id: &CommitId,
) -> Result<CommitInfo> {
scope_time!("get_commit_info");
scope_time!("get_commit_info");
let repo = repo(repo_path)?;
let repo: gix::Repository = gix_repo(repo_path)?;
let mailmap = repo.open_mailmap();
let commit = repo.find_commit((*commit_id).into())?;
let author = commit.author();
let commit = repo.find_commit(*commit_id)?;
let commit_ref = commit.decode()?;
Ok(CommitInfo {
message: commit.message().unwrap_or("").into(),
author: author.name().unwrap_or("<unknown>").into(),
time: commit.time().seconds(),
id: CommitId(commit.id()),
})
let message = gix_get_message(&commit_ref, None);
let author = commit_ref.author()?;
let author = mailmap.try_resolve(author).map_or_else(
|| author.name.into(),
|signature| signature.name,
);
Ok(CommitInfo {
message,
author: author.to_string(),
time: commit_ref.time()?.seconds,
id: commit.id().detach().into(),
})
}
/// if `message_limit` is set the message will be
/// limited to the first line and truncated to fit
pub fn get_message(
c: &Commit,
message_limit: Option<usize>,
c: &git2::Commit,
message_limit: Option<usize>,
) -> String {
let msg = String::from_utf8_lossy(c.message_bytes());
let msg = msg.trim();
let msg = String::from_utf8_lossy(c.message_bytes());
let msg = msg.trim();
message_limit.map_or_else(
|| msg.to_string(),
|limit| {
let msg = msg.lines().next().unwrap_or_default();
msg.unicode_truncate(limit).0.to_string()
},
)
message_limit.map_or_else(
|| msg.to_string(),
|limit| {
let msg = msg.lines().next().unwrap_or_default();
msg.unicode_truncate(limit).0.to_string()
},
)
}
/// if `message_limit` is set the message will be
/// limited to the first line and truncated to fit
pub fn gix_get_message(
commit_ref: &gix::objs::CommitRef,
message_limit: Option<usize>,
) -> String {
let message = commit_ref.message.to_string();
let message = message.trim();
message_limit.map_or_else(
|| message.to_string(),
|limit| {
let message = message.lines().next().unwrap_or_default();
message.unicode_truncate(limit).0.to_string()
},
)
}
#[cfg(test)]
mod tests {
use super::get_commits_info;
use crate::error::Result;
use crate::sync::{
commit, stage_add_file, tests::repo_init_empty,
utils::get_head_repo,
};
use std::{fs::File, io::Write, path::Path};
use super::get_commits_info;
use crate::{
error::Result,
sync::{
commit, stage_add_file, tests::repo_init_empty,
utils::get_head_repo, CommitId, RepoPath,
},
};
use std::{fs::File, io::Write, path::Path};
#[test]
fn test_log() -> Result<()> {
let file_path = Path::new("foo");
let (_td, repo) = repo_init_empty().unwrap();
let root = repo.path().parent().unwrap();
let repo_path = root.as_os_str().to_str().unwrap();
#[test]
fn test_log() -> Result<()> {
let file_path = Path::new("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();
File::create(&root.join(file_path))?.write_all(b"a")?;
stage_add_file(repo_path, file_path).unwrap();
let c1 = commit(repo_path, "commit1").unwrap();
File::create(&root.join(file_path))?.write_all(b"a")?;
stage_add_file(repo_path, file_path).unwrap();
let c2 = commit(repo_path, "commit2").unwrap();
File::create(root.join(file_path))?.write_all(b"a")?;
stage_add_file(repo_path, file_path).unwrap();
let c1 = commit(repo_path, "commit1").unwrap();
File::create(root.join(file_path))?.write_all(b"a")?;
stage_add_file(repo_path, file_path).unwrap();
let c2 = commit(repo_path, "commit2").unwrap();
let res =
get_commits_info(repo_path, &vec![c2, c1], 50).unwrap();
let res = get_commits_info(repo_path, &[c2, c1], 50).unwrap();
assert_eq!(res.len(), 2);
assert_eq!(res[0].message.as_str(), "commit2");
assert_eq!(res[0].author.as_str(), "name");
assert_eq!(res[1].message.as_str(), "commit1");
assert_eq!(res.len(), 2);
assert_eq!(res[0].message.as_str(), "commit2");
assert_eq!(res[0].author.as_str(), "name");
assert_eq!(res[1].message.as_str(), "commit1");
Ok(())
}
File::create(root.join(".mailmap"))?
.write_all(b"new name <newemail> <email>")?;
let res = get_commits_info(repo_path, &[c2], 50).unwrap();
#[test]
fn test_log_first_msg_line() -> Result<()> {
let file_path = Path::new("foo");
let (_td, repo) = repo_init_empty().unwrap();
let root = repo.path().parent().unwrap();
let repo_path = root.as_os_str().to_str().unwrap();
assert_eq!(res[0].author.as_str(), "new name");
File::create(&root.join(file_path))?.write_all(b"a")?;
stage_add_file(repo_path, file_path).unwrap();
let c1 = commit(repo_path, "subject\nbody").unwrap();
Ok(())
}
let res = get_commits_info(repo_path, &vec![c1], 50).unwrap();
#[test]
fn test_log_first_msg_line() -> Result<()> {
let file_path = Path::new("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();
assert_eq!(res.len(), 1);
assert_eq!(res[0].message.as_str(), "subject");
File::create(root.join(file_path))?.write_all(b"a")?;
stage_add_file(repo_path, file_path).unwrap();
let c1 = commit(repo_path, "subject\nbody").unwrap();
Ok(())
}
let res = get_commits_info(repo_path, &[c1], 50).unwrap();
#[test]
fn test_invalid_utf8() -> Result<()> {
let file_path = Path::new("foo");
let (_td, repo) = repo_init_empty().unwrap();
let root = repo.path().parent().unwrap();
let repo_path = root.as_os_str().to_str().unwrap();
assert_eq!(res.len(), 1);
assert_eq!(res[0].message.as_str(), "subject");
File::create(&root.join(file_path))?.write_all(b"a")?;
stage_add_file(repo_path, file_path).unwrap();
Ok(())
}
let msg = invalidstring::invalid_utf8("test msg");
commit(repo_path, msg.as_str()).unwrap();
#[test]
fn test_invalid_utf8() -> Result<()> {
let file_path = Path::new("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();
let res = get_commits_info(
repo_path,
&vec![get_head_repo(&repo).unwrap().into()],
50,
)
.unwrap();
File::create(root.join(file_path))?.write_all(b"a")?;
stage_add_file(repo_path, file_path).unwrap();
assert_eq!(res.len(), 1);
dbg!(&res[0].message);
assert_eq!(res[0].message.starts_with("test msg"), true);
let msg = invalidstring::invalid_utf8("test msg");
commit(repo_path, msg.as_str()).unwrap();
Ok(())
}
let res = get_commits_info(
repo_path,
&[get_head_repo(&repo).unwrap()],
50,
)
.unwrap();
assert_eq!(res.len(), 1);
dbg!(&res[0].message);
assert!(res[0].message.starts_with("test msg"));
Ok(())
}
#[test]
fn test_get_commit_from_revision() -> Result<()> {
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();
let foo_file = Path::new("foo");
File::create(root.join(foo_file))?.write_all(b"a")?;
stage_add_file(repo_path, foo_file).unwrap();
let c1 = commit(repo_path, "subject: foo\nbody").unwrap();
let c1_rev = c1.get_short_string();
assert_eq!(
CommitId::from_revision(repo_path, c1_rev.as_str())
.unwrap(),
c1
);
const FOREIGN_HASH: &str =
"d6d7d55cb6e4ba7301d6a11a657aab4211e5777e";
assert!(
CommitId::from_revision(repo_path, FOREIGN_HASH).is_err()
);
Ok(())
}
}

View file

@ -1,114 +1,170 @@
use super::utils::repo;
use crate::error::Result;
use git2::Repository;
use scopetime::scope_time;
use serde::{Deserialize, Serialize};
use super::{repository::repo, RepoPath};
// see https://git-scm.com/docs/git-config#Documentation/git-config.txt-statusshowUntrackedFiles
/// represents the `status.showUntrackedFiles` git config state
#[derive(
Hash, Copy, Clone, Default, PartialEq, Eq, Serialize, Deserialize,
)]
pub enum ShowUntrackedFilesConfig {
///
No,
///
Normal,
///
All,
///
#[default]
No,
///
Normal,
///
All,
}
impl ShowUntrackedFilesConfig {
///
pub const fn include_none(&self) -> bool {
matches!(self, Self::No)
}
///
pub const fn include_none(self) -> bool {
matches!(self, Self::No)
}
///
pub const fn include_untracked(&self) -> bool {
matches!(self, Self::Normal | Self::All)
}
///
pub const fn include_untracked(self) -> bool {
matches!(self, Self::Normal | Self::All)
}
///
pub const fn recurse_untracked_dirs(&self) -> bool {
matches!(self, Self::All)
}
///
pub const fn recurse_untracked_dirs(self) -> bool {
matches!(self, Self::All)
}
}
pub fn untracked_files_config_repo(
repo: &Repository,
repo: &Repository,
) -> Result<ShowUntrackedFilesConfig> {
let show_untracked_files =
get_config_string_repo(repo, "status.showUntrackedFiles")?;
let show_untracked_files =
get_config_string_repo(repo, "status.showUntrackedFiles")?;
if let Some(show_untracked_files) = show_untracked_files {
if &show_untracked_files == "no" {
return Ok(ShowUntrackedFilesConfig::No);
} else if &show_untracked_files == "normal" {
return Ok(ShowUntrackedFilesConfig::Normal);
}
}
if let Some(show_untracked_files) = show_untracked_files {
if &show_untracked_files == "no" {
return Ok(ShowUntrackedFilesConfig::No);
} else if &show_untracked_files == "normal" {
return Ok(ShowUntrackedFilesConfig::Normal);
}
}
Ok(ShowUntrackedFilesConfig::All)
// This does not reflect how git works according to its docs that say: "If this variable is not
// specified, it defaults to `normal`."
//
// https://git-scm.com/docs/git-config#Documentation/git-config.txt-statusshowUntrackedFiles
//
// Note that this might become less relevant over time as more code gets migrated to `gitoxide`
// because `gitoxide` respects `status.showUntrackedFiles` by default.
Ok(ShowUntrackedFilesConfig::All)
}
// see https://git-scm.com/docs/git-config#Documentation/git-config.txt-pushdefault
/// represents `push.default` git config
#[derive(PartialEq, Default, Eq)]
pub enum PushDefaultStrategyConfig {
Nothing,
Current,
Upstream,
#[default]
Simple,
Matching,
}
impl<'a> TryFrom<&'a str> for PushDefaultStrategyConfig {
type Error = crate::Error;
fn try_from(
value: &'a str,
) -> std::result::Result<Self, Self::Error> {
match value {
"nothing" => Ok(Self::Nothing),
"current" => Ok(Self::Current),
"upstream" | "tracking" => Ok(Self::Upstream),
"simple" => Ok(Self::Simple),
"matching" => Ok(Self::Matching),
_ => Err(crate::Error::GitConfig(format!(
"malformed value for push.default: {value}, must be one of nothing, matching, simple, upstream or current"
))),
}
}
}
pub fn push_default_strategy_config_repo(
repo: &Repository,
) -> Result<PushDefaultStrategyConfig> {
(get_config_string_repo(repo, "push.default")?).map_or_else(
|| Ok(PushDefaultStrategyConfig::default()),
|entry_str| {
PushDefaultStrategyConfig::try_from(entry_str.as_str())
},
)
}
///
pub fn untracked_files_config(
repo_path: &str,
repo_path: &RepoPath,
) -> Result<ShowUntrackedFilesConfig> {
let repo = repo(repo_path)?;
untracked_files_config_repo(&repo)
let repo = repo(repo_path)?;
untracked_files_config_repo(&repo)
}
/// get string from config
pub fn get_config_string(
repo_path: &str,
key: &str,
repo_path: &RepoPath,
key: &str,
) -> Result<Option<String>> {
let repo = repo(repo_path)?;
get_config_string_repo(&repo, key)
let repo = repo(repo_path)?;
get_config_string_repo(&repo, key)
}
pub fn get_config_string_repo(
repo: &Repository,
key: &str,
repo: &Repository,
key: &str,
) -> Result<Option<String>> {
scope_time!("get_config_string_repo");
scope_time!("get_config_string_repo");
let cfg = repo.config()?;
let cfg = repo.config()?;
// this code doesnt match what the doc says regarding what
// gets returned when but it actually works
let entry_res = cfg.get_entry(key);
// this code doesn't match what the doc says regarding what
// gets returned when but it actually works
let entry_res = cfg.get_entry(key);
let entry = match entry_res {
Ok(ent) => ent,
Err(_) => return Ok(None),
};
let Ok(entry) = entry_res else {
return Ok(None);
};
if entry.has_value() {
Ok(entry.value().map(std::string::ToString::to_string))
} else {
Ok(None)
}
if entry.has_value() {
Ok(entry.value().map(std::string::ToString::to_string))
} else {
Ok(None)
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::sync::tests::repo_init;
use super::*;
use crate::sync::tests::repo_init;
#[test]
fn test_get_config() {
let bad_dir_cfg =
get_config_string("oodly_noodly", "this.doesnt.exist");
assert!(bad_dir_cfg.is_err());
#[test]
fn test_get_config() {
let bad_dir_cfg = get_config_string(
&"oodly_noodly".into(),
"this.doesnt.exist",
);
assert!(bad_dir_cfg.is_err());
let (_td, repo) = repo_init().unwrap();
let path = repo.path();
let rpath = path.as_os_str().to_str().unwrap();
let bad_cfg = get_config_string(rpath, "this.doesnt.exist");
assert!(bad_cfg.is_ok());
assert!(bad_cfg.unwrap().is_none());
// repo init sets user.name
let good_cfg = get_config_string(rpath, "user.name");
assert!(good_cfg.is_ok());
assert!(good_cfg.unwrap().is_some());
}
let (_td, repo) = repo_init().unwrap();
let path = repo.path();
let rpath = path.as_os_str().to_str().unwrap();
let bad_cfg =
get_config_string(&rpath.into(), "this.doesnt.exist");
assert!(bad_cfg.is_ok());
assert!(bad_cfg.unwrap().is_none());
// repo init sets user.name
let good_cfg = get_config_string(&rpath.into(), "user.name");
assert!(good_cfg.is_ok());
assert!(good_cfg.unwrap().is_some());
}
}

View file

@ -1,253 +1,367 @@
//! credentials git helper
use super::remotes::get_default_remote_in_repo;
use crate::{
error::{Error, Result},
CWD,
use super::{
remotes::{
get_default_remote_for_fetch_in_repo,
get_default_remote_for_push_in_repo,
get_default_remote_in_repo,
},
repository::repo,
RepoPath,
};
use git2::{Config, CredentialHelper};
use crate::error::{Error, Result};
use git2::CredentialHelper;
/// basic Authentication Credentials
#[derive(Debug, Clone, Default, PartialEq)]
#[derive(Debug, Clone, Default, PartialEq, Eq)]
pub struct BasicAuthCredential {
///
pub username: Option<String>,
///
pub password: Option<String>,
///
pub username: Option<String>,
///
pub password: Option<String>,
}
impl BasicAuthCredential {
///
pub const fn is_complete(&self) -> bool {
self.username.is_some() && self.password.is_some()
}
///
pub const fn new(
username: Option<String>,
password: Option<String>,
) -> Self {
Self { username, password }
}
///
pub const fn is_complete(&self) -> bool {
self.username.is_some() && self.password.is_some()
}
///
pub const fn new(
username: Option<String>,
password: Option<String>,
) -> Self {
Self { username, password }
}
}
/// know if username and password are needed for this url
pub fn need_username_password() -> Result<bool> {
let repo = crate::sync::utils::repo(CWD)?;
let url = repo
.find_remote(&get_default_remote_in_repo(&repo)?)?
.url()
.ok_or(Error::UnknownRemote)?
.to_owned();
let is_http = url.starts_with("http");
Ok(is_http)
pub fn need_username_password(repo_path: &RepoPath) -> Result<bool> {
let repo = repo(repo_path)?;
let remote =
repo.find_remote(&get_default_remote_in_repo(&repo)?)?;
let url = remote
.pushurl()
.or_else(|| remote.url())
.ok_or(Error::UnknownRemote)?
.to_owned();
let is_http = url.starts_with("http");
Ok(is_http)
}
/// know if username and password are needed for this url
/// TODO: Very similar to `need_username_password_for_fetch`. Can be refactored. See also
/// `need_username_password`.
pub fn need_username_password_for_fetch(
repo_path: &RepoPath,
) -> Result<bool> {
let repo = repo(repo_path)?;
let remote = repo
.find_remote(&get_default_remote_for_fetch_in_repo(&repo)?)?;
let url = remote
.url()
.or_else(|| remote.url())
.ok_or(Error::UnknownRemote)?
.to_owned();
let is_http = url.starts_with("http");
Ok(is_http)
}
/// know if username and password are needed for this url
/// TODO: Very similar to `need_username_password_for_fetch`. Can be refactored. See also
/// `need_username_password`.
pub fn need_username_password_for_push(
repo_path: &RepoPath,
) -> Result<bool> {
let repo = repo(repo_path)?;
let remote = repo
.find_remote(&get_default_remote_for_push_in_repo(&repo)?)?;
let url = remote
.pushurl()
.or_else(|| remote.url())
.ok_or(Error::UnknownRemote)?
.to_owned();
let is_http = url.starts_with("http");
Ok(is_http)
}
/// extract username and password
pub fn extract_username_password() -> Result<BasicAuthCredential> {
let repo = crate::sync::utils::repo(CWD)?;
let url = repo
.find_remote(&get_default_remote_in_repo(&repo)?)?
.url()
.ok_or(Error::UnknownRemote)?
.to_owned();
let mut helper = CredentialHelper::new(&url);
pub fn extract_username_password(
repo_path: &RepoPath,
) -> Result<BasicAuthCredential> {
let repo = repo(repo_path)?;
let url = repo
.find_remote(&get_default_remote_in_repo(&repo)?)?
.url()
.ok_or(Error::UnknownRemote)?
.to_owned();
let mut helper = CredentialHelper::new(&url);
if let Ok(config) = Config::open_default() {
helper.config(&config);
}
Ok(match helper.execute() {
Some((username, password)) => {
BasicAuthCredential::new(Some(username), Some(password))
}
None => extract_cred_from_url(&url),
})
//TODO: look at Cred::credential_helper,
//if the username is in the url we need to set it here,
//I dont think `config` will pick it up
if let Ok(config) = repo.config() {
helper.config(&config);
}
Ok(match helper.execute() {
Some((username, password)) => {
BasicAuthCredential::new(Some(username), Some(password))
}
None => extract_cred_from_url(&url),
})
}
/// extract username and password
/// TODO: Very similar to `extract_username_password_for_fetch`. Can be refactored.
pub fn extract_username_password_for_fetch(
repo_path: &RepoPath,
) -> Result<BasicAuthCredential> {
let repo = repo(repo_path)?;
let url = repo
.find_remote(&get_default_remote_for_fetch_in_repo(&repo)?)?
.url()
.ok_or(Error::UnknownRemote)?
.to_owned();
let mut helper = CredentialHelper::new(&url);
//TODO: look at Cred::credential_helper,
//if the username is in the url we need to set it here,
//I dont think `config` will pick it up
if let Ok(config) = repo.config() {
helper.config(&config);
}
Ok(match helper.execute() {
Some((username, password)) => {
BasicAuthCredential::new(Some(username), Some(password))
}
None => extract_cred_from_url(&url),
})
}
/// extract username and password
/// TODO: Very similar to `extract_username_password_for_fetch`. Can be refactored.
pub fn extract_username_password_for_push(
repo_path: &RepoPath,
) -> Result<BasicAuthCredential> {
let repo = repo(repo_path)?;
let url = repo
.find_remote(&get_default_remote_for_push_in_repo(&repo)?)?
.url()
.ok_or(Error::UnknownRemote)?
.to_owned();
let mut helper = CredentialHelper::new(&url);
//TODO: look at Cred::credential_helper,
//if the username is in the url we need to set it here,
//I dont think `config` will pick it up
if let Ok(config) = repo.config() {
helper.config(&config);
}
Ok(match helper.execute() {
Some((username, password)) => {
BasicAuthCredential::new(Some(username), Some(password))
}
None => extract_cred_from_url(&url),
})
}
/// extract credentials from url
pub fn extract_cred_from_url(url: &str) -> BasicAuthCredential {
if let Ok(url) = url::Url::parse(url) {
BasicAuthCredential::new(
if url.username() == "" {
None
} else {
Some(url.username().to_owned())
},
url.password().map(std::borrow::ToOwned::to_owned),
)
} else {
BasicAuthCredential::new(None, None)
}
url::Url::parse(url).map_or_else(
|_| BasicAuthCredential::new(None, None),
|url| {
BasicAuthCredential::new(
if url.username() == "" {
None
} else {
Some(url.username().to_owned())
},
url.password().map(std::borrow::ToOwned::to_owned),
)
},
)
}
#[cfg(test)]
mod tests {
use crate::sync::{
cred::{
extract_cred_from_url, extract_username_password,
need_username_password, BasicAuthCredential,
},
remotes::DEFAULT_REMOTE_NAME,
tests::repo_init,
};
use serial_test::serial;
use std::env;
use crate::sync::{
cred::{
extract_cred_from_url, extract_username_password,
need_username_password, BasicAuthCredential,
},
remotes::DEFAULT_REMOTE_NAME,
tests::repo_init,
RepoPath,
};
use serial_test::serial;
#[test]
fn test_credential_complete() {
assert_eq!(
BasicAuthCredential::new(
Some("username".to_owned()),
Some("password".to_owned())
)
.is_complete(),
true
);
}
#[test]
fn test_credential_complete() {
assert!(BasicAuthCredential::new(
Some("username".to_owned()),
Some("password".to_owned())
)
.is_complete());
}
#[test]
fn test_credential_not_complete() {
assert_eq!(
BasicAuthCredential::new(
None,
Some("password".to_owned())
)
.is_complete(),
false
);
assert_eq!(
BasicAuthCredential::new(
Some("username".to_owned()),
None
)
.is_complete(),
false
);
assert_eq!(
BasicAuthCredential::new(None, None).is_complete(),
false
);
}
#[test]
fn test_credential_not_complete() {
assert!(!BasicAuthCredential::new(
None,
Some("password".to_owned())
)
.is_complete());
assert!(!BasicAuthCredential::new(
Some("username".to_owned()),
None
)
.is_complete());
assert!(!BasicAuthCredential::new(None, None).is_complete());
}
#[test]
fn test_extract_username_from_url() {
assert_eq!(
extract_cred_from_url("https://user@github.com"),
BasicAuthCredential::new(Some("user".to_owned()), None)
);
}
#[test]
fn test_extract_username_from_url() {
assert_eq!(
extract_cred_from_url("https://user@github.com"),
BasicAuthCredential::new(Some("user".to_owned()), None)
);
}
#[test]
fn test_extract_username_password_from_url() {
assert_eq!(
extract_cred_from_url("https://user:pwd@github.com"),
BasicAuthCredential::new(
Some("user".to_owned()),
Some("pwd".to_owned())
)
);
}
#[test]
fn test_extract_username_password_from_url() {
assert_eq!(
extract_cred_from_url("https://user:pwd@github.com"),
BasicAuthCredential::new(
Some("user".to_owned()),
Some("pwd".to_owned())
)
);
}
#[test]
fn test_extract_nothing_from_url() {
assert_eq!(
extract_cred_from_url("https://github.com"),
BasicAuthCredential::new(None, None)
);
}
#[test]
fn test_extract_nothing_from_url() {
assert_eq!(
extract_cred_from_url("https://github.com"),
BasicAuthCredential::new(None, None)
);
}
#[test]
#[serial]
fn test_need_username_password_if_https() {
let (_td, repo) = repo_init().unwrap();
let root = repo.path().parent().unwrap();
let repo_path = root.as_os_str().to_str().unwrap();
#[test]
#[serial]
fn test_need_username_password_if_https() {
let (_td, repo) = repo_init().unwrap();
let root = repo.path().parent().unwrap();
let repo_path: &RepoPath =
&root.as_os_str().to_str().unwrap().into();
env::set_current_dir(repo_path).unwrap();
repo.remote(DEFAULT_REMOTE_NAME, "http://user@github.com")
.unwrap();
repo.remote(DEFAULT_REMOTE_NAME, "http://user@github.com")
.unwrap();
assert_eq!(need_username_password().unwrap(), true);
}
assert!(need_username_password(repo_path).unwrap());
}
#[test]
#[serial]
fn test_dont_need_username_password_if_ssh() {
let (_td, repo) = repo_init().unwrap();
let root = repo.path().parent().unwrap();
let repo_path = root.as_os_str().to_str().unwrap();
#[test]
#[serial]
fn test_dont_need_username_password_if_ssh() {
let (_td, repo) = repo_init().unwrap();
let root = repo.path().parent().unwrap();
let repo_path: &RepoPath =
&root.as_os_str().to_str().unwrap().into();
env::set_current_dir(repo_path).unwrap();
repo.remote(DEFAULT_REMOTE_NAME, "git@github.com:user/repo")
.unwrap();
repo.remote(DEFAULT_REMOTE_NAME, "git@github.com:user/repo")
.unwrap();
assert_eq!(need_username_password().unwrap(), false);
}
assert!(!need_username_password(repo_path).unwrap());
}
#[test]
#[serial]
#[should_panic]
fn test_error_if_no_remote_when_trying_to_retrieve_if_need_username_password(
) {
let (_td, repo) = repo_init().unwrap();
let root = repo.path().parent().unwrap();
let repo_path = root.as_os_str().to_str().unwrap();
#[test]
#[serial]
fn test_dont_need_username_password_if_pushurl_ssh() {
let (_td, repo) = repo_init().unwrap();
let root = repo.path().parent().unwrap();
let repo_path: &RepoPath =
&root.as_os_str().to_str().unwrap().into();
env::set_current_dir(repo_path).unwrap();
repo.remote(DEFAULT_REMOTE_NAME, "http://user@github.com")
.unwrap();
repo.remote_set_pushurl(
DEFAULT_REMOTE_NAME,
Some("git@github.com:user/repo"),
)
.unwrap();
need_username_password().unwrap();
}
assert!(!need_username_password(repo_path).unwrap());
}
#[test]
#[serial]
fn test_extract_username_password_from_repo() {
let (_td, repo) = repo_init().unwrap();
let root = repo.path().parent().unwrap();
let repo_path = root.as_os_str().to_str().unwrap();
#[test]
#[serial]
#[should_panic]
fn test_error_if_no_remote_when_trying_to_retrieve_if_need_username_password(
) {
let (_td, repo) = repo_init().unwrap();
let root = repo.path().parent().unwrap();
let repo_path: &RepoPath =
&root.as_os_str().to_str().unwrap().into();
env::set_current_dir(repo_path).unwrap();
repo.remote(
DEFAULT_REMOTE_NAME,
"http://user:pass@github.com",
)
.unwrap();
need_username_password(repo_path).unwrap();
}
assert_eq!(
extract_username_password().unwrap(),
BasicAuthCredential::new(
Some("user".to_owned()),
Some("pass".to_owned())
)
);
}
#[test]
#[serial]
fn test_extract_username_password_from_repo() {
let (_td, repo) = repo_init().unwrap();
let root = repo.path().parent().unwrap();
let repo_path: &RepoPath =
&root.as_os_str().to_str().unwrap().into();
#[test]
#[serial]
fn test_extract_username_from_repo() {
let (_td, repo) = repo_init().unwrap();
let root = repo.path().parent().unwrap();
let repo_path = root.as_os_str().to_str().unwrap();
repo.remote(
DEFAULT_REMOTE_NAME,
"http://user:pass@github.com",
)
.unwrap();
env::set_current_dir(repo_path).unwrap();
repo.remote(DEFAULT_REMOTE_NAME, "http://user@github.com")
.unwrap();
assert_eq!(
extract_username_password(repo_path).unwrap(),
BasicAuthCredential::new(
Some("user".to_owned()),
Some("pass".to_owned())
)
);
}
assert_eq!(
extract_username_password().unwrap(),
BasicAuthCredential::new(Some("user".to_owned()), None)
);
}
#[test]
#[serial]
fn test_extract_username_from_repo() {
let (_td, repo) = repo_init().unwrap();
let root = repo.path().parent().unwrap();
let repo_path: &RepoPath =
&root.as_os_str().to_str().unwrap().into();
#[test]
#[serial]
#[should_panic]
fn test_error_if_no_remote_when_trying_to_extract_username_password(
) {
let (_td, repo) = repo_init().unwrap();
let root = repo.path().parent().unwrap();
let repo_path = root.as_os_str().to_str().unwrap();
repo.remote(DEFAULT_REMOTE_NAME, "http://user@github.com")
.unwrap();
env::set_current_dir(repo_path).unwrap();
assert_eq!(
extract_username_password(repo_path).unwrap(),
BasicAuthCredential::new(Some("user".to_owned()), None)
);
}
extract_username_password().unwrap();
}
#[test]
#[serial]
#[should_panic]
fn test_error_if_no_remote_when_trying_to_extract_username_password(
) {
let (_td, repo) = repo_init().unwrap();
let root = repo.path().parent().unwrap();
let repo_path: &RepoPath =
&root.as_os_str().to_str().unwrap().into();
extract_username_password(repo_path).unwrap();
}
}

File diff suppressed because it is too large Load diff

View file

@ -1,389 +1,501 @@
use super::utils::{repo, work_dir};
use crate::error::{Error, Result};
use scopetime::scope_time;
use std::{
fs::File,
io::{Read, Write},
path::Path,
process::Command,
use super::{repository::repo, RepoPath};
use crate::{
error::Result,
sync::{
branch::get_branch_upstream_merge,
config::{
push_default_strategy_config_repo,
PushDefaultStrategyConfig,
},
remotes::{proxy_auto, tags::tags_missing_remote, Callbacks},
},
};
const HOOK_POST_COMMIT: &str = ".git/hooks/post-commit";
const HOOK_PRE_COMMIT: &str = ".git/hooks/pre-commit";
const HOOK_COMMIT_MSG: &str = ".git/hooks/commit-msg";
const HOOK_COMMIT_MSG_TEMP_FILE: &str = ".git/COMMIT_EDITMSG";
/// this hook is documented here <https://git-scm.com/docs/githooks#_commit_msg>
/// we use the same convention as other git clients to create a temp file containing
/// the commit message at `.git/COMMIT_EDITMSG` and pass it's relative path as the only
/// parameter to the hook script.
pub fn hooks_commit_msg(
repo_path: &str,
msg: &mut String,
) -> Result<HookResult> {
scope_time!("hooks_commit_msg");
let work_dir = work_dir_as_string(repo_path)?;
if hook_runable(work_dir.as_str(), HOOK_COMMIT_MSG) {
let temp_file = Path::new(work_dir.as_str())
.join(HOOK_COMMIT_MSG_TEMP_FILE);
File::create(&temp_file)?.write_all(msg.as_bytes())?;
let res = run_hook(
work_dir.as_str(),
HOOK_COMMIT_MSG,
&[HOOK_COMMIT_MSG_TEMP_FILE],
)?;
// load possibly altered msg
msg.clear();
File::open(temp_file)?.read_to_string(msg)?;
Ok(res)
} else {
Ok(HookResult::Ok)
}
}
/// this hook is documented here <https://git-scm.com/docs/githooks#_pre_commit>
///
pub fn hooks_pre_commit(repo_path: &str) -> Result<HookResult> {
scope_time!("hooks_pre_commit");
let work_dir = work_dir_as_string(repo_path)?;
if hook_runable(work_dir.as_str(), HOOK_PRE_COMMIT) {
Ok(run_hook(work_dir.as_str(), HOOK_PRE_COMMIT, &[])?)
} else {
Ok(HookResult::Ok)
}
}
///
pub fn hooks_post_commit(repo_path: &str) -> Result<HookResult> {
scope_time!("hooks_post_commit");
let work_dir = work_dir_as_string(repo_path)?;
let work_dir_str = work_dir.as_str();
if hook_runable(work_dir_str, HOOK_POST_COMMIT) {
Ok(run_hook(work_dir_str, HOOK_POST_COMMIT, &[])?)
} else {
Ok(HookResult::Ok)
}
}
fn work_dir_as_string(repo_path: &str) -> Result<String> {
let repo = repo(repo_path)?;
work_dir(&repo)?
.to_str()
.map(std::string::ToString::to_string)
.ok_or_else(|| {
Error::Generic(
"workdir contains invalid utf8".to_string(),
)
})
}
fn hook_runable(path: &str, hook: &str) -> bool {
let path = Path::new(path);
let path = path.join(hook);
path.exists() && is_executable(&path)
}
use git2::{BranchType, Direction, Oid};
pub use git2_hooks::{PrePushRef, PrepareCommitMsgSource};
use scopetime::scope_time;
use std::collections::HashMap;
///
#[derive(Debug, PartialEq)]
#[derive(Debug, PartialEq, Eq)]
pub enum HookResult {
/// Everything went fine
Ok,
/// Hook returned error
NotOk(String),
/// Everything went fine
Ok,
/// Hook returned error
NotOk(String),
}
/// this function calls hook scripts based on conventions documented here
/// see <https://git-scm.com/docs/githooks>
fn run_hook(
path: &str,
hook_script: &str,
args: &[&str],
impl From<git2_hooks::HookResult> for HookResult {
fn from(v: git2_hooks::HookResult) -> Self {
match v {
git2_hooks::HookResult::NoHookFound => Self::Ok,
git2_hooks::HookResult::Run(response) => {
if response.is_successful() {
Self::Ok
} else {
Self::NotOk(if response.stderr.is_empty() {
response.stdout
} else if response.stdout.is_empty() {
response.stderr
} else {
format!(
"{}\n{}",
response.stdout, response.stderr
)
})
}
}
}
}
}
/// Retrieve advertised refs from the remote for the upcoming push.
fn advertised_remote_refs(
repo_path: &RepoPath,
remote: Option<&str>,
url: &str,
basic_credential: Option<crate::sync::cred::BasicAuthCredential>,
) -> Result<HashMap<String, Oid>> {
let repo = repo(repo_path)?;
let mut remote_handle = if let Some(name) = remote {
repo.find_remote(name)?
} else {
repo.remote_anonymous(url)?
};
let callbacks = Callbacks::new(None, basic_credential);
let conn = remote_handle.connect_auth(
Direction::Push,
Some(callbacks.callbacks()),
Some(proxy_auto()),
)?;
let mut map = HashMap::new();
for head in conn.list()? {
map.insert(head.name().to_string(), head.oid());
}
Ok(map)
}
/// Determine the remote ref name for a branch push.
///
/// Respects `push.default=upstream` config when set and upstream is configured.
/// Otherwise defaults to `refs/heads/{branch}`. Delete operations always use
/// the simple ref name.
fn get_remote_ref_for_push(
repo_path: &RepoPath,
branch: &str,
delete: bool,
) -> Result<String> {
// For delete operations, always use the simple ref name
// regardless of push.default configuration
if delete {
return Ok(format!("refs/heads/{branch}"));
}
let repo = repo(repo_path)?;
let push_default_strategy =
push_default_strategy_config_repo(&repo)?;
// When push.default=upstream, use the configured upstream ref if available
if push_default_strategy == PushDefaultStrategyConfig::Upstream {
if let Ok(Some(upstream_ref)) =
get_branch_upstream_merge(repo_path, branch)
{
return Ok(upstream_ref);
}
// If upstream strategy is set but no upstream is configured,
// fall through to default behavior
}
// Default: push to remote branch with same name as local
Ok(format!("refs/heads/{branch}"))
}
/// see `git2_hooks::hooks_commit_msg`
pub fn hooks_commit_msg(
repo_path: &RepoPath,
msg: &mut String,
) -> Result<HookResult> {
let arg_str = format!("{} {}", hook_script, args.join(" "));
let bash_args = vec!["-c".to_string(), arg_str];
scope_time!("hooks_commit_msg");
let output = Command::new("bash")
.args(bash_args)
.current_dir(path)
// This call forces Command to handle the Path environment correctly on windows,
// the specific env set here does not matter
// see https://github.com/rust-lang/rust/issues/37519
.env(
"DUMMY_ENV_TO_FIX_WINDOWS_CMD_RUNS",
"FixPathHandlingOnWindows",
)
.output()?;
let repo = repo(repo_path)?;
if output.status.success() {
Ok(HookResult::Ok)
} else {
let err = String::from_utf8_lossy(&output.stderr);
let out = String::from_utf8_lossy(&output.stdout);
let formatted = format!("{}{}", out, err);
Ok(HookResult::NotOk(formatted))
}
Ok(git2_hooks::hooks_commit_msg(&repo, None, msg)?.into())
}
#[cfg(not(windows))]
fn is_executable(path: &Path) -> bool {
use std::os::unix::fs::PermissionsExt;
let metadata = match path.metadata() {
Ok(metadata) => metadata,
Err(_) => return false,
};
/// see `git2_hooks::hooks_pre_commit`
pub fn hooks_pre_commit(repo_path: &RepoPath) -> Result<HookResult> {
scope_time!("hooks_pre_commit");
let permissions = metadata.permissions();
permissions.mode() & 0o111 != 0
let repo = repo(repo_path)?;
Ok(git2_hooks::hooks_pre_commit(&repo, None)?.into())
}
#[cfg(windows)]
/// windows does not consider bash scripts to be executable so we consider everything
/// to be executable (which is not far from the truth for windows platform.)
const fn is_executable(_: &Path) -> bool {
true
/// see `git2_hooks::hooks_post_commit`
pub fn hooks_post_commit(repo_path: &RepoPath) -> Result<HookResult> {
scope_time!("hooks_post_commit");
let repo = repo(repo_path)?;
Ok(git2_hooks::hooks_post_commit(&repo, None)?.into())
}
/// see `git2_hooks::hooks_prepare_commit_msg`
pub fn hooks_prepare_commit_msg(
repo_path: &RepoPath,
source: PrepareCommitMsgSource,
msg: &mut String,
) -> Result<HookResult> {
scope_time!("hooks_prepare_commit_msg");
let repo = repo(repo_path)?;
Ok(git2_hooks::hooks_prepare_commit_msg(
&repo, None, source, msg,
)?
.into())
}
/// see `git2_hooks::hooks_pre_push`
pub fn hooks_pre_push(
repo_path: &RepoPath,
remote: &str,
push: &PrePushTarget<'_>,
basic_credential: Option<crate::sync::cred::BasicAuthCredential>,
) -> Result<HookResult> {
scope_time!("hooks_pre_push");
let repo = repo(repo_path)?;
if !git2_hooks::hook_available(
&repo,
None,
git2_hooks::HOOK_PRE_PUSH,
)? {
return Ok(HookResult::Ok);
}
let git_remote = repo.find_remote(remote)?;
let url = git_remote
.pushurl()
.or_else(|| git_remote.url())
.ok_or_else(|| {
crate::error::Error::Generic(format!(
"remote '{remote}' has no URL configured"
))
})?
.to_string();
let advertised = advertised_remote_refs(
repo_path,
Some(remote),
&url,
basic_credential,
)?;
let updates = match push {
PrePushTarget::Branch { branch, delete } => {
let remote_ref =
get_remote_ref_for_push(repo_path, branch, *delete)?;
vec![pre_push_branch_update(
repo_path,
branch,
&remote_ref,
*delete,
&advertised,
)?]
}
PrePushTarget::Tags => {
pre_push_tag_updates(repo_path, remote, &advertised)?
}
};
Ok(git2_hooks::hooks_pre_push(
&repo,
None,
Some(remote),
&url,
&updates,
)?
.into())
}
/// Build a single pre-push update line for a branch.
fn pre_push_branch_update(
repo_path: &RepoPath,
branch_name: &str,
remote_ref: &str,
delete: bool,
advertised: &HashMap<String, Oid>,
) -> Result<PrePushRef> {
let repo = repo(repo_path)?;
let local_ref = format!("refs/heads/{branch_name}");
let local_oid = (!delete)
.then(|| {
repo.find_branch(branch_name, BranchType::Local)
.ok()
.and_then(|branch| branch.get().peel_to_commit().ok())
.map(|commit| commit.id())
})
.flatten();
let remote_oid = advertised.get(remote_ref).copied();
Ok(PrePushRef::new(
local_ref, local_oid, remote_ref, remote_oid,
))
}
/// Build pre-push updates for tags that are missing on the remote.
fn pre_push_tag_updates(
repo_path: &RepoPath,
remote: &str,
advertised: &HashMap<String, Oid>,
) -> Result<Vec<PrePushRef>> {
let repo = repo(repo_path)?;
let tags = tags_missing_remote(repo_path, remote, None)?;
let mut updates = Vec::with_capacity(tags.len());
for tag_ref in tags {
if let Ok(reference) = repo.find_reference(&tag_ref) {
let tag_oid = reference.target().or_else(|| {
reference.peel_to_commit().ok().map(|c| c.id())
});
let remote_ref = tag_ref.clone();
let advertised_oid = advertised.get(&remote_ref).copied();
updates.push(PrePushRef::new(
tag_ref.clone(),
tag_oid,
remote_ref,
advertised_oid,
));
}
}
Ok(updates)
}
/// What is being pushed.
pub enum PrePushTarget<'a> {
/// Push a single branch.
Branch {
/// Local branch name being pushed.
branch: &'a str,
/// Whether this is a delete push.
delete: bool,
},
/// Push tags.
Tags,
}
#[cfg(test)]
mod tests {
use super::*;
use crate::sync::tests::repo_init;
use std::fs::{self, File};
use std::{ffi::OsString, io::Write as _, path::Path};
#[test]
fn test_smoke() {
let (_td, repo) = repo_init().unwrap();
let root = repo.path().parent().unwrap();
let repo_path = root.as_os_str().to_str().unwrap();
use git2::Repository;
use tempfile::TempDir;
let mut msg = String::from("test");
let res = hooks_commit_msg(repo_path, &mut msg).unwrap();
use super::*;
use crate::sync::tests::repo_init_with_prefix;
assert_eq!(res, HookResult::Ok);
fn repo_init() -> Result<(TempDir, Repository)> {
let mut os_string: OsString = OsString::new();
let res = hooks_post_commit(repo_path).unwrap();
os_string.push("gitui $# ' ");
assert_eq!(res, HookResult::Ok);
}
#[cfg(target_os = "linux")]
{
use std::os::unix::ffi::OsStrExt;
fn create_hook(path: &Path, hook_path: &str, hook_script: &[u8]) {
File::create(&path.join(hook_path))
.unwrap()
.write_all(hook_script)
.unwrap();
const INVALID_UTF8: &[u8] = b"\xED\xA0\x80";
#[cfg(not(windows))]
{
Command::new("chmod")
.args(&["+x", hook_path])
.current_dir(path)
.output()
.unwrap();
}
}
os_string.push(std::ffi::OsStr::from_bytes(INVALID_UTF8));
#[test]
fn test_hooks_commit_msg_ok() {
let (_td, repo) = repo_init().unwrap();
let root = repo.path().parent().unwrap();
let repo_path = root.as_os_str().to_str().unwrap();
assert!(os_string.to_str().is_none());
}
let hook = b"#!/bin/sh
exit 0
";
os_string.push(" ");
create_hook(root, HOOK_COMMIT_MSG, hook);
repo_init_with_prefix(os_string)
}
let mut msg = String::from("test");
let res = hooks_commit_msg(repo_path, &mut msg).unwrap();
fn create_hook_in_path(path: &Path, hook_script: &[u8]) {
std::fs::File::create(path)
.unwrap()
.write_all(hook_script)
.unwrap();
assert_eq!(res, HookResult::Ok);
#[cfg(unix)]
{
std::process::Command::new("chmod")
.arg("+x")
.arg(path)
// .current_dir(path)
.output()
.unwrap();
}
}
assert_eq!(msg, String::from("test"));
}
#[test]
fn test_post_commit_hook_reject_in_subfolder() {
let (_td, repo) = repo_init().unwrap();
let root = repo.workdir().unwrap();
#[test]
fn test_pre_commit_sh() {
let (_td, repo) = repo_init().unwrap();
let root = repo.path().parent().unwrap();
let repo_path = root.as_os_str().to_str().unwrap();
let hook = b"#!/bin/sh
echo 'rejected'
exit 1
";
let hook = b"#!/bin/sh
exit 0
";
git2_hooks::create_hook(
&repo,
git2_hooks::HOOK_POST_COMMIT,
hook,
);
create_hook(root, HOOK_PRE_COMMIT, hook);
let res = hooks_pre_commit(repo_path).unwrap();
assert_eq!(res, HookResult::Ok);
}
let subfolder = root.join("foo/");
std::fs::create_dir_all(&subfolder).unwrap();
#[test]
fn test_pre_commit_fail_sh() {
let (_td, repo) = repo_init().unwrap();
let root = repo.path().parent().unwrap();
let repo_path = root.as_os_str().to_str().unwrap();
let res = hooks_post_commit(&subfolder.into()).unwrap();
let hook = b"#!/bin/sh
echo 'rejected'
assert_eq!(
res,
HookResult::NotOk(String::from("rejected\n"))
);
}
// make sure we run the hooks with the correct pwd.
// for non-bare repos this is the dir of the worktree
// unfortunately does not work on windows
#[test]
#[cfg(unix)]
fn test_pre_commit_workdir() {
let (_td, repo) = repo_init().unwrap();
let root = repo.workdir().unwrap();
let repo_path: &RepoPath = &root.to_path_buf().into();
let hook = b"#!/bin/sh
echo \"$(pwd)\"
exit 1
";
git2_hooks::create_hook(
&repo,
git2_hooks::HOOK_PRE_COMMIT,
hook,
);
let res = hooks_pre_commit(repo_path).unwrap();
if let HookResult::NotOk(res) = res {
assert_eq!(
res.trim_end().trim_end_matches('/'),
// TODO: fix if output isn't utf8.
root.to_string_lossy().trim_end_matches('/'),
);
} else {
assert!(false);
}
}
#[test]
fn test_hooks_commit_msg_reject_in_subfolder() {
let (_td, repo) = repo_init().unwrap();
let root = repo.workdir().unwrap();
let hook = b"#!/bin/sh
echo 'msg' > \"$1\"
echo 'rejected'
exit 1
";
git2_hooks::create_hook(
&repo,
git2_hooks::HOOK_COMMIT_MSG,
hook,
);
let subfolder = root.join("foo/");
std::fs::create_dir_all(&subfolder).unwrap();
let mut msg = String::from("test");
let res =
hooks_commit_msg(&subfolder.into(), &mut msg).unwrap();
assert_eq!(
res,
HookResult::NotOk(String::from("rejected\n"))
);
assert_eq!(msg, String::from("msg\n"));
}
#[test]
fn test_hooks_commit_msg_reject_in_hooks_folder_githooks_moved_absolute(
) {
let (_td, repo) = repo_init().unwrap();
let root = repo.workdir().unwrap();
let mut config = repo.config().unwrap();
const HOOKS_DIR: &str = "my_hooks";
config.set_str("core.hooksPath", HOOKS_DIR).unwrap();
let hook = b"#!/bin/sh
echo 'msg' > \"$1\"
echo 'rejected'
exit 1
";
let hooks_folder = root.join(HOOKS_DIR);
std::fs::create_dir_all(&hooks_folder).unwrap();
create_hook_in_path(&hooks_folder.join("commit-msg"), hook);
let mut msg = String::from("test");
let res =
hooks_commit_msg(&hooks_folder.into(), &mut msg).unwrap();
assert_eq!(
res,
HookResult::NotOk(String::from("rejected\n"))
);
assert_eq!(msg, String::from("msg\n"));
}
#[test]
fn test_pre_push_hook_rejects_based_on_stdin() {
let (_td, repo) = repo_init().unwrap();
let hook = b"#!/bin/sh
cat
exit 1
";
create_hook(root, HOOK_PRE_COMMIT, hook);
let res = hooks_pre_commit(repo_path).unwrap();
assert!(res != HookResult::Ok);
}
git2_hooks::create_hook(
&repo,
git2_hooks::HOOK_PRE_PUSH,
hook,
);
#[test]
fn test_pre_commit_py() {
let (_td, repo) = repo_init().unwrap();
let root = repo.path().parent().unwrap();
let repo_path = root.as_os_str().to_str().unwrap();
let commit_id = repo.head().unwrap().target().unwrap();
let update = git2_hooks::PrePushRef::new(
"refs/heads/master",
Some(commit_id),
"refs/heads/master",
None,
);
// mirror how python pre-commmit sets itself up
#[cfg(not(windows))]
let hook = b"#!/usr/bin/env python
import sys
sys.exit(0)
";
#[cfg(windows)]
let hook = b"#!/bin/env python.exe
import sys
sys.exit(0)
";
let expected_stdin =
git2_hooks::PrePushRef::to_stdin(&[update.clone()]);
create_hook(root, HOOK_PRE_COMMIT, hook);
let res = hooks_pre_commit(repo_path).unwrap();
assert_eq!(res, HookResult::Ok);
}
let res = git2_hooks::hooks_pre_push(
&repo,
None,
Some("origin"),
"https://github.com/test/repo.git",
&[update],
)
.unwrap();
#[test]
fn test_pre_commit_fail_py() {
let (_td, repo) = repo_init().unwrap();
let root = repo.path().parent().unwrap();
let repo_path = root.as_os_str().to_str().unwrap();
// mirror how python pre-commmit sets itself up
#[cfg(not(windows))]
let hook = b"#!/usr/bin/env python
import sys
sys.exit(1)
";
#[cfg(windows)]
let hook = b"#!/bin/env python.exe
import sys
sys.exit(1)
";
create_hook(root, HOOK_PRE_COMMIT, hook);
let res = hooks_pre_commit(repo_path).unwrap();
assert!(res != HookResult::Ok);
}
#[test]
fn test_hooks_commit_msg_reject() {
let (_td, repo) = repo_init().unwrap();
let root = repo.path().parent().unwrap();
let repo_path = root.as_os_str().to_str().unwrap();
let hook = b"#!/bin/sh
echo 'msg' > $1
echo 'rejected'
exit 1
";
create_hook(root, HOOK_COMMIT_MSG, hook);
let mut msg = String::from("test");
let res = hooks_commit_msg(repo_path, &mut msg).unwrap();
assert_eq!(
res,
HookResult::NotOk(String::from("rejected\n"))
);
assert_eq!(msg, String::from("msg\n"));
}
#[test]
fn test_hooks_commit_msg_reject_in_subfolder() {
let (_td, repo) = repo_init().unwrap();
let root = repo.path().parent().unwrap();
// let repo_path = root.as_os_str().to_str().unwrap();
let hook = b"#!/bin/sh
echo 'msg' > $1
echo 'rejected'
exit 1
";
create_hook(root, HOOK_COMMIT_MSG, hook);
let subfolder = root.join("foo/");
fs::create_dir_all(&subfolder).unwrap();
let mut msg = String::from("test");
let res =
hooks_commit_msg(subfolder.to_str().unwrap(), &mut msg)
.unwrap();
assert_eq!(
res,
HookResult::NotOk(String::from("rejected\n"))
);
assert_eq!(msg, String::from("msg\n"));
}
#[test]
fn test_commit_msg_no_block_but_alter() {
let (_td, repo) = repo_init().unwrap();
let root = repo.path().parent().unwrap();
let repo_path = root.as_os_str().to_str().unwrap();
let hook = b"#!/bin/sh
echo 'msg' > $1
exit 0
";
create_hook(root, HOOK_COMMIT_MSG, hook);
let mut msg = String::from("test");
let res = hooks_commit_msg(repo_path, &mut msg).unwrap();
assert_eq!(res, HookResult::Ok);
assert_eq!(msg, String::from("msg\n"));
}
#[test]
fn test_post_commit_hook_reject_in_subfolder() {
let (_td, repo) = repo_init().unwrap();
let root = repo.path().parent().unwrap();
let hook = b"#!/bin/sh
echo 'rejected'
exit 1
";
create_hook(root, HOOK_POST_COMMIT, hook);
let subfolder = root.join("foo/");
fs::create_dir_all(&subfolder).unwrap();
let res =
hooks_post_commit(subfolder.to_str().unwrap()).unwrap();
assert_eq!(
res,
HookResult::NotOk(String::from("rejected\n"))
);
}
let git2_hooks::HookResult::Run(response) = res else {
panic!("Expected Run result");
};
assert!(!response.is_successful());
assert_eq!(response.stdout, expected_stdin);
assert!(expected_stdin.contains("refs/heads/master"));
}
}

View file

@ -1,187 +1,194 @@
use super::{
diff::{get_diff_raw, HunkHeader},
utils::repo,
diff::{get_diff_raw, DiffOptions, HunkHeader},
RepoPath,
};
use crate::{
error::{Error, Result},
hash,
error::{Error, Result},
hash,
sync::repository::repo,
};
use git2::{ApplyLocation, ApplyOptions, Diff};
use scopetime::scope_time;
///
pub fn stage_hunk(
repo_path: &str,
file_path: &str,
hunk_hash: u64,
repo_path: &RepoPath,
file_path: &str,
hunk_hash: u64,
options: Option<DiffOptions>,
) -> Result<()> {
scope_time!("stage_hunk");
scope_time!("stage_hunk");
let repo = repo(repo_path)?;
let repo = repo(repo_path)?;
let diff = get_diff_raw(&repo, file_path, false, false, None)?;
let diff = get_diff_raw(&repo, file_path, false, false, options)?;
let mut opt = ApplyOptions::new();
opt.hunk_callback(|hunk| {
hunk.map_or(false, |hunk| {
let header = HunkHeader::from(hunk);
hash(&header) == hunk_hash
})
});
let mut opt = ApplyOptions::new();
opt.hunk_callback(|hunk| {
hunk.is_some_and(|hunk| {
let header = HunkHeader::from(hunk);
hash(&header) == hunk_hash
})
});
repo.apply(&diff, ApplyLocation::Index, Some(&mut opt))?;
repo.apply(&diff, ApplyLocation::Index, Some(&mut opt))?;
Ok(())
Ok(())
}
/// this will fail for an all untracked file
pub fn reset_hunk(
repo_path: &str,
file_path: &str,
hunk_hash: u64,
repo_path: &RepoPath,
file_path: &str,
hunk_hash: u64,
options: Option<DiffOptions>,
) -> Result<()> {
scope_time!("reset_hunk");
scope_time!("reset_hunk");
let repo = repo(repo_path)?;
let repo = repo(repo_path)?;
let diff = get_diff_raw(&repo, file_path, false, false, None)?;
let diff = get_diff_raw(&repo, file_path, false, false, options)?;
let hunk_index = find_hunk_index(&diff, hunk_hash);
if let Some(hunk_index) = hunk_index {
let mut hunk_idx = 0;
let mut opt = ApplyOptions::new();
opt.hunk_callback(|_hunk| {
let res = hunk_idx == hunk_index;
hunk_idx += 1;
res
});
let hunk_index = find_hunk_index(&diff, hunk_hash);
if let Some(hunk_index) = hunk_index {
let mut hunk_idx = 0;
let mut opt = ApplyOptions::new();
opt.hunk_callback(|_hunk| {
let res = hunk_idx == hunk_index;
hunk_idx += 1;
res
});
let diff = get_diff_raw(&repo, file_path, false, true, None)?;
let diff = get_diff_raw(&repo, file_path, false, true, None)?;
repo.apply(&diff, ApplyLocation::WorkDir, Some(&mut opt))?;
repo.apply(&diff, ApplyLocation::WorkDir, Some(&mut opt))?;
Ok(())
} else {
Err(Error::Generic("hunk not found".to_string()))
}
Ok(())
} else {
Err(Error::Generic("hunk not found".to_string()))
}
}
fn find_hunk_index(diff: &Diff, hunk_hash: u64) -> Option<usize> {
let mut result = None;
let mut result = None;
let mut hunk_count = 0;
let mut hunk_count = 0;
let foreach_result = diff.foreach(
&mut |_, _| true,
None,
Some(&mut |_, hunk| {
let header = HunkHeader::from(hunk);
if hash(&header) == hunk_hash {
result = Some(hunk_count);
}
hunk_count += 1;
true
}),
None,
);
let foreach_result = diff.foreach(
&mut |_, _| true,
None,
Some(&mut |_, hunk| {
let header = HunkHeader::from(hunk);
if hash(&header) == hunk_hash {
result = Some(hunk_count);
}
hunk_count += 1;
true
}),
None,
);
if foreach_result.is_ok() {
result
} else {
None
}
if foreach_result.is_ok() {
result
} else {
None
}
}
///
pub fn unstage_hunk(
repo_path: &str,
file_path: &str,
hunk_hash: u64,
repo_path: &RepoPath,
file_path: &str,
hunk_hash: u64,
options: Option<DiffOptions>,
) -> Result<bool> {
scope_time!("revert_hunk");
scope_time!("revert_hunk");
let repo = repo(repo_path)?;
let repo = repo(repo_path)?;
let diff = get_diff_raw(&repo, file_path, true, false, None)?;
let diff_count_positive = diff.deltas().len();
let diff = get_diff_raw(&repo, file_path, true, false, options)?;
let diff_count_positive = diff.deltas().len();
let hunk_index = find_hunk_index(&diff, hunk_hash);
let hunk_index = hunk_index.map_or_else(
|| Err(Error::Generic("hunk not found".to_string())),
Ok,
)?;
let hunk_index = find_hunk_index(&diff, hunk_hash);
let hunk_index = hunk_index.map_or_else(
|| Err(Error::Generic("hunk not found".to_string())),
Ok,
)?;
let diff = get_diff_raw(&repo, file_path, true, true, None)?;
let diff = get_diff_raw(&repo, file_path, true, true, options)?;
if diff.deltas().len() != diff_count_positive {
return Err(Error::Generic(format!(
"hunk error: {}!={}",
diff.deltas().len(),
diff_count_positive
)));
}
if diff.deltas().len() != diff_count_positive {
return Err(Error::Generic(format!(
"hunk error: {}!={}",
diff.deltas().len(),
diff_count_positive
)));
}
let mut count = 0;
{
let mut hunk_idx = 0;
let mut opt = ApplyOptions::new();
opt.hunk_callback(|_hunk| {
let res = if hunk_idx == hunk_index {
count += 1;
true
} else {
false
};
let mut count = 0;
{
let mut hunk_idx = 0;
let mut opt = ApplyOptions::new();
opt.hunk_callback(|_hunk| {
let res = if hunk_idx == hunk_index {
count += 1;
true
} else {
false
};
hunk_idx += 1;
hunk_idx += 1;
res
});
res
});
repo.apply(&diff, ApplyLocation::Index, Some(&mut opt))?;
}
repo.apply(&diff, ApplyLocation::Index, Some(&mut opt))?;
}
Ok(count == 1)
Ok(count == 1)
}
#[cfg(test)]
mod tests {
use super::*;
use crate::{
error::Result,
sync::{diff::get_diff, tests::repo_init_empty},
};
use std::{
fs::{self, File},
io::Write,
path::Path,
};
use super::*;
use crate::{
error::Result,
sync::{diff::get_diff, tests::repo_init_empty},
};
use std::{
fs::{self, File},
io::Write,
path::Path,
};
#[test]
fn reset_untracked_file_which_will_not_find_hunk() -> Result<()> {
let file_path = Path::new("foo/foo.txt");
let (_td, repo) = repo_init_empty()?;
let root = repo.path().parent().unwrap();
let repo_path = root.as_os_str().to_str().unwrap();
#[test]
fn reset_untracked_file_which_will_not_find_hunk() -> Result<()> {
let file_path = Path::new("foo/foo.txt");
let (_td, repo) = repo_init_empty()?;
let root = repo.path().parent().unwrap();
let repo_path: &RepoPath =
&root.as_os_str().to_str().unwrap().into();
let sub_path = root.join("foo/");
let sub_path = root.join("foo/");
fs::create_dir_all(&sub_path)?;
File::create(root.join(file_path))?.write_all(b"test")?;
fs::create_dir_all(&sub_path)?;
File::create(&root.join(file_path))?.write_all(b"test")?;
let sub_path: &RepoPath = &sub_path.to_str().unwrap().into();
let diff = get_diff(
sub_path,
file_path.to_str().unwrap(),
false,
None,
)?;
let diff = get_diff(
sub_path.to_str().unwrap(),
file_path.to_str().unwrap(),
false,
)?;
assert!(reset_hunk(
repo_path,
file_path.to_str().unwrap(),
diff.hunks[0].header_hash,
None,
)
.is_err());
assert!(reset_hunk(
repo_path,
file_path.to_str().unwrap(),
diff.hunks[0].header_hash,
)
.is_err());
Ok(())
}
Ok(())
}
}

View file

@ -1,127 +1,159 @@
use super::utils::{repo, work_dir};
use crate::error::Result;
use super::{utils::work_dir, RepoPath};
use crate::{
error::{Error, Result},
sync::repository::repo,
};
use scopetime::scope_time;
use std::{
fs::{File, OpenOptions},
io::{Read, Seek, SeekFrom, Write},
path::Path,
fs::{File, OpenOptions},
io::{Read, Seek, SeekFrom, Write},
path::Path,
};
static GITIGNORE: &str = ".gitignore";
/// add file or path to root ignore file
pub fn add_to_ignore(
repo_path: &str,
path_to_ignore: &str,
repo_path: &RepoPath,
path_to_ignore: &str,
) -> Result<()> {
scope_time!("add_to_ignore");
scope_time!("add_to_ignore");
let repo = repo(repo_path)?;
let repo = repo(repo_path)?;
let ignore_file = work_dir(&repo)?.join(GITIGNORE);
if Path::new(path_to_ignore).file_name()
== Path::new(GITIGNORE).file_name()
{
return Err(Error::Generic(String::from(
"cannot ignore gitignore",
)));
}
let optional_newline = ignore_file.exists()
&& !file_ends_with_newline(&ignore_file)?;
let ignore_file = work_dir(&repo)?.join(GITIGNORE);
let mut file = OpenOptions::new()
.append(true)
.create(true)
.open(ignore_file)?;
let optional_newline = ignore_file.exists()
&& !file_ends_with_newline(&ignore_file)?;
writeln!(
file,
"{}{}",
if optional_newline { "\n" } else { "" },
path_to_ignore
)?;
let mut file = OpenOptions::new()
.append(true)
.create(true)
.open(ignore_file)?;
Ok(())
writeln!(
file,
"{}{}",
if optional_newline { "\n" } else { "" },
path_to_ignore
)?;
Ok(())
}
fn file_ends_with_newline(file: &Path) -> Result<bool> {
let mut file = File::open(file)?;
let size = file.metadata()?.len();
let mut file = File::open(file)?;
let size = file.metadata()?.len();
file.seek(SeekFrom::Start(size.saturating_sub(1)))?;
let mut last_char = String::with_capacity(1);
file.read_to_string(&mut last_char)?;
file.seek(SeekFrom::Start(size.saturating_sub(1)))?;
let mut last_char = String::with_capacity(1);
file.read_to_string(&mut last_char)?;
Ok(last_char == "\n")
Ok(last_char == "\n")
}
#[cfg(test)]
mod tests {
use super::*;
use crate::sync::tests::repo_init;
use io::BufRead;
use std::{fs::File, io, path::Path};
use super::*;
use crate::sync::{tests::repo_init, utils::repo_write_file};
use io::BufRead;
use pretty_assertions::assert_eq;
use std::{fs::File, io, path::Path};
#[test]
fn test_empty() -> Result<()> {
let ignore_file_path = Path::new(".gitignore");
let file_path = Path::new("foo.txt");
let (_td, repo) = repo_init()?;
let root = repo.path().parent().unwrap();
let repo_path = root.as_os_str().to_str().unwrap();
#[test]
fn test_empty() -> Result<()> {
let ignore_file_path = Path::new(".gitignore");
let file_path = Path::new("foo.txt");
let (_td, repo) = repo_init()?;
let root = repo.path().parent().unwrap();
let repo_path: &RepoPath =
&root.as_os_str().to_str().unwrap().into();
File::create(&root.join(file_path))?.write_all(b"test")?;
File::create(root.join(file_path))?.write_all(b"test")?;
assert_eq!(root.join(ignore_file_path).exists(), false);
add_to_ignore(repo_path, file_path.to_str().unwrap())?;
assert_eq!(root.join(ignore_file_path).exists(), true);
assert_eq!(root.join(ignore_file_path).exists(), false);
add_to_ignore(repo_path, file_path.to_str().unwrap())?;
assert_eq!(root.join(ignore_file_path).exists(), true);
Ok(())
}
Ok(())
}
fn read_lines<P>(
filename: P,
) -> io::Result<io::Lines<io::BufReader<File>>>
where
P: AsRef<Path>,
{
let file = File::open(filename)?;
Ok(io::BufReader::new(file).lines())
}
fn read_lines<P>(
filename: P,
) -> io::Result<io::Lines<io::BufReader<File>>>
where
P: AsRef<Path>,
{
let file = File::open(filename)?;
Ok(io::BufReader::new(file).lines())
}
#[test]
fn test_append() -> Result<()> {
let ignore_file_path = Path::new(".gitignore");
let file_path = Path::new("foo.txt");
let (_td, repo) = repo_init()?;
let root = repo.path().parent().unwrap();
let repo_path = root.as_os_str().to_str().unwrap();
#[test]
fn test_append() -> Result<()> {
let ignore_file_path = Path::new(".gitignore");
let file_path = Path::new("foo.txt");
let (_td, repo) = repo_init()?;
let root = repo.path().parent().unwrap();
let repo_path: &RepoPath =
&root.as_os_str().to_str().unwrap().into();
File::create(&root.join(file_path))?.write_all(b"test")?;
File::create(&root.join(ignore_file_path))?
.write_all(b"foo\n")?;
File::create(root.join(file_path))?.write_all(b"test")?;
File::create(root.join(ignore_file_path))?
.write_all(b"foo\n")?;
add_to_ignore(repo_path, file_path.to_str().unwrap())?;
add_to_ignore(repo_path, file_path.to_str().unwrap())?;
let mut lines =
read_lines(&root.join(ignore_file_path)).unwrap();
assert_eq!(&lines.nth(1).unwrap().unwrap(), "foo.txt");
let mut lines =
read_lines(root.join(ignore_file_path)).unwrap();
assert_eq!(&lines.nth(1).unwrap().unwrap(), "foo.txt");
Ok(())
}
Ok(())
}
#[test]
fn test_append_no_newline_at_end() -> Result<()> {
let ignore_file_path = Path::new(".gitignore");
let file_path = Path::new("foo.txt");
let (_td, repo) = repo_init()?;
let root = repo.path().parent().unwrap();
let repo_path = root.as_os_str().to_str().unwrap();
#[test]
fn test_append_no_newline_at_end() -> Result<()> {
let ignore_file_path = Path::new(".gitignore");
let file_path = Path::new("foo.txt");
let (_td, repo) = repo_init()?;
let root = repo.path().parent().unwrap();
let repo_path: &RepoPath =
&root.as_os_str().to_str().unwrap().into();
File::create(&root.join(file_path))?.write_all(b"test")?;
File::create(&root.join(ignore_file_path))?
.write_all(b"foo")?;
File::create(root.join(file_path))?.write_all(b"test")?;
File::create(root.join(ignore_file_path))?
.write_all(b"foo")?;
add_to_ignore(repo_path, file_path.to_str().unwrap())?;
add_to_ignore(repo_path, file_path.to_str().unwrap())?;
let mut lines =
read_lines(&root.join(ignore_file_path)).unwrap();
assert_eq!(&lines.nth(1).unwrap().unwrap(), "foo.txt");
let mut lines =
read_lines(root.join(ignore_file_path)).unwrap();
assert_eq!(&lines.nth(1).unwrap().unwrap(), "foo.txt");
Ok(())
}
Ok(())
}
#[test]
fn test_ignore_ignore() {
let ignore_file_path = Path::new(".gitignore");
let (_td, repo) = repo_init().unwrap();
let root = repo.path().parent().unwrap();
let repo_path: &RepoPath =
&root.as_os_str().to_str().unwrap().into();
repo_write_file(&repo, ".gitignore", "#foo").unwrap();
let res = add_to_ignore(repo_path, ".gitignore");
assert!(res.is_err());
let lines = read_lines(root.join(ignore_file_path)).unwrap();
assert_eq!(lines.count(), 1);
}
}

View file

@ -1,248 +1,386 @@
use super::CommitId;
use super::{CommitId, SharedCommitFilterFn};
use crate::error::Result;
use git2::{Commit, Oid, Repository};
use gix::revision::Walk;
use std::{
cmp::Ordering,
collections::{BinaryHeap, HashSet},
sync::Arc,
cmp::Ordering,
collections::{BinaryHeap, HashSet},
};
struct TimeOrderedCommit<'a>(Commit<'a>);
impl<'a> Eq for TimeOrderedCommit<'a> {}
impl Eq for TimeOrderedCommit<'_> {}
impl<'a> PartialEq for TimeOrderedCommit<'a> {
fn eq(&self, other: &Self) -> bool {
self.0.time().eq(&other.0.time())
}
impl PartialEq for TimeOrderedCommit<'_> {
fn eq(&self, other: &Self) -> bool {
self.0.time().eq(&other.0.time())
}
}
impl<'a> PartialOrd for TimeOrderedCommit<'a> {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
self.0.time().partial_cmp(&other.0.time())
}
impl PartialOrd for TimeOrderedCommit<'_> {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
Some(self.cmp(other))
}
}
impl<'a> Ord for TimeOrderedCommit<'a> {
fn cmp(&self, other: &Self) -> Ordering {
self.0.time().cmp(&other.0.time())
}
impl Ord for TimeOrderedCommit<'_> {
fn cmp(&self, other: &Self) -> Ordering {
self.0.time().cmp(&other.0.time())
}
}
///
pub type LogWalkerFilter = Arc<
Box<dyn Fn(&Repository, &CommitId) -> Result<bool> + Send + Sync>,
>;
///
pub struct LogWalker<'a> {
commits: BinaryHeap<TimeOrderedCommit<'a>>,
visited: HashSet<Oid>,
limit: usize,
repo: &'a Repository,
filter: Option<LogWalkerFilter>,
commits: BinaryHeap<TimeOrderedCommit<'a>>,
visited: HashSet<Oid>,
limit: usize,
repo: &'a Repository,
filter: Option<SharedCommitFilterFn>,
}
impl<'a> LogWalker<'a> {
///
pub fn new(repo: &'a Repository, limit: usize) -> Result<Self> {
let c = repo.head()?.peel_to_commit()?;
///
pub fn new(repo: &'a Repository, limit: usize) -> Result<Self> {
let c = repo.head()?.peel_to_commit()?;
let mut commits = BinaryHeap::with_capacity(10);
commits.push(TimeOrderedCommit(c));
let mut commits = BinaryHeap::with_capacity(10);
commits.push(TimeOrderedCommit(c));
Ok(Self {
commits,
limit,
visited: HashSet::with_capacity(1000),
repo,
filter: None,
})
}
Ok(Self {
commits,
limit,
visited: HashSet::with_capacity(1000),
repo,
filter: None,
})
}
///
pub fn filter(self, filter: Option<LogWalkerFilter>) -> Self {
Self { filter, ..self }
}
///
pub fn visited(&self) -> usize {
self.visited.len()
}
///
pub fn read(&mut self, out: &mut Vec<CommitId>) -> Result<usize> {
let mut count = 0_usize;
///
#[must_use]
pub fn filter(
self,
filter: Option<SharedCommitFilterFn>,
) -> Self {
Self { filter, ..self }
}
while let Some(c) = self.commits.pop() {
for p in c.0.parents() {
self.visit(p);
}
///
pub fn read(&mut self, out: &mut Vec<CommitId>) -> Result<usize> {
let mut count = 0_usize;
let id: CommitId = c.0.id().into();
let commit_should_be_included =
if let Some(ref filter) = self.filter {
filter(self.repo, &id)?
} else {
true
};
while let Some(c) = self.commits.pop() {
for p in c.0.parents() {
self.visit(p);
}
if commit_should_be_included {
out.push(id);
}
let id: CommitId = c.0.id().into();
let commit_should_be_included =
if let Some(ref filter) = self.filter {
filter(self.repo, &id)?
} else {
true
};
count += 1;
if count == self.limit {
break;
}
}
if commit_should_be_included {
out.push(id);
}
Ok(count)
}
count += 1;
if count == self.limit {
break;
}
}
//
fn visit(&mut self, c: Commit<'a>) {
if !self.visited.contains(&c.id()) {
self.visited.insert(c.id());
self.commits.push(TimeOrderedCommit(c));
}
}
Ok(count)
}
//
fn visit(&mut self, c: Commit<'a>) {
if self.visited.insert(c.id()) {
self.commits.push(TimeOrderedCommit(c));
}
}
}
/// This is separate from `LogWalker` because filtering currently (June 2024) works through
/// `SharedCommitFilterFn`.
///
/// `SharedCommitFilterFn` requires access to a `git2::repo::Repository` because, under the hood,
/// it calls into functions that work with a `git2::repo::Repository`. It seems unwise to open a
/// repo both through `gix::discover` and `Repository::open_ext` at the same time, so there is a
/// separate struct that works with `gix::Repository` only.
///
/// A more long-term option is to refactor filtering to work with a `gix::Repository` and to remove
/// `LogWalker` once this is done, but this is a larger effort.
pub struct LogWalkerWithoutFilter<'a> {
walk: Walk<'a>,
limit: usize,
visited: usize,
}
impl<'a> LogWalkerWithoutFilter<'a> {
///
pub fn new(
repo: &'a mut gix::Repository,
limit: usize,
) -> Result<Self> {
// This seems to be an object cache size that yields optimal performance. Theres no specific
// reason this is 2^14, so benchmarking might reveal that theres better values.
repo.object_cache_size_if_unset(2_usize.pow(14));
let commit = repo.head()?.peel_to_commit()?;
let tips = [commit.id];
let platform = repo
.rev_walk(tips)
.sorting(gix::revision::walk::Sorting::ByCommitTime(gix::traverse::commit::simple::CommitTimeOrder::NewestFirst))
.use_commit_graph(false);
let walk = platform.all()?;
Ok(Self {
walk,
limit,
visited: 0,
})
}
///
pub const fn visited(&self) -> usize {
self.visited
}
///
pub fn read(&mut self, out: &mut Vec<CommitId>) -> Result<usize> {
let mut count = 0_usize;
while let Some(Ok(info)) = self.walk.next() {
out.push(info.id.into());
count += 1;
if count == self.limit {
break;
}
}
self.visited += count;
Ok(count)
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::error::Result;
use crate::sync::{
commit, commit_files::get_commit_diff, get_commits_info,
stage_add_file, tests::repo_init_empty,
};
use pretty_assertions::assert_eq;
use std::{fs::File, io::Write, path::Path};
use super::*;
use crate::error::Result;
use crate::sync::commit_filter::{SearchFields, SearchOptions};
use crate::sync::repository::gix_repo;
use crate::sync::tests::write_commit_file;
use crate::sync::{
commit, get_commits_info, stage_add_file,
tests::repo_init_empty,
};
use crate::sync::{
diff_contains_file, filter_commit_by_search, LogFilterSearch,
LogFilterSearchOptions, RepoPath,
};
use pretty_assertions::assert_eq;
use std::{fs::File, io::Write, path::Path};
#[test]
fn test_limit() -> Result<()> {
let file_path = Path::new("foo");
let (_td, repo) = repo_init_empty().unwrap();
let root = repo.path().parent().unwrap();
let repo_path = root.as_os_str().to_str().unwrap();
#[test]
fn test_limit() -> Result<()> {
let file_path = Path::new("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();
File::create(&root.join(file_path))?.write_all(b"a")?;
stage_add_file(repo_path, file_path).unwrap();
commit(repo_path, "commit1").unwrap();
File::create(&root.join(file_path))?.write_all(b"a")?;
stage_add_file(repo_path, file_path).unwrap();
let oid2 = commit(repo_path, "commit2").unwrap();
File::create(root.join(file_path))?.write_all(b"a")?;
stage_add_file(repo_path, file_path).unwrap();
commit(repo_path, "commit1").unwrap();
File::create(root.join(file_path))?.write_all(b"a")?;
stage_add_file(repo_path, file_path).unwrap();
let oid2 = commit(repo_path, "commit2").unwrap();
let mut items = Vec::new();
let mut walk = LogWalker::new(&repo, 1)?;
walk.read(&mut items).unwrap();
let mut items = Vec::new();
let mut walk = LogWalker::new(&repo, 1)?;
walk.read(&mut items).unwrap();
assert_eq!(items.len(), 1);
assert_eq!(items[0], oid2.into());
assert_eq!(items.len(), 1);
assert_eq!(items[0], oid2);
Ok(())
}
Ok(())
}
#[test]
fn test_logwalker() -> Result<()> {
let file_path = Path::new("foo");
let (_td, repo) = repo_init_empty().unwrap();
let root = repo.path().parent().unwrap();
let repo_path = root.as_os_str().to_str().unwrap();
#[test]
fn test_logwalker() -> Result<()> {
let file_path = Path::new("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();
File::create(&root.join(file_path))?.write_all(b"a")?;
stage_add_file(repo_path, file_path).unwrap();
commit(repo_path, "commit1").unwrap();
File::create(&root.join(file_path))?.write_all(b"a")?;
stage_add_file(repo_path, file_path).unwrap();
let oid2 = commit(repo_path, "commit2").unwrap();
File::create(root.join(file_path))?.write_all(b"a")?;
stage_add_file(repo_path, file_path).unwrap();
commit(repo_path, "commit1").unwrap();
File::create(root.join(file_path))?.write_all(b"a")?;
stage_add_file(repo_path, file_path).unwrap();
let oid2 = commit(repo_path, "commit2").unwrap();
let mut items = Vec::new();
let mut walk = LogWalker::new(&repo, 100)?;
walk.read(&mut items).unwrap();
let mut items = Vec::new();
let mut walk = LogWalker::new(&repo, 100)?;
walk.read(&mut items).unwrap();
let info = get_commits_info(repo_path, &items, 50).unwrap();
dbg!(&info);
let info = get_commits_info(repo_path, &items, 50).unwrap();
dbg!(&info);
assert_eq!(items.len(), 2);
assert_eq!(items[0], oid2.into());
assert_eq!(items.len(), 2);
assert_eq!(items[0], oid2);
let mut items = Vec::new();
walk.read(&mut items).unwrap();
let mut items = Vec::new();
walk.read(&mut items).unwrap();
assert_eq!(items.len(), 0);
assert_eq!(items.len(), 0);
Ok(())
}
Ok(())
}
#[test]
fn test_logwalker_with_filter() -> Result<()> {
let file_path = Path::new("foo");
let second_file_path = Path::new("baz");
let (_td, repo) = repo_init_empty().unwrap();
let root = repo.path().parent().unwrap();
let repo_path = root.as_os_str().to_str().unwrap();
#[test]
fn test_logwalker_without_filter() -> Result<()> {
let file_path = Path::new("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();
File::create(&root.join(file_path))?.write_all(b"a")?;
stage_add_file(repo_path, file_path).unwrap();
File::create(root.join(file_path))?.write_all(b"a")?;
stage_add_file(repo_path, file_path).unwrap();
commit(repo_path, "commit1").unwrap();
File::create(root.join(file_path))?.write_all(b"a")?;
stage_add_file(repo_path, file_path).unwrap();
let oid2 = commit(repo_path, "commit2").unwrap();
let _first_commit_id = commit(repo_path, "commit1").unwrap();
let mut repo: gix::Repository = gix_repo(repo_path)?;
let mut walk = LogWalkerWithoutFilter::new(&mut repo, 100)?;
let mut items = Vec::new();
assert!(matches!(walk.read(&mut items), Ok(2)));
File::create(&root.join(second_file_path))?
.write_all(b"a")?;
stage_add_file(repo_path, second_file_path).unwrap();
let info = get_commits_info(repo_path, &items, 50).unwrap();
dbg!(&info);
let second_commit_id = commit(repo_path, "commit2").unwrap();
assert_eq!(items.len(), 2);
assert_eq!(items[0], oid2);
File::create(&root.join(file_path))?.write_all(b"b")?;
stage_add_file(repo_path, file_path).unwrap();
let mut items = Vec::new();
assert!(matches!(walk.read(&mut items), Ok(0)));
let _third_commit_id = commit(repo_path, "commit3").unwrap();
assert_eq!(items.len(), 0);
let diff_contains_baz = |repo: &Repository,
commit_id: &CommitId|
-> Result<bool> {
let diff = get_commit_diff(
&repo,
*commit_id,
Some("baz".into()),
)?;
Ok(())
}
let contains_file = diff.deltas().len() > 0;
#[test]
fn test_logwalker_with_filter() -> Result<()> {
let file_path = Path::new("foo");
let second_file_path = Path::new("baz");
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();
Ok(contains_file)
};
File::create(root.join(file_path))?.write_all(b"a")?;
stage_add_file(&repo_path, file_path).unwrap();
let mut items = Vec::new();
let mut walker = LogWalker::new(&repo, 100)?
.filter(Some(Arc::new(Box::new(diff_contains_baz))));
walker.read(&mut items).unwrap();
let _first_commit_id = commit(&repo_path, "commit1").unwrap();
assert_eq!(items.len(), 1);
assert_eq!(items[0], second_commit_id.into());
File::create(root.join(second_file_path))?.write_all(b"a")?;
stage_add_file(&repo_path, second_file_path).unwrap();
let mut items = Vec::new();
walker.read(&mut items).unwrap();
let second_commit_id = commit(&repo_path, "commit2").unwrap();
assert_eq!(items.len(), 0);
File::create(root.join(file_path))?.write_all(b"b")?;
stage_add_file(&repo_path, file_path).unwrap();
let diff_contains_bar = |repo: &Repository,
commit_id: &CommitId|
-> Result<bool> {
let diff = get_commit_diff(
&repo,
*commit_id,
Some("bar".into()),
)?;
let _third_commit_id = commit(&repo_path, "commit3").unwrap();
let contains_file = diff.deltas().len() > 0;
let diff_contains_baz = diff_contains_file("baz".into());
Ok(contains_file)
};
let mut items = Vec::new();
let mut walker = LogWalker::new(&repo, 100)?
.filter(Some(diff_contains_baz));
walker.read(&mut items).unwrap();
let mut items = Vec::new();
let mut walker = LogWalker::new(&repo, 100)?
.filter(Some(Arc::new(Box::new(diff_contains_bar))));
walker.read(&mut items).unwrap();
assert_eq!(items.len(), 1);
assert_eq!(items[0], second_commit_id);
assert_eq!(items.len(), 0);
let mut items = Vec::new();
walker.read(&mut items).unwrap();
Ok(())
}
assert_eq!(items.len(), 0);
let diff_contains_bar = diff_contains_file("bar".into());
let mut items = Vec::new();
let mut walker = LogWalker::new(&repo, 100)?
.filter(Some(diff_contains_bar));
walker.read(&mut items).unwrap();
assert_eq!(items.len(), 0);
Ok(())
}
#[test]
fn test_logwalker_with_filter_search() {
let (_td, repo) = repo_init_empty().unwrap();
write_commit_file(&repo, "foo", "a", "commit1");
let second_commit_id = write_commit_file(
&repo,
"baz",
"a",
"my commit msg (#2)",
);
write_commit_file(&repo, "foo", "b", "commit3");
let log_filter = filter_commit_by_search(
LogFilterSearch::new(LogFilterSearchOptions {
fields: SearchFields::MESSAGE_SUMMARY,
options: SearchOptions::FUZZY_SEARCH,
search_pattern: String::from("my msg"),
}),
);
let mut items = Vec::new();
let mut walker = LogWalker::new(&repo, 100)
.unwrap()
.filter(Some(log_filter));
walker.read(&mut items).unwrap();
assert_eq!(items.len(), 1);
assert_eq!(items[0], second_commit_id);
let log_filter = filter_commit_by_search(
LogFilterSearch::new(LogFilterSearchOptions {
fields: SearchFields::FILENAMES,
options: SearchOptions::FUZZY_SEARCH,
search_pattern: String::from("fo"),
}),
);
let mut items = Vec::new();
let mut walker = LogWalker::new(&repo, 100)
.unwrap()
.filter(Some(log_filter));
walker.read(&mut items).unwrap();
assert_eq!(items.len(), 2);
}
}

View file

@ -1,141 +1,188 @@
use crate::{
error::{Error, Result},
sync::{
branch::merge_commit::commit_merge_with_head, reset_stage,
reset_workdir, utils, CommitId,
},
error::{Error, Result},
sync::{
branch::merge_commit::commit_merge_with_head,
rebase::{
abort_rebase, continue_rebase, get_rebase_progress,
},
repository::repo,
reset_stage, reset_workdir, CommitId,
},
};
use git2::{BranchType, Commit, MergeOptions, Repository};
use scopetime::scope_time;
use super::{
rebase::{RebaseProgress, RebaseState},
RepoPath,
};
///
pub fn mergehead_ids(repo_path: &str) -> Result<Vec<CommitId>> {
scope_time!("mergehead_ids");
pub fn mergehead_ids(repo_path: &RepoPath) -> Result<Vec<CommitId>> {
scope_time!("mergehead_ids");
let mut repo = utils::repo(repo_path)?;
let mut repo = repo(repo_path)?;
let mut ids: Vec<CommitId> = Vec::new();
repo.mergehead_foreach(|id| {
ids.push(CommitId::from(*id));
true
})?;
let mut ids: Vec<CommitId> = Vec::new();
repo.mergehead_foreach(|id| {
ids.push(CommitId::from(*id));
true
})?;
Ok(ids)
Ok(ids)
}
/// does these steps:
/// * reset all staged changes,
/// * revert all changes in workdir
/// * cleanup repo merge state
pub fn abort_merge(repo_path: &str) -> Result<()> {
scope_time!("cleanup_state");
pub fn abort_pending_state(repo_path: &RepoPath) -> Result<()> {
scope_time!("abort_pending_state");
let repo = utils::repo(repo_path)?;
let repo = repo(repo_path)?;
reset_stage(repo_path, "*")?;
reset_workdir(repo_path, "*")?;
reset_stage(repo_path, "*")?;
reset_workdir(repo_path, "*")?;
repo.cleanup_state()?;
repo.cleanup_state()?;
Ok(())
Ok(())
}
///
pub fn merge_branch(repo_path: &str, branch: &str) -> Result<()> {
scope_time!("merge_branch");
pub fn merge_branch(
repo_path: &RepoPath,
branch: &str,
branch_type: BranchType,
) -> Result<()> {
scope_time!("merge_branch");
let repo = utils::repo(repo_path)?;
let repo = repo(repo_path)?;
merge_branch_repo(&repo, branch)?;
merge_branch_repo(&repo, branch, branch_type)?;
Ok(())
Ok(())
}
///
pub fn rebase_progress(
repo_path: &RepoPath,
) -> Result<RebaseProgress> {
scope_time!("rebase_progress");
let repo = repo(repo_path)?;
get_rebase_progress(&repo)
}
///
pub fn continue_pending_rebase(
repo_path: &RepoPath,
) -> Result<RebaseState> {
scope_time!("continue_pending_rebase");
let repo = repo(repo_path)?;
continue_rebase(&repo)
}
///
pub fn abort_pending_rebase(repo_path: &RepoPath) -> Result<()> {
scope_time!("abort_pending_rebase");
let repo = repo(repo_path)?;
abort_rebase(&repo)
}
///
pub fn merge_branch_repo(
repo: &Repository,
branch: &str,
repo: &Repository,
branch: &str,
branch_type: BranchType,
) -> Result<()> {
let branch = repo.find_branch(branch, BranchType::Local)?;
let branch = repo.find_branch(branch, branch_type)?;
let annotated =
repo.reference_to_annotated_commit(&branch.into_reference())?;
let annotated =
repo.reference_to_annotated_commit(&branch.into_reference())?;
let (analysis, _) = repo.merge_analysis(&[&annotated])?;
let (analysis, _) = repo.merge_analysis(&[&annotated])?;
//TODO: support merge on unborn
if analysis.is_unborn() {
return Err(Error::Generic("head is unborn".into()));
}
//TODO: support merge on unborn
if analysis.is_unborn() {
return Err(Error::Generic("head is unborn".into()));
}
let mut opt = MergeOptions::default();
let mut opt = MergeOptions::default();
repo.merge(&[&annotated], Some(&mut opt), None)?;
repo.merge(&[&annotated], Some(&mut opt), None)?;
Ok(())
Ok(())
}
///
pub fn merge_msg(repo_path: &str) -> Result<String> {
scope_time!("merge_msg");
pub fn merge_msg(repo_path: &RepoPath) -> Result<String> {
scope_time!("merge_msg");
let repo = utils::repo(repo_path)?;
let content = repo.message()?;
let repo = repo(repo_path)?;
let content = repo.message()?;
Ok(content)
Ok(content)
}
///
pub fn merge_commit(
repo_path: &str,
msg: &str,
ids: &[CommitId],
repo_path: &RepoPath,
msg: &str,
ids: &[CommitId],
) -> Result<CommitId> {
scope_time!("merge_commit");
scope_time!("merge_commit");
let repo = utils::repo(repo_path)?;
let repo = repo(repo_path)?;
let mut commits: Vec<Commit> = Vec::new();
let mut commits: Vec<Commit> = Vec::new();
for id in ids {
commits.push(repo.find_commit((*id).into())?);
}
for id in ids {
commits.push(repo.find_commit((*id).into())?);
}
let id = commit_merge_with_head(&repo, &commits, msg)?;
let id = commit_merge_with_head(&repo, &commits, msg)?;
Ok(id)
Ok(id)
}
#[cfg(test)]
mod tests {
use super::*;
use crate::sync::{
create_branch,
tests::{repo_init, write_commit_file},
};
use pretty_assertions::assert_eq;
use super::*;
use crate::sync::{
create_branch,
tests::{repo_init, write_commit_file},
RepoPath,
};
use pretty_assertions::assert_eq;
#[test]
fn test_smoke() {
let (_td, repo) = repo_init().unwrap();
let root = repo.path().parent().unwrap();
let repo_path = root.as_os_str().to_str().unwrap();
#[test]
fn test_smoke() {
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 c1 =
write_commit_file(&repo, "test.txt", "test", "commit1");
let c1 =
write_commit_file(&repo, "test.txt", "test", "commit1");
create_branch(repo_path, "foo").unwrap();
create_branch(repo_path, "foo").unwrap();
write_commit_file(&repo, "test.txt", "test2", "commit2");
write_commit_file(&repo, "test.txt", "test2", "commit2");
merge_branch(repo_path, "master").unwrap();
merge_branch(repo_path, "master", BranchType::Local).unwrap();
let msg = merge_msg(repo_path).unwrap();
let msg = merge_msg(repo_path).unwrap();
assert_eq!(&msg[0..12], "Merge branch");
assert_eq!(&msg[0..12], "Merge branch");
let mergeheads = mergehead_ids(repo_path).unwrap();
let mergeheads = mergehead_ids(repo_path).unwrap();
assert_eq!(mergeheads[0], c1);
}
assert_eq!(mergeheads[0], c1);
}
}

View file

@ -5,9 +5,11 @@
pub mod blame;
pub mod branch;
mod commit;
pub mod commit;
mod commit_details;
mod commit_files;
pub mod commit_files;
mod commit_filter;
mod commit_revert;
mod commits_info;
mod config;
pub mod cred;
@ -18,300 +20,361 @@ mod ignore;
mod logwalker;
mod merge;
mod patches;
mod rebase;
pub mod remotes;
mod repository;
mod reset;
mod reword;
pub mod sign;
mod staging;
mod stash;
mod state;
pub mod status;
mod submodules;
mod tags;
mod tree;
pub mod utils;
pub use blame::{blame_file, BlameHunk, FileBlame};
pub use branch::{
branch_compare_upstream, checkout_branch, config_is_pull_rebase,
create_branch, delete_branch, get_branch_remote,
get_branches_info, merge_commit::merge_upstream_commit,
merge_ff::branch_merge_upstream_fastforward,
merge_rebase::merge_upstream_rebase, rename::rename_branch,
BranchCompare, BranchInfo,
branch_compare_upstream, checkout_branch, checkout_commit,
config_is_pull_rebase, create_branch, delete_branch,
get_branch_remote, get_branch_upstream_merge, get_branches_info,
merge_commit::merge_upstream_commit,
merge_ff::branch_merge_upstream_fastforward,
merge_rebase::merge_upstream_rebase, rename::rename_branch,
validate_branch_name, BranchCompare, BranchDetails, BranchInfo,
};
pub use commit::{amend, commit, tag};
pub use commit::{amend, commit, tag_commit};
pub use commit_details::{
get_commit_details, CommitDetails, CommitMessage, CommitSignature,
get_commit_details, CommitDetails, CommitMessage, CommitSignature,
};
pub use commit_files::get_commit_files;
pub use commit_filter::{
diff_contains_file, filter_commit_by_search, LogFilterSearch,
LogFilterSearchOptions, SearchFields, SearchOptions,
SharedCommitFilterFn,
};
pub use commit_revert::{commit_revert, revert_commit, revert_head};
pub use commits_info::{
get_commit_info, get_commits_info, CommitId, CommitInfo,
get_commit_info, get_commits_info, CommitId, CommitInfo,
};
pub use config::{
get_config_string, untracked_files_config,
ShowUntrackedFilesConfig,
get_config_string, untracked_files_config,
ShowUntrackedFilesConfig,
};
pub use diff::get_diff_commit;
pub use git2::BranchType;
pub use hooks::{
hooks_commit_msg, hooks_post_commit, hooks_pre_commit, HookResult,
hooks_commit_msg, hooks_post_commit, hooks_pre_commit,
hooks_pre_push, hooks_prepare_commit_msg, HookResult,
PrePushTarget, PrepareCommitMsgSource,
};
pub use hunks::{reset_hunk, stage_hunk, unstage_hunk};
pub use ignore::add_to_ignore;
pub use logwalker::{LogWalker, LogWalkerFilter};
pub use logwalker::{LogWalker, LogWalkerWithoutFilter};
pub use merge::{
abort_merge, merge_branch, merge_commit, merge_msg, mergehead_ids,
abort_pending_rebase, abort_pending_state,
continue_pending_rebase, merge_branch, merge_commit, merge_msg,
mergehead_ids, rebase_progress,
};
pub use rebase::rebase_branch;
pub use remotes::{
get_default_remote, get_remotes, push::AsyncProgress,
tags::PushTagsProgress,
add_remote, delete_remote, get_default_remote,
get_default_remote_for_fetch, get_default_remote_for_push,
get_remote_url, get_remotes, push::AsyncProgress, rename_remote,
tags::PushTagsProgress, update_remote_url, validate_remote_name,
};
pub use reset::{reset_stage, reset_workdir};
pub(crate) use repository::{gix_repo, repo};
pub use repository::{RepoPath, RepoPathRef};
pub use reset::{reset_repo, reset_stage, reset_workdir};
pub use reword::reword;
pub use staging::{discard_lines, stage_lines};
pub use stash::{
get_stashes, stash_apply, stash_drop, stash_pop, stash_save,
get_stashes, stash_apply, stash_drop, stash_pop, stash_save,
};
pub use state::{repo_state, RepoState};
pub use status::is_workdir_clean;
pub use submodules::{
get_submodules, submodule_parent_info, update_submodule,
SubmoduleInfo, SubmoduleParentInfo, SubmoduleStatus,
};
pub use tags::{
delete_tag, get_tags, get_tags_with_metadata, CommitTags,
TagWithMetadata, Tags,
delete_tag, get_tags, get_tags_with_metadata, CommitTags, Tag,
TagWithMetadata, Tags,
};
pub use tree::{tree_file_content, tree_files, TreeFile};
pub use utils::{
get_head, get_head_tuple, is_bare_repo, is_repo, repo_dir,
stage_add_all, stage_add_file, stage_addremoved, Head,
get_head, get_head_tuple, repo_dir, repo_open_error,
stage_add_all, stage_add_file, stage_addremoved, Head,
};
pub use git2::ResetType;
/// test utils
#[cfg(test)]
mod tests {
use super::{
commit, stage_add_file,
status::{get_status, StatusType},
utils::{get_head_repo, repo, repo_write_file},
CommitId, LogWalker,
};
use crate::error::Result;
use git2::Repository;
use std::{path::Path, process::Command};
use tempfile::TempDir;
pub mod tests {
use super::{
commit,
repository::repo,
stage_add_file,
status::{get_status, StatusType},
utils::{get_head_repo, repo_write_file},
CommitId, LogWalker, RepoPath,
};
use crate::error::Result;
use git2::Repository;
use std::{ffi::OsStr, path::Path, process::Command};
use tempfile::TempDir;
/// Calling `set_search_path` with an empty directory makes sure that there
/// is no git config interfering with our tests (for example user-local
/// `.gitconfig`).
#[allow(unsafe_code)]
fn sandbox_config_files() {
use git2::{opts::set_search_path, ConfigLevel};
use std::sync::Once;
///
pub fn repo_init_empty() -> Result<(TempDir, Repository)> {
init_log();
static INIT: Once = Once::new();
sandbox_config_files();
// Adapted from https://github.com/rust-lang/cargo/pull/9035
INIT.call_once(|| unsafe {
let temp_dir = TempDir::new().unwrap();
let path = temp_dir.path();
let td = TempDir::new()?;
let repo = Repository::init(td.path())?;
{
let mut config = repo.config()?;
config.set_str("user.name", "name")?;
config.set_str("user.email", "email")?;
}
Ok((td, repo))
}
set_search_path(ConfigLevel::System, &path).unwrap();
set_search_path(ConfigLevel::Global, &path).unwrap();
set_search_path(ConfigLevel::XDG, &path).unwrap();
set_search_path(ConfigLevel::ProgramData, &path).unwrap();
});
}
///
pub fn repo_init() -> Result<(TempDir, Repository)> {
repo_init_with_prefix("gitui")
}
/// write, stage and commit a file
pub fn write_commit_file(
repo: &Repository,
file: &str,
content: &str,
commit_name: &str,
) -> CommitId {
repo_write_file(repo, file, content).unwrap();
///
#[inline]
pub fn repo_init_with_prefix(
prefix: impl AsRef<OsStr>,
) -> Result<(TempDir, Repository)> {
init_log();
stage_add_file(
repo.workdir().unwrap().to_str().unwrap(),
Path::new(file),
)
.unwrap();
sandbox_config_files();
commit(repo.workdir().unwrap().to_str().unwrap(), commit_name)
.unwrap()
}
let td = TempDir::with_prefix(prefix)?;
let repo = Repository::init(td.path())?;
{
let mut config = repo.config()?;
config.set_str("user.name", "name")?;
config.set_str("user.email", "email")?;
/// write, stage and commit a file giving the commit a specific timestamp
pub fn write_commit_file_at(
repo: &Repository,
file: &str,
content: &str,
commit_name: &str,
time: git2::Time,
) -> CommitId {
repo_write_file(repo, file, content).unwrap();
let mut index = repo.index()?;
let id = index.write_tree()?;
let path = repo.workdir().unwrap().to_str().unwrap();
let tree = repo.find_tree(id)?;
let sig = repo.signature()?;
repo.commit(
Some("HEAD"),
&sig,
&sig,
"initial",
&tree,
&[],
)?;
}
Ok((td, repo))
}
stage_add_file(path, Path::new(file)).unwrap();
///
pub fn repo_clone(p: &str) -> Result<(TempDir, Repository)> {
sandbox_config_files();
commit_at(path, commit_name, time)
}
let td = TempDir::new()?;
fn commit_at(
repo_path: &str,
msg: &str,
time: git2::Time,
) -> CommitId {
let repo = repo(repo_path).unwrap();
let td_path = td.path().as_os_str().to_str().unwrap();
let signature =
git2::Signature::new("name", "email", &time).unwrap();
let mut index = repo.index().unwrap();
let tree_id = index.write_tree().unwrap();
let tree = repo.find_tree(tree_id).unwrap();
let repo = Repository::clone(p, td_path).unwrap();
let parents = if let Ok(id) = get_head_repo(&repo) {
vec![repo.find_commit(id.into()).unwrap()]
} else {
Vec::new()
};
let mut config = repo.config()?;
config.set_str("user.name", "name")?;
config.set_str("user.email", "email")?;
let parents = parents.iter().collect::<Vec<_>>();
Ok((td, repo))
}
let commit = repo
.commit(
Some("HEAD"),
&signature,
&signature,
msg,
&tree,
parents.as_slice(),
)
.unwrap()
.into();
/// write, stage and commit a file
pub fn write_commit_file(
repo: &Repository,
file: &str,
content: &str,
commit_name: &str,
) -> CommitId {
repo_write_file(repo, file, content).unwrap();
commit
}
stage_add_file(
&repo.workdir().unwrap().to_str().unwrap().into(),
Path::new(file),
)
.unwrap();
///
pub fn repo_init_empty() -> Result<(TempDir, Repository)> {
sandbox_config_files();
commit(
&repo.workdir().unwrap().to_str().unwrap().into(),
commit_name,
)
.unwrap()
}
let td = TempDir::new()?;
let repo = Repository::init(td.path())?;
{
let mut config = repo.config()?;
config.set_str("user.name", "name")?;
config.set_str("user.email", "email")?;
}
Ok((td, repo))
}
/// write, stage and commit a file giving the commit a specific timestamp
pub fn write_commit_file_at(
repo: &Repository,
file: &str,
content: &str,
commit_name: &str,
time: git2::Time,
) -> CommitId {
repo_write_file(repo, file, content).unwrap();
///
pub fn repo_init() -> Result<(TempDir, Repository)> {
sandbox_config_files();
let path: &RepoPath =
&repo.workdir().unwrap().to_str().unwrap().into();
let td = TempDir::new()?;
let repo = Repository::init(td.path())?;
{
let mut config = repo.config()?;
config.set_str("user.name", "name")?;
config.set_str("user.email", "email")?;
stage_add_file(path, Path::new(file)).unwrap();
let mut index = repo.index()?;
let id = index.write_tree()?;
commit_at(path, commit_name, time)
}
let tree = repo.find_tree(id)?;
let sig = repo.signature()?;
repo.commit(
Some("HEAD"),
&sig,
&sig,
"initial",
&tree,
&[],
)?;
}
Ok((td, repo))
}
/// helper returning amount of files with changes in the (wd,stage)
pub fn get_statuses(repo_path: &RepoPath) -> (usize, usize) {
(
get_status(repo_path, StatusType::WorkingDir, None)
.unwrap()
.len(),
get_status(repo_path, StatusType::Stage, None)
.unwrap()
.len(),
)
}
///
pub fn repo_clone(p: &str) -> Result<(TempDir, Repository)> {
sandbox_config_files();
///
pub fn debug_cmd_print(path: &RepoPath, cmd: &str) {
let cmd = debug_cmd(path, cmd);
eprintln!("\n----\n{cmd}");
}
let td = TempDir::new()?;
/// helper to fetch commit details using log walker
pub fn get_commit_ids(
r: &Repository,
max_count: usize,
) -> Vec<CommitId> {
let mut commit_ids = Vec::<CommitId>::new();
LogWalker::new(r, max_count)
.unwrap()
.read(&mut commit_ids)
.unwrap();
let td_path = td.path().as_os_str().to_str().unwrap();
commit_ids
}
let repo = Repository::clone(p, td_path).unwrap();
/// Same as `repo_init`, but the repo is a bare repo (--bare)
pub fn repo_init_bare() -> Result<(TempDir, Repository)> {
init_log();
let mut config = repo.config()?;
config.set_str("user.name", "name")?;
config.set_str("user.email", "email")?;
let tmp_repo_dir = TempDir::new()?;
let bare_repo = Repository::init_bare(tmp_repo_dir.path())?;
Ok((tmp_repo_dir, bare_repo))
}
Ok((td, repo))
}
/// Calling `set_search_path` with an empty directory makes sure that there
/// is no git config interfering with our tests (for example user-local
/// `.gitconfig`).
#[allow(unsafe_code)]
fn sandbox_config_files() {
use git2::{opts::set_search_path, ConfigLevel};
use std::sync::Once;
/// Same as repo_init, but the repo is a bare repo (--bare)
pub fn repo_init_bare() -> Result<(TempDir, Repository)> {
let tmp_repo_dir = TempDir::new()?;
let bare_repo = Repository::init_bare(tmp_repo_dir.path())?;
Ok((tmp_repo_dir, bare_repo))
}
static INIT: Once = Once::new();
/// helper returning amount of files with changes in the (wd,stage)
pub fn get_statuses(repo_path: &str) -> (usize, usize) {
(
get_status(repo_path, StatusType::WorkingDir)
.unwrap()
.len(),
get_status(repo_path, StatusType::Stage).unwrap().len(),
)
}
// Adapted from https://github.com/rust-lang/cargo/pull/9035
INIT.call_once(|| unsafe {
let temp_dir = TempDir::new().unwrap();
let path = temp_dir.path();
///
pub fn debug_cmd_print(path: &str, cmd: &str) {
let cmd = debug_cmd(path, cmd);
eprintln!("\n----\n{}", cmd);
}
set_search_path(ConfigLevel::System, path).unwrap();
set_search_path(ConfigLevel::Global, path).unwrap();
set_search_path(ConfigLevel::XDG, path).unwrap();
set_search_path(ConfigLevel::ProgramData, path).unwrap();
});
}
/// helper to fetch commmit details using log walker
pub fn get_commit_ids(
r: &Repository,
max_count: usize,
) -> Vec<CommitId> {
let mut commit_ids = Vec::<CommitId>::new();
LogWalker::new(r, max_count)
.unwrap()
.read(&mut commit_ids)
.unwrap();
fn commit_at(
repo_path: &RepoPath,
msg: &str,
time: git2::Time,
) -> CommitId {
let repo = repo(repo_path).unwrap();
commit_ids
}
let signature =
git2::Signature::new("name", "email", &time).unwrap();
let mut index = repo.index().unwrap();
let tree_id = index.write_tree().unwrap();
let tree = repo.find_tree(tree_id).unwrap();
fn debug_cmd(path: &str, cmd: &str) -> String {
let output = if cfg!(target_os = "windows") {
Command::new("cmd")
.args(&["/C", cmd])
.current_dir(path)
.output()
.unwrap()
} else {
Command::new("sh")
.arg("-c")
.arg(cmd)
.current_dir(path)
.output()
.unwrap()
};
let parents = if let Ok(id) = get_head_repo(&repo) {
vec![repo.find_commit(id.into()).unwrap()]
} else {
Vec::new()
};
let stdout = String::from_utf8_lossy(&output.stdout);
let stderr = String::from_utf8_lossy(&output.stderr);
format!(
"{}{}",
if stdout.is_empty() {
String::new()
} else {
format!("out:\n{}", stdout)
},
if stderr.is_empty() {
String::new()
} else {
format!("err:\n{}", stderr)
}
)
}
let parents = parents.iter().collect::<Vec<_>>();
let commit = repo
.commit(
Some("HEAD"),
&signature,
&signature,
msg,
&tree,
parents.as_slice(),
)
.unwrap()
.into();
commit
}
// init log
fn init_log() {
let _ = env_logger::builder()
.is_test(true)
.filter_level(log::LevelFilter::Trace)
.try_init();
}
fn debug_cmd(path: &RepoPath, cmd: &str) -> String {
let output = if cfg!(target_os = "windows") {
Command::new("cmd")
.args(["/C", cmd])
.current_dir(path.gitpath())
.output()
.unwrap()
} else {
Command::new("sh")
.arg("-c")
.arg(cmd)
.current_dir(path.gitpath())
.output()
.unwrap()
};
let stdout = String::from_utf8_lossy(&output.stdout);
let stderr = String::from_utf8_lossy(&output.stderr);
format!(
"{}{}",
if stdout.is_empty() {
String::new()
} else {
format!("out:\n{stdout}")
},
if stderr.is_empty() {
String::new()
} else {
format!("err:\n{stderr}")
}
)
}
}

View file

@ -1,73 +1,78 @@
use super::diff::{get_diff_raw, HunkHeader};
use super::diff::{get_diff_raw, DiffOptions, HunkHeader};
use crate::error::{Error, Result};
use git2::{Diff, DiffLine, Patch, Repository};
#[allow(clippy::redundant_pub_crate)]
pub(crate) struct HunkLines<'a> {
pub hunk: HunkHeader,
pub lines: Vec<DiffLine<'a>>,
pub struct HunkLines<'a> {
pub hunk: HunkHeader,
pub lines: Vec<DiffLine<'a>>,
}
#[allow(clippy::redundant_pub_crate)]
pub(crate) fn get_file_diff_patch_and_hunklines<'a>(
repo: &'a Repository,
file: &str,
is_staged: bool,
reverse: bool,
) -> Result<(Patch<'a>, Vec<HunkLines<'a>>)> {
let diff = get_diff_raw(repo, file, is_staged, reverse, Some(1))?;
let patches = get_patches(&diff)?;
if patches.len() > 1 {
return Err(Error::Generic(String::from("patch error")));
}
pub fn get_file_diff_patch<'a>(
repo: &'a Repository,
file: &str,
is_staged: bool,
reverse: bool,
) -> Result<Patch<'a>> {
let diff = get_diff_raw(
repo,
file,
is_staged,
reverse,
Some(DiffOptions {
context: 1,
..DiffOptions::default()
}),
)?;
let patches = get_patches(&diff)?;
if patches.len() > 1 {
return Err(Error::Generic(String::from("patch error")));
}
let patch = patches.into_iter().next().ok_or_else(|| {
Error::Generic(String::from("no patch found"))
})?;
let patch = patches.into_iter().next().ok_or_else(|| {
Error::Generic(String::from("no patch found"))
})?;
let lines = patch_get_hunklines(&patch)?;
Ok((patch, lines))
Ok(patch)
}
//
fn patch_get_hunklines<'a>(
patch: &Patch<'a>,
pub fn patch_get_hunklines<'a>(
patch: &'a Patch<'a>,
) -> Result<Vec<HunkLines<'a>>> {
let count_hunks = patch.num_hunks();
let mut res = Vec::with_capacity(count_hunks);
for hunk_idx in 0..count_hunks {
let (hunk, _) = patch.hunk(hunk_idx)?;
let count_hunks = patch.num_hunks();
let mut res = Vec::with_capacity(count_hunks);
for hunk_idx in 0..count_hunks {
let (hunk, _) = patch.hunk(hunk_idx)?;
let count_lines = patch.num_lines_in_hunk(hunk_idx)?;
let count_lines = patch.num_lines_in_hunk(hunk_idx)?;
let mut hunk = HunkLines {
hunk: HunkHeader::from(hunk),
lines: Vec::with_capacity(count_lines),
};
let mut hunk = HunkLines {
hunk: HunkHeader::from(hunk),
lines: Vec::with_capacity(count_lines),
};
for line_idx in 0..count_lines {
let line = patch.line_in_hunk(hunk_idx, line_idx)?;
hunk.lines.push(line);
}
for line_idx in 0..count_lines {
let line = patch.line_in_hunk(hunk_idx, line_idx)?;
hunk.lines.push(line);
}
res.push(hunk);
}
res.push(hunk);
}
Ok(res)
Ok(res)
}
//
fn get_patches<'a>(diff: &Diff<'a>) -> Result<Vec<Patch<'a>>> {
let count = diff.deltas().len();
let count = diff.deltas().len();
let mut res = Vec::with_capacity(count);
for idx in 0..count {
let p = Patch::from_diff(diff, idx)?;
if let Some(p) = p {
res.push(p);
}
}
let mut res = Vec::with_capacity(count);
for idx in 0..count {
let p = Patch::from_diff(diff, idx)?;
if let Some(p) = p {
res.push(p);
}
}
Ok(res)
Ok(res)
}

343
asyncgit/src/sync/rebase.rs Normal file
View file

@ -0,0 +1,343 @@
use git2::{BranchType, Repository};
use scopetime::scope_time;
use crate::{
error::{Error, Result},
sync::repository::repo,
};
use super::{CommitId, RepoPath};
/// rebase current HEAD on `branch`
pub fn rebase_branch(
repo_path: &RepoPath,
branch: &str,
branch_type: BranchType,
) -> Result<RebaseState> {
scope_time!("rebase_branch");
let repo = repo(repo_path)?;
rebase_branch_repo(&repo, branch, branch_type)
}
fn rebase_branch_repo(
repo: &Repository,
branch_name: &str,
branch_type: BranchType,
) -> Result<RebaseState> {
let branch = repo.find_branch(branch_name, branch_type)?;
let annotated =
repo.reference_to_annotated_commit(&branch.into_reference())?;
rebase(repo, &annotated)
}
/// rebase attempt which aborts and undo's rebase if any conflict appears
pub fn conflict_free_rebase(
repo: &git2::Repository,
commit: &git2::AnnotatedCommit,
) -> Result<CommitId> {
let mut rebase = repo.rebase(None, Some(commit), None, None)?;
let signature =
crate::sync::commit::signature_allow_undefined_name(repo)?;
let mut last_commit = None;
while let Some(op) = rebase.next() {
let _op = op?;
if repo.index()?.has_conflicts() {
rebase.abort()?;
return Err(Error::RebaseConflict);
}
let c = rebase.commit(None, &signature, None)?;
last_commit = Some(CommitId::from(c));
}
if repo.index()?.has_conflicts() {
rebase.abort()?;
return Err(Error::RebaseConflict);
}
rebase.finish(Some(&signature))?;
last_commit.ok_or_else(|| {
Error::Generic(String::from("no commit rebased"))
})
}
///
#[derive(PartialEq, Eq, Debug)]
pub enum RebaseState {
///
Finished,
///
Conflicted,
}
/// rebase
pub fn rebase(
repo: &git2::Repository,
commit: &git2::AnnotatedCommit,
) -> Result<RebaseState> {
let mut rebase = repo.rebase(None, Some(commit), None, None)?;
let signature =
crate::sync::commit::signature_allow_undefined_name(repo)?;
while let Some(op) = rebase.next() {
let _op = op?;
// dbg!(op.id());
if repo.index()?.has_conflicts() {
return Ok(RebaseState::Conflicted);
}
rebase.commit(None, &signature, None)?;
}
if repo.index()?.has_conflicts() {
return Ok(RebaseState::Conflicted);
}
rebase.finish(Some(&signature))?;
Ok(RebaseState::Finished)
}
/// continue pending rebase
pub fn continue_rebase(
repo: &git2::Repository,
) -> Result<RebaseState> {
let mut rebase = repo.open_rebase(None)?;
let signature =
crate::sync::commit::signature_allow_undefined_name(repo)?;
if repo.index()?.has_conflicts() {
return Ok(RebaseState::Conflicted);
}
// try commit current rebase step
if !repo.index()?.is_empty() {
rebase.commit(None, &signature, None)?;
}
while let Some(op) = rebase.next() {
let _op = op?;
// dbg!(op.id());
if repo.index()?.has_conflicts() {
return Ok(RebaseState::Conflicted);
}
rebase.commit(None, &signature, None)?;
}
if repo.index()?.has_conflicts() {
return Ok(RebaseState::Conflicted);
}
rebase.finish(Some(&signature))?;
Ok(RebaseState::Finished)
}
///
#[derive(PartialEq, Eq, Debug)]
pub struct RebaseProgress {
///
pub steps: usize,
///
pub current: usize,
///
pub current_commit: Option<CommitId>,
}
///
pub fn get_rebase_progress(
repo: &git2::Repository,
) -> Result<RebaseProgress> {
let mut rebase = repo.open_rebase(None)?;
let current_commit: Option<CommitId> = rebase
.operation_current()
.and_then(|idx| rebase.nth(idx))
.map(|op| op.id().into());
let progress = RebaseProgress {
steps: rebase.len(),
current: rebase.operation_current().unwrap_or_default(),
current_commit,
};
Ok(progress)
}
///
pub fn abort_rebase(repo: &git2::Repository) -> Result<()> {
let mut rebase = repo.open_rebase(None)?;
rebase.abort()?;
Ok(())
}
#[cfg(test)]
mod test_conflict_free_rebase {
use crate::sync::{
checkout_branch, create_branch,
rebase::{rebase_branch, RebaseState},
repo_state,
repository::repo,
tests::{repo_init, write_commit_file},
CommitId, RepoPath, RepoState,
};
use git2::{BranchType, Repository};
use super::conflict_free_rebase;
fn parent_ids(repo: &Repository, c: CommitId) -> Vec<CommitId> {
let foo = repo
.find_commit(c.into())
.unwrap()
.parent_ids()
.map(CommitId::from)
.collect();
foo
}
///
fn test_rebase_branch_repo(
repo_path: &RepoPath,
branch_name: &str,
) -> CommitId {
let repo = repo(repo_path).unwrap();
let branch =
repo.find_branch(branch_name, BranchType::Local).unwrap();
let annotated = repo
.reference_to_annotated_commit(&branch.into_reference())
.unwrap();
conflict_free_rebase(&repo, &annotated).unwrap()
}
#[test]
fn test_smoke() {
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 c1 =
write_commit_file(&repo, "test1.txt", "test", "commit1");
create_branch(repo_path, "foo").unwrap();
let c2 =
write_commit_file(&repo, "test2.txt", "test", "commit2");
assert_eq!(parent_ids(&repo, c2), vec![c1]);
checkout_branch(repo_path, "master").unwrap();
let c3 =
write_commit_file(&repo, "test3.txt", "test", "commit3");
checkout_branch(repo_path, "foo").unwrap();
let r = test_rebase_branch_repo(repo_path, "master");
assert_eq!(parent_ids(&repo, r), vec![c3]);
}
#[test]
fn test_conflict() {
let (_td, repo) = repo_init().unwrap();
let root = repo.path().parent().unwrap();
let repo_path: &RepoPath =
&root.as_os_str().to_str().unwrap().into();
write_commit_file(&repo, "test.txt", "test1", "commit1");
create_branch(repo_path, "foo").unwrap();
write_commit_file(&repo, "test.txt", "test2", "commit2");
checkout_branch(repo_path, "master").unwrap();
write_commit_file(&repo, "test.txt", "test3", "commit3");
checkout_branch(repo_path, "foo").unwrap();
let res =
rebase_branch(repo_path, "master", BranchType::Local);
assert!(matches!(res.unwrap(), RebaseState::Conflicted));
assert_eq!(repo_state(repo_path).unwrap(), RepoState::Rebase);
}
}
#[cfg(test)]
mod test_rebase {
use crate::sync::{
checkout_branch, create_branch,
rebase::{
abort_rebase, get_rebase_progress, RebaseProgress,
RebaseState,
},
rebase_branch, repo_state,
tests::{repo_init, write_commit_file},
RepoPath, RepoState,
};
use git2::BranchType;
#[test]
fn test_conflicted_abort() {
let (_td, repo) = repo_init().unwrap();
let root = repo.path().parent().unwrap();
let repo_path: &RepoPath =
&root.as_os_str().to_str().unwrap().into();
write_commit_file(&repo, "test.txt", "test1", "commit1");
create_branch(repo_path, "foo").unwrap();
let c =
write_commit_file(&repo, "test.txt", "test2", "commit2");
checkout_branch(repo_path, "master").unwrap();
write_commit_file(&repo, "test.txt", "test3", "commit3");
checkout_branch(repo_path, "foo").unwrap();
assert!(get_rebase_progress(&repo).is_err());
// rebase
let r = rebase_branch(repo_path, "master", BranchType::Local)
.unwrap();
assert_eq!(r, RebaseState::Conflicted);
assert_eq!(repo_state(repo_path).unwrap(), RepoState::Rebase);
assert_eq!(
get_rebase_progress(&repo).unwrap(),
RebaseProgress {
current: 0,
steps: 1,
current_commit: Some(c)
}
);
// abort
abort_rebase(&repo).unwrap();
assert_eq!(repo_state(repo_path).unwrap(), RepoState::Clean);
}
}

View file

@ -1,223 +1,221 @@
#![allow(dead_code)]
use super::push::ProgressNotification;
use crate::{error::Result, sync::cred::BasicAuthCredential};
use crossbeam_channel::Sender;
use git2::{Cred, Error as GitError, RemoteCallbacks};
use std::sync::{
atomic::{AtomicBool, Ordering},
Arc, Mutex,
atomic::{AtomicBool, Ordering},
Arc, Mutex,
};
///
#[derive(Default, Clone)]
pub struct CallbackStats {
pub push_rejected_msg: Option<(String, String)>,
pub push_rejected_msg: Option<(String, String)>,
}
///
#[derive(Clone)]
pub struct Callbacks {
sender: Option<Sender<ProgressNotification>>,
basic_credential: Option<BasicAuthCredential>,
stats: Arc<Mutex<CallbackStats>>,
first_call_to_credentials: Arc<AtomicBool>,
sender: Option<Sender<ProgressNotification>>,
basic_credential: Option<BasicAuthCredential>,
stats: Arc<Mutex<CallbackStats>>,
first_call_to_credentials: Arc<AtomicBool>,
}
impl Callbacks {
///
pub fn new(
sender: Option<Sender<ProgressNotification>>,
basic_credential: Option<BasicAuthCredential>,
) -> Self {
let stats = Arc::new(Mutex::new(CallbackStats::default()));
///
pub fn new(
sender: Option<Sender<ProgressNotification>>,
basic_credential: Option<BasicAuthCredential>,
) -> Self {
let stats = Arc::new(Mutex::new(CallbackStats::default()));
Self {
sender,
basic_credential,
stats,
first_call_to_credentials: Arc::new(AtomicBool::new(
true,
)),
}
}
Self {
sender,
basic_credential,
stats,
first_call_to_credentials: Arc::new(AtomicBool::new(
true,
)),
}
}
///
pub fn get_stats(&self) -> Result<CallbackStats> {
let stats = self.stats.lock()?;
Ok(stats.clone())
}
///
pub fn get_stats(&self) -> Result<CallbackStats> {
let stats = self.stats.lock()?;
Ok(stats.clone())
}
///
pub fn callbacks<'a>(&self) -> RemoteCallbacks<'a> {
let mut callbacks = RemoteCallbacks::new();
///
pub fn callbacks<'a>(&self) -> RemoteCallbacks<'a> {
let mut callbacks = RemoteCallbacks::new();
let this = self.clone();
callbacks.push_transfer_progress(
move |current, total, bytes| {
this.push_transfer_progress(current, total, bytes);
},
);
let this = self.clone();
callbacks.push_transfer_progress(
move |current, total, bytes| {
this.push_transfer_progress(current, total, bytes);
},
);
let this = self.clone();
callbacks.update_tips(move |name, a, b| {
this.update_tips(name, a, b);
true
});
let this = self.clone();
callbacks.update_tips(move |name, a, b| {
this.update_tips(name, a, b);
true
});
let this = self.clone();
callbacks.transfer_progress(move |p| {
this.transfer_progress(&p);
true
});
let this = self.clone();
callbacks.transfer_progress(move |p| {
this.transfer_progress(&p);
true
});
let this = self.clone();
callbacks.pack_progress(move |stage, current, total| {
this.pack_progress(stage, total, current);
});
let this = self.clone();
callbacks.pack_progress(move |stage, current, total| {
this.pack_progress(stage, total, current);
});
let this = self.clone();
callbacks.push_update_reference(move |reference, msg| {
this.push_update_reference(reference, msg);
Ok(())
});
let this = self.clone();
callbacks.push_update_reference(move |reference, msg| {
this.push_update_reference(reference, msg);
Ok(())
});
let this = self.clone();
callbacks.credentials(
move |url, username_from_url, allowed_types| {
this.credentials(
url,
username_from_url,
allowed_types,
)
},
);
let this = self.clone();
callbacks.credentials(
move |url, username_from_url, allowed_types| {
this.credentials(
url,
username_from_url,
allowed_types,
)
},
);
callbacks
}
callbacks.sideband_progress(move |data| {
log::debug!(
"sideband transfer: '{}'",
String::from_utf8_lossy(data).trim()
);
true
});
fn push_update_reference(
&self,
reference: &str,
msg: Option<&str>,
) {
log::debug!(
"push_update_reference: '{}' {:?}",
reference,
msg
);
callbacks
}
if let Ok(mut stats) = self.stats.lock() {
stats.push_rejected_msg = msg
.map(|msg| (reference.to_string(), msg.to_string()));
}
}
fn push_update_reference(
&self,
reference: &str,
msg: Option<&str>,
) {
log::debug!("push_update_reference: '{reference}' {msg:?}");
fn pack_progress(
&self,
stage: git2::PackBuilderStage,
total: usize,
current: usize,
) {
log::debug!("packing: {:?} - {}/{}", stage, current, total);
self.sender.clone().map(|sender| {
sender.send(ProgressNotification::Packing {
stage,
total,
current,
})
});
}
if let Ok(mut stats) = self.stats.lock() {
stats.push_rejected_msg = msg
.map(|msg| (reference.to_string(), msg.to_string()));
}
}
fn transfer_progress(&self, p: &git2::Progress) {
log::debug!(
"transfer: {}/{}",
p.received_objects(),
p.total_objects()
);
self.sender.clone().map(|sender| {
sender.send(ProgressNotification::Transfer {
objects: p.received_objects(),
total_objects: p.total_objects(),
})
});
}
fn pack_progress(
&self,
stage: git2::PackBuilderStage,
total: usize,
current: usize,
) {
log::debug!("packing: {stage:?} - {current}/{total}");
self.sender.clone().map(|sender| {
sender.send(ProgressNotification::Packing {
stage,
total,
current,
})
});
}
fn update_tips(&self, name: &str, a: git2::Oid, b: git2::Oid) {
log::debug!("update tips: '{}' [{}] [{}]", name, a, b);
self.sender.clone().map(|sender| {
sender.send(ProgressNotification::UpdateTips {
name: name.to_string(),
a: a.into(),
b: b.into(),
})
});
}
fn transfer_progress(&self, p: &git2::Progress) {
log::debug!(
"transfer: {}/{}",
p.received_objects(),
p.total_objects()
);
self.sender.clone().map(|sender| {
sender.send(ProgressNotification::Transfer {
objects: p.received_objects(),
total_objects: p.total_objects(),
})
});
}
fn push_transfer_progress(
&self,
current: usize,
total: usize,
bytes: usize,
) {
log::debug!("progress: {}/{} ({} B)", current, total, bytes,);
self.sender.clone().map(|sender| {
sender.send(ProgressNotification::PushTransfer {
current,
total,
bytes,
})
});
}
fn update_tips(&self, name: &str, a: git2::Oid, b: git2::Oid) {
log::debug!("update tips: '{name}' [{a}] [{b}]");
self.sender.clone().map(|sender| {
sender.send(ProgressNotification::UpdateTips {
name: name.to_string(),
a: a.into(),
b: b.into(),
})
});
}
// If credentials are bad, we don't ask the user to re-fill their creds. We push an error and they will be able to restart their action (for example a push) and retype their creds.
// This behavior is explained in a issue on git2-rs project : https://github.com/rust-lang/git2-rs/issues/347
// An implementation reference is done in cargo : https://github.com/rust-lang/cargo/blob/9fb208dddb12a3081230a5fd8f470e01df8faa25/src/cargo/sources/git/utils.rs#L588
// There is also a guide about libgit2 authentication : https://libgit2.org/docs/guides/authentication/
fn credentials(
&self,
url: &str,
username_from_url: Option<&str>,
allowed_types: git2::CredentialType,
) -> std::result::Result<Cred, GitError> {
log::debug!(
"creds: '{}' {:?} ({:?})",
url,
username_from_url,
allowed_types
);
fn push_transfer_progress(
&self,
current: usize,
total: usize,
bytes: usize,
) {
log::debug!("progress: {current}/{total} ({bytes} B)");
self.sender.clone().map(|sender| {
sender.send(ProgressNotification::PushTransfer {
current,
total,
bytes,
})
});
}
// This boolean is used to avoid multiple calls to credentials callback.
if self.first_call_to_credentials.load(Ordering::Relaxed) {
self.first_call_to_credentials
.store(false, Ordering::Relaxed);
} else {
return Err(GitError::from_str("Bad credentials."));
}
// If credentials are bad, we don't ask the user to re-fill their creds. We push an error and they will be able to restart their action (for example a push) and retype their creds.
// This behavior is explained in a issue on git2-rs project : https://github.com/rust-lang/git2-rs/issues/347
// An implementation reference is done in cargo : https://github.com/rust-lang/cargo/blob/9fb208dddb12a3081230a5fd8f470e01df8faa25/src/cargo/sources/git/utils.rs#L588
// There is also a guide about libgit2 authentication : https://libgit2.org/docs/guides/authentication/
fn credentials(
&self,
url: &str,
username_from_url: Option<&str>,
allowed_types: git2::CredentialType,
) -> std::result::Result<Cred, GitError> {
log::debug!(
"creds: '{url}' {username_from_url:?} ({allowed_types:?})",
);
match &self.basic_credential {
_ if allowed_types.is_ssh_key() => {
match username_from_url {
Some(username) => {
Cred::ssh_key_from_agent(username)
}
None => Err(GitError::from_str(
" Couldn't extract username from url.",
)),
}
}
Some(BasicAuthCredential {
username: Some(user),
password: Some(pwd),
}) if allowed_types.is_user_pass_plaintext() => {
Cred::userpass_plaintext(user, pwd)
}
Some(BasicAuthCredential {
username: Some(user),
password: _,
}) if allowed_types.is_username() => Cred::username(user),
_ if allowed_types.is_default() => Cred::default(),
_ => Err(GitError::from_str("Couldn't find credentials")),
}
}
// This boolean is used to avoid multiple calls to credentials callback.
if self.first_call_to_credentials.load(Ordering::Relaxed) {
self.first_call_to_credentials
.store(false, Ordering::Relaxed);
} else {
return Err(GitError::from_str("Bad credentials."));
}
match &self.basic_credential {
_ if allowed_types.is_ssh_key() => username_from_url
.map_or_else(
|| {
Err(GitError::from_str(
" Couldn't extract username from url.",
))
},
Cred::ssh_key_from_agent,
),
Some(BasicAuthCredential {
username: Some(user),
password: Some(pwd),
}) if allowed_types.is_user_pass_plaintext() => {
Cred::userpass_plaintext(user, pwd)
}
Some(BasicAuthCredential {
username: Some(user),
password: _,
}) if allowed_types.is_username() => Cred::username(user),
_ if allowed_types.is_default() => Cred::default(),
_ => Err(GitError::from_str("Couldn't find credentials")),
}
}
}

View file

@ -5,212 +5,555 @@ pub(crate) mod push;
pub(crate) mod tags;
use crate::{
error::{Error, Result},
sync::{
cred::BasicAuthCredential,
remotes::push::ProgressNotification, utils,
},
error::{Error, Result},
sync::{
cred::BasicAuthCredential,
remotes::push::ProgressNotification, repository::repo, utils,
},
ProgressPercent,
};
use crossbeam_channel::Sender;
use git2::{BranchType, FetchOptions, Repository};
use git2::{
BranchType, FetchOptions, ProxyOptions, Remote, Repository,
};
use scopetime::scope_time;
use utils::bytes2string;
pub use callbacks::Callbacks;
pub use tags::tags_missing_remote;
use super::RepoPath;
/// origin
pub const DEFAULT_REMOTE_NAME: &str = "origin";
///
pub fn get_remotes(repo_path: &str) -> Result<Vec<String>> {
scope_time!("get_remotes");
pub fn proxy_auto<'a>() -> ProxyOptions<'a> {
let mut proxy = ProxyOptions::new();
proxy.auto();
proxy
}
let repo = utils::repo(repo_path)?;
let remotes = repo.remotes()?;
let remotes: Vec<String> =
remotes.iter().flatten().map(String::from).collect();
///
pub fn add_remote(
repo_path: &RepoPath,
name: &str,
url: &str,
) -> Result<()> {
let repo = repo(repo_path)?;
repo.remote(name, url)?;
Ok(())
}
Ok(remotes)
///
pub fn rename_remote(
repo_path: &RepoPath,
name: &str,
new_name: &str,
) -> Result<()> {
let repo = repo(repo_path)?;
repo.remote_rename(name, new_name)?;
Ok(())
}
///
pub fn update_remote_url(
repo_path: &RepoPath,
name: &str,
new_url: &str,
) -> Result<()> {
let repo = repo(repo_path)?;
repo.remote_set_url(name, new_url)?;
Ok(())
}
///
pub fn delete_remote(
repo_path: &RepoPath,
remote_name: &str,
) -> Result<()> {
let repo = repo(repo_path)?;
repo.remote_delete(remote_name)?;
Ok(())
}
///
pub fn validate_remote_name(name: &str) -> bool {
Remote::is_valid_name(name)
}
///
pub fn get_remotes(repo_path: &RepoPath) -> Result<Vec<String>> {
scope_time!("get_remotes");
let repo = repo(repo_path)?;
let remotes = repo.remotes()?;
let remotes: Vec<String> =
remotes.iter().flatten().map(String::from).collect();
Ok(remotes)
}
///
pub fn get_remote_url(
repo_path: &RepoPath,
remote_name: &str,
) -> Result<Option<String>> {
let repo = repo(repo_path)?;
let remote = repo.find_remote(remote_name)?.clone();
let url = remote.url();
if let Some(u) = url {
return Ok(Some(u.to_string()));
}
Ok(None)
}
/// tries to find origin or the only remote that is defined if any
/// in case of multiple remotes and none named *origin* we fail
pub fn get_default_remote(repo_path: &str) -> Result<String> {
let repo = utils::repo(repo_path)?;
get_default_remote_in_repo(&repo)
pub fn get_default_remote(repo_path: &RepoPath) -> Result<String> {
let repo = repo(repo_path)?;
get_default_remote_in_repo(&repo)
}
/// Gets the current branch the user is on.
/// Returns none if they are not on a branch
/// and Err if there was a problem finding the branch
fn get_current_branch(
repo: &Repository,
) -> Result<Option<git2::Branch<'_>>> {
for b in repo.branches(None)? {
let branch = b?.0;
if branch.is_head() {
return Ok(Some(branch));
}
}
Ok(None)
}
/// Tries to find the default repo to fetch from based on configuration.
///
/// > `branch.<name>.remote`
/// >
/// > When on branch `<name>`, it tells `git fetch` and `git push` which remote to fetch from or
/// > push to. [...] If no remote is configured, or if you are not on any branch and there is more
/// > than one remote defined in the repository, it defaults to `origin` for fetching [...].
///
/// [git-config-branch-name-remote]: https://git-scm.com/docs/git-config#Documentation/git-config.txt-branchltnamegtremote
///
/// Falls back to `get_default_remote_in_repo`.
pub fn get_default_remote_for_fetch(
repo_path: &RepoPath,
) -> Result<String> {
let repo = repo(repo_path)?;
get_default_remote_for_fetch_in_repo(&repo)
}
// TODO: Very similar to `get_default_remote_for_push_in_repo`. Can probably be refactored.
pub(crate) fn get_default_remote_for_fetch_in_repo(
repo: &Repository,
) -> Result<String> {
scope_time!("get_default_remote_for_fetch_in_repo");
let config = repo.config()?;
let branch = get_current_branch(repo)?;
if let Some(branch) = branch {
let remote_name = bytes2string(branch.name_bytes()?)?;
let entry_name = format!("branch.{}.remote", &remote_name);
if let Ok(entry) = config.get_entry(&entry_name) {
return bytes2string(entry.value_bytes());
}
}
get_default_remote_in_repo(repo)
}
/// Tries to find the default repo to push to based on configuration.
///
/// > `remote.pushDefault`
/// >
/// > The remote to push to by default. Overrides `branch.<name>.remote` for all branches, and is
/// > overridden by `branch.<name>.pushRemote` for specific branches.
///
/// > `branch.<name>.remote`
/// >
/// > When on branch `<name>`, it tells `git fetch` and `git push` which remote to fetch from or
/// > push to. The remote to push to may be overridden with `remote.pushDefault` (for all
/// > branches). The remote to push to, for the current branch, may be further overridden by
/// > `branch.<name>.pushRemote`. If no remote is configured, or if you are not on any branch and
/// > there is more than one remote defined in the repository, it defaults to `origin` for fetching
/// > and `remote.pushDefault` for pushing.
///
/// [git-config-remote-push-default]: https://git-scm.com/docs/git-config#Documentation/git-config.txt-remotepushDefault
/// [git-config-branch-name-remote]: https://git-scm.com/docs/git-config#Documentation/git-config.txt-branchltnamegtremote
///
/// Falls back to `get_default_remote_in_repo`.
pub fn get_default_remote_for_push(
repo_path: &RepoPath,
) -> Result<String> {
let repo = repo(repo_path)?;
get_default_remote_for_push_in_repo(&repo)
}
// TODO: Very similar to `get_default_remote_for_fetch_in_repo`. Can probably be refactored.
pub(crate) fn get_default_remote_for_push_in_repo(
repo: &Repository,
) -> Result<String> {
scope_time!("get_default_remote_for_push_in_repo");
let config = repo.config()?;
let branch = get_current_branch(repo)?;
if let Some(branch) = branch {
let remote_name = bytes2string(branch.name_bytes()?)?;
let entry_name =
format!("branch.{}.pushRemote", &remote_name);
if let Ok(entry) = config.get_entry(&entry_name) {
return bytes2string(entry.value_bytes());
}
if let Ok(entry) = config.get_entry("remote.pushDefault") {
return bytes2string(entry.value_bytes());
}
let entry_name = format!("branch.{}.remote", &remote_name);
if let Ok(entry) = config.get_entry(&entry_name) {
return bytes2string(entry.value_bytes());
}
}
get_default_remote_in_repo(repo)
}
/// see `get_default_remote`
pub(crate) fn get_default_remote_in_repo(
repo: &Repository,
repo: &Repository,
) -> Result<String> {
scope_time!("get_default_remote_in_repo");
scope_time!("get_default_remote_in_repo");
let remotes = repo.remotes()?;
let remotes = repo.remotes()?;
// if `origin` exists return that
let found_origin = remotes.iter().any(|r| {
r.map(|r| r == DEFAULT_REMOTE_NAME).unwrap_or_default()
});
if found_origin {
return Ok(DEFAULT_REMOTE_NAME.into());
}
// if `origin` exists return that
let found_origin = remotes
.iter()
.any(|r| r.is_some_and(|r| r == DEFAULT_REMOTE_NAME));
if found_origin {
return Ok(DEFAULT_REMOTE_NAME.into());
}
//if only one remote exists pick that
if remotes.len() == 1 {
let first_remote = remotes
.iter()
.next()
.flatten()
.map(String::from)
.ok_or_else(|| {
Error::Generic("no remote found".into())
})?;
//if only one remote exists pick that
if remotes.len() == 1 {
let first_remote = remotes
.iter()
.next()
.flatten()
.map(String::from)
.ok_or_else(|| {
Error::Generic("no remote found".into())
})?;
return Ok(first_remote);
}
return Ok(first_remote);
}
//inconclusive
Err(Error::NoDefaultRemoteFound)
//inconclusive
Err(Error::NoDefaultRemoteFound)
}
/// fetches from upstream/remote for `branch`
///
fn fetch_from_remote(
repo_path: &RepoPath,
remote: &str,
basic_credential: Option<BasicAuthCredential>,
progress_sender: Option<Sender<ProgressNotification>>,
) -> Result<()> {
let repo = repo(repo_path)?;
let mut remote = repo.find_remote(remote)?;
let mut options = FetchOptions::new();
let callbacks = Callbacks::new(progress_sender, basic_credential);
options.prune(git2::FetchPrune::On);
options.proxy_options(proxy_auto());
options.download_tags(git2::AutotagOption::All);
options.remote_callbacks(callbacks.callbacks());
remote.fetch(&[] as &[&str], Some(&mut options), None)?;
// fetch tags (also removing remotely deleted ones)
remote.fetch(
&["refs/tags/*:refs/tags/*"],
Some(&mut options),
None,
)?;
Ok(())
}
/// updates/prunes all branches from all remotes
pub fn fetch_all(
repo_path: &RepoPath,
basic_credential: &Option<BasicAuthCredential>,
progress_sender: &Option<Sender<ProgressPercent>>,
) -> Result<()> {
scope_time!("fetch_all");
let repo = repo(repo_path)?;
let remotes = repo
.remotes()?
.iter()
.flatten()
.map(String::from)
.collect::<Vec<_>>();
let remotes_count = remotes.len();
for (idx, remote) in remotes.into_iter().enumerate() {
fetch_from_remote(
repo_path,
&remote,
basic_credential.clone(),
None,
)?;
if let Some(sender) = progress_sender {
let progress = ProgressPercent::new(idx, remotes_count);
sender.send(progress)?;
}
}
Ok(())
}
/// fetches from upstream/remote for local `branch`
pub(crate) fn fetch(
repo_path: &str,
branch: &str,
basic_credential: Option<BasicAuthCredential>,
progress_sender: Option<Sender<ProgressNotification>>,
repo_path: &RepoPath,
branch: &str,
basic_credential: Option<BasicAuthCredential>,
progress_sender: Option<Sender<ProgressNotification>>,
) -> Result<usize> {
scope_time!("fetch_origin");
scope_time!("fetch");
let repo = utils::repo(repo_path)?;
let branch_ref = repo
.find_branch(branch, BranchType::Local)?
.into_reference();
let branch_ref = bytes2string(branch_ref.name_bytes())?;
let remote_name = repo.branch_upstream_remote(&branch_ref)?;
let remote_name = bytes2string(&*remote_name)?;
let mut remote = repo.find_remote(&remote_name)?;
let repo = repo(repo_path)?;
let branch_ref = repo
.find_branch(branch, BranchType::Local)?
.into_reference();
let branch_ref = bytes2string(branch_ref.name_bytes())?;
let remote_name = repo.branch_upstream_remote(&branch_ref)?;
let remote_name = bytes2string(&remote_name)?;
let mut remote = repo.find_remote(&remote_name)?;
let mut options = FetchOptions::new();
let callbacks = Callbacks::new(progress_sender, basic_credential);
options.remote_callbacks(callbacks.callbacks());
let mut options = FetchOptions::new();
options.download_tags(git2::AutotagOption::All);
let callbacks = Callbacks::new(progress_sender, basic_credential);
options.remote_callbacks(callbacks.callbacks());
options.proxy_options(proxy_auto());
remote.fetch(&[branch], Some(&mut options), None)?;
remote.fetch(&[branch], Some(&mut options), None)?;
Ok(remote.stats().received_bytes())
Ok(remote.stats().received_bytes())
}
#[cfg(test)]
mod tests {
use super::*;
use crate::sync::tests::{
debug_cmd_print, repo_clone, repo_init,
};
use super::*;
use crate::sync::tests::{
debug_cmd_print, repo_clone, repo_init,
};
#[test]
fn test_smoke() {
let (remote_dir, _remote) = repo_init().unwrap();
let remote_path = remote_dir.path().to_str().unwrap();
let (repo_dir, _repo) = repo_clone(remote_path).unwrap();
let repo_path = repo_dir.path().as_os_str().to_str().unwrap();
#[test]
fn test_smoke() {
let (remote_dir, _remote) = repo_init().unwrap();
let remote_path = remote_dir.path().to_str().unwrap();
let (repo_dir, _repo) = repo_clone(remote_path).unwrap();
let repo_path: &RepoPath =
&repo_dir.keep().as_os_str().to_str().unwrap().into();
let remotes = get_remotes(repo_path).unwrap();
let remotes = get_remotes(repo_path).unwrap();
assert_eq!(remotes, vec![String::from("origin")]);
assert_eq!(remotes, vec![String::from("origin")]);
fetch(repo_path, "master", None, None).unwrap();
}
fetch(repo_path, "master", None, None).unwrap();
}
#[test]
fn test_default_remote() {
let (remote_dir, _remote) = repo_init().unwrap();
let remote_path = remote_dir.path().to_str().unwrap();
let (repo_dir, _repo) = repo_clone(remote_path).unwrap();
let repo_path = repo_dir.path().as_os_str().to_str().unwrap();
#[test]
fn test_default_remote() {
let (remote_dir, _remote) = repo_init().unwrap();
let remote_path = remote_dir.path().to_str().unwrap();
let (repo_dir, _repo) = repo_clone(remote_path).unwrap();
let repo_path: &RepoPath =
&repo_dir.keep().as_os_str().to_str().unwrap().into();
debug_cmd_print(
repo_path,
&format!("git remote add second {}", remote_path)[..],
);
debug_cmd_print(
repo_path,
&format!("git remote add second {remote_path}")[..],
);
let remotes = get_remotes(repo_path).unwrap();
let remotes = get_remotes(repo_path).unwrap();
assert_eq!(
remotes,
vec![String::from("origin"), String::from("second")]
);
assert_eq!(
remotes,
vec![String::from("origin"), String::from("second")]
);
let first = get_default_remote_in_repo(
&utils::repo(repo_path).unwrap(),
)
.unwrap();
assert_eq!(first, String::from("origin"));
}
let first =
get_default_remote_in_repo(&repo(repo_path).unwrap())
.unwrap();
assert_eq!(first, String::from("origin"));
}
#[test]
fn test_default_remote_out_of_order() {
let (remote_dir, _remote) = repo_init().unwrap();
let remote_path = remote_dir.path().to_str().unwrap();
let (repo_dir, _repo) = repo_clone(remote_path).unwrap();
let repo_path = repo_dir.path().as_os_str().to_str().unwrap();
#[test]
fn test_default_remote_out_of_order() {
let (remote_dir, _remote) = repo_init().unwrap();
let remote_path = remote_dir.path().to_str().unwrap();
let (repo_dir, _repo) = repo_clone(remote_path).unwrap();
let repo_path: &RepoPath =
&repo_dir.keep().as_os_str().to_str().unwrap().into();
debug_cmd_print(
repo_path,
"git remote rename origin alternate",
);
debug_cmd_print(
repo_path,
"git remote rename origin alternate",
);
debug_cmd_print(
repo_path,
&format!("git remote add origin {}", remote_path)[..],
);
debug_cmd_print(
repo_path,
&format!("git remote add origin {remote_path}")[..],
);
//NOTE: aparently remotes are not chronolically sorted but alphabetically
let remotes = get_remotes(repo_path).unwrap();
//NOTE: apparently remotes are not chronolically sorted but alphabetically
let remotes = get_remotes(repo_path).unwrap();
assert_eq!(
remotes,
vec![String::from("alternate"), String::from("origin")]
);
assert_eq!(
remotes,
vec![String::from("alternate"), String::from("origin")]
);
let first = get_default_remote_in_repo(
&utils::repo(repo_path).unwrap(),
)
.unwrap();
assert_eq!(first, String::from("origin"));
}
let first =
get_default_remote_in_repo(&repo(repo_path).unwrap())
.unwrap();
assert_eq!(first, String::from("origin"));
}
#[test]
fn test_default_remote_inconclusive() {
let (remote_dir, _remote) = repo_init().unwrap();
let remote_path = remote_dir.path().to_str().unwrap();
let (repo_dir, _repo) = repo_clone(remote_path).unwrap();
let repo_path = repo_dir.path().as_os_str().to_str().unwrap();
#[test]
fn test_default_remote_inconclusive() {
let (remote_dir, _remote) = repo_init().unwrap();
let remote_path = remote_dir.path().to_str().unwrap();
let (repo_dir, _repo) = repo_clone(remote_path).unwrap();
let repo_path: &RepoPath =
&repo_dir.keep().as_os_str().to_str().unwrap().into();
debug_cmd_print(
repo_path,
"git remote rename origin alternate",
);
debug_cmd_print(
repo_path,
"git remote rename origin alternate",
);
debug_cmd_print(
repo_path,
&format!("git remote add someremote {}", remote_path)[..],
);
debug_cmd_print(
repo_path,
&format!("git remote add someremote {remote_path}")[..],
);
let remotes = get_remotes(repo_path).unwrap();
assert_eq!(
remotes,
vec![
String::from("alternate"),
String::from("someremote")
]
);
let remotes = get_remotes(repo_path).unwrap();
assert_eq!(
remotes,
vec![
String::from("alternate"),
String::from("someremote")
]
);
let res = get_default_remote_in_repo(
&utils::repo(repo_path).unwrap(),
);
assert_eq!(res.is_err(), true);
assert!(matches!(res, Err(Error::NoDefaultRemoteFound)));
}
let default_remote =
get_default_remote_in_repo(&repo(repo_path).unwrap());
assert!(matches!(
default_remote,
Err(Error::NoDefaultRemoteFound)
));
}
#[test]
fn test_default_remote_for_fetch() {
let (remote_dir, _remote) = repo_init().unwrap();
let remote_path = remote_dir.path().to_str().unwrap();
let (repo_dir, repo) = repo_clone(remote_path).unwrap();
let repo_path: &RepoPath =
&repo_dir.keep().as_os_str().to_str().unwrap().into();
debug_cmd_print(
repo_path,
"git remote rename origin alternate",
);
debug_cmd_print(
repo_path,
&format!("git remote add someremote {remote_path}")[..],
);
let mut config = repo.config().unwrap();
config
.set_str("branch.master.remote", "branchremote")
.unwrap();
let default_fetch_remote =
get_default_remote_for_fetch_in_repo(&repo);
assert!(
matches!(default_fetch_remote, Ok(remote_name) if remote_name == "branchremote")
);
}
#[test]
fn test_default_remote_for_push() {
let (remote_dir, _remote) = repo_init().unwrap();
let remote_path = remote_dir.path().to_str().unwrap();
let (repo_dir, repo) = repo_clone(remote_path).unwrap();
let repo_path: &RepoPath =
&repo_dir.keep().as_os_str().to_str().unwrap().into();
debug_cmd_print(
repo_path,
"git remote rename origin alternate",
);
debug_cmd_print(
repo_path,
&format!("git remote add someremote {remote_path}")[..],
);
let mut config = repo.config().unwrap();
config
.set_str("branch.master.remote", "branchremote")
.unwrap();
let default_push_remote =
get_default_remote_for_push_in_repo(&repo);
assert!(
matches!(default_push_remote, Ok(remote_name) if remote_name == "branchremote")
);
config.set_str("remote.pushDefault", "pushdefault").unwrap();
let default_push_remote =
get_default_remote_for_push_in_repo(&repo);
assert!(
matches!(default_push_remote, Ok(remote_name) if remote_name == "pushdefault")
);
config
.set_str("branch.master.pushRemote", "branchpushremote")
.unwrap();
let default_push_remote =
get_default_remote_for_push_in_repo(&repo);
assert!(
matches!(default_push_remote, Ok(remote_name) if remote_name == "branchpushremote")
);
}
}

Some files were not shown because too many files have changed in this diff Show more