Skip to content

Commit 5bfdb40

Browse files
author
Stephan Dilly
authored
commit details in log (#107)
1 parent 3dc1119 commit 5bfdb40

27 files changed

+791
-78
lines changed

.github/workflows/cd.yml

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,20 +10,28 @@ jobs:
1010
strategy:
1111
matrix:
1212
os: [ubuntu-latest, macos-latest, windows-latest]
13-
rust: [stable]
13+
1414
runs-on: ${{ matrix.os }}
1515
steps:
1616
- uses: actions/checkout@v2
1717
- name: Get version
1818
id: get_version
1919
run: echo ::set-output name=version::${GITHUB_REF/refs\/tags\//}
20+
21+
- name: Install Rust
22+
uses: actions-rs/toolchain@v1
23+
with:
24+
toolchain: stable
25+
profile: minimal
26+
components: clippy
27+
2028
- name: Build
2129
run: cargo build
2230
- name: Run tests
2331
run: make test
2432
- name: Run clippy
2533
run: |
26-
rustup component add clippy
34+
cargo clean
2735
make clippy
2836
2937
- name: Setup MUSL

.github/workflows/ci.yml

Lines changed: 24 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -13,19 +13,29 @@ jobs:
1313
strategy:
1414
matrix:
1515
os: [ubuntu-latest, windows-latest, macos-latest]
16-
rust: [stable]
1716

1817
runs-on: ${{ matrix.os }}
1918

2019
steps:
2120
- uses: actions/checkout@v2
21+
22+
- name: Install Rust
23+
uses: actions-rs/toolchain@v1
24+
with:
25+
toolchain: stable
26+
profile: minimal
27+
components: clippy
28+
2229
- name: Build Debug
23-
run: cargo build
30+
run: |
31+
rustc --version
32+
cargo build
33+
2434
- name: Run tests
2535
run: make test
36+
2637
- name: Run clippy
2738
run: |
28-
rustup component add clippy
2939
cargo clean
3040
make clippy
3141
- name: Build Release
@@ -35,9 +45,15 @@ jobs:
3545
runs-on: ubuntu-latest
3646
steps:
3747
- uses: actions/checkout@v2
48+
- name: Install Rust
49+
uses: actions-rs/toolchain@v1
50+
with:
51+
toolchain: stable
52+
profile: minimal
53+
target: x86_64-unknown-linux-musl
54+
3855
- name: Setup MUSL
3956
run: |
40-
rustup target add x86_64-unknown-linux-musl
4157
sudo apt-get -qq install musl-tools
4258
- name: Build Debug
4359
run: cargo build --target=x86_64-unknown-linux-musl
@@ -51,7 +67,10 @@ jobs:
5167
steps:
5268
- uses: actions/checkout@master
5369
- name: Install Rust
54-
run: rustup update stable && rustup default stable && rustup component add rustfmt
70+
uses: actions-rs/toolchain@v1
71+
with:
72+
toolchain: stable
73+
components: rustfmt
5574
- run: cargo fmt -- --check
5675

5776
sec:

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
1313
### Added
1414
- New `Stage all [a]`/`Unstage all [a]` in changes lists ([#82](https://github.com/extrawurst/gitui/issues/82))
1515
- add `-d`, `--directory` options to set working directory ([#73](https://github.com/extrawurst/gitui/issues/73))
16+
- commit detail view in revlog ([#80](https://github.com/extrawurst/gitui/issues/80))
1617

1718
### Fixed
1819
- app closes when staging invalid file/path ([#108](https://github.com/extrawurst/gitui/issues/108))

Makefile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ fmt:
2929
cargo fmt -- --check
3030

3131
clippy:
32+
touch src/main.rs
3233
cargo clean -p gitui -p asyncgit -p scopetime
3334
cargo clippy --all-features
3435

README.md

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ presentation slides: https://github.com/extrawurst/gitui-presentation
4545

4646
# known limitations
4747

48-
* no support for [bare repositories](https://git-scm.com/book/en/v2/Git-on-the-Server-Getting-Git-on-a-Server)
48+
* 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))
4949
* [core.hooksPath](https://git-scm.com/docs/githooks) config not supported
5050
* revert/reset hunk in working dir (see [#11](https://github.com/extrawurst/gitui/issues/11))
5151

@@ -89,9 +89,7 @@ see [releases](https://github.com/extrawurst/gitui/releases)
8989

9090
### requirements
9191

92-
install `rust`/`cargo`: https://www.rust-lang.org/tools/install
93-
94-
min rust version: `1.42`
92+
install **latest** `rust`/`cargo`: https://www.rust-lang.org/tools/install
9593

9694
### cargo install
9795

asyncgit/src/commit_files.rs

Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
1+
use crate::{
2+
error::Result, sync, AsyncNotification, StatusItem, CWD,
3+
};
4+
use crossbeam_channel::Sender;
5+
use std::sync::{
6+
atomic::{AtomicUsize, Ordering},
7+
Arc, Mutex,
8+
};
9+
use sync::CommitId;
10+
11+
type ResultType = Vec<StatusItem>;
12+
struct Request<R, A>(R, A);
13+
14+
///
15+
pub struct AsyncCommitFiles {
16+
current: Arc<Mutex<Option<Request<CommitId, ResultType>>>>,
17+
sender: Sender<AsyncNotification>,
18+
pending: Arc<AtomicUsize>,
19+
}
20+
21+
impl AsyncCommitFiles {
22+
///
23+
pub fn new(sender: &Sender<AsyncNotification>) -> Self {
24+
Self {
25+
current: Arc::new(Mutex::new(None)),
26+
sender: sender.clone(),
27+
pending: Arc::new(AtomicUsize::new(0)),
28+
}
29+
}
30+
31+
///
32+
pub fn current(
33+
&mut self,
34+
) -> Result<Option<(CommitId, ResultType)>> {
35+
let c = self.current.lock()?;
36+
37+
if let Some(c) = c.as_ref() {
38+
Ok(Some((c.0, c.1.clone())))
39+
} else {
40+
Ok(None)
41+
}
42+
}
43+
44+
///
45+
pub fn is_pending(&self) -> bool {
46+
self.pending.load(Ordering::Relaxed) > 0
47+
}
48+
49+
///
50+
pub fn fetch(&mut self, id: CommitId) -> Result<()> {
51+
if self.is_pending() {
52+
return Ok(());
53+
}
54+
55+
log::trace!("request: {}", id.to_string());
56+
57+
{
58+
let current = self.current.lock()?;
59+
if let Some(ref c) = *current {
60+
if c.0 == id {
61+
return Ok(());
62+
}
63+
}
64+
}
65+
66+
let arc_current = Arc::clone(&self.current);
67+
let sender = self.sender.clone();
68+
let arc_pending = Arc::clone(&self.pending);
69+
70+
rayon_core::spawn(move || {
71+
arc_pending.fetch_add(1, Ordering::Relaxed);
72+
73+
Self::fetch_helper(id, arc_current)
74+
.expect("failed to fetch");
75+
76+
arc_pending.fetch_sub(1, Ordering::Relaxed);
77+
78+
sender
79+
.send(AsyncNotification::CommitFiles)
80+
.expect("error sending");
81+
});
82+
83+
Ok(())
84+
}
85+
86+
fn fetch_helper(
87+
id: CommitId,
88+
arc_current: Arc<
89+
Mutex<Option<Request<CommitId, ResultType>>>,
90+
>,
91+
) -> Result<()> {
92+
let res = sync::get_commit_files(CWD, id)?;
93+
94+
{
95+
let mut last = arc_current.lock()?;
96+
*last = Some(Request(id, res));
97+
}
98+
99+
Ok(())
100+
}
101+
}

asyncgit/src/lib.rs

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,13 +6,15 @@
66
#![deny(clippy::result_unwrap_used)]
77
#![deny(clippy::panic)]
88

9+
mod commit_files;
910
mod diff;
1011
mod error;
1112
mod revlog;
1213
mod status;
1314
pub mod sync;
1415

1516
pub use crate::{
17+
commit_files::AsyncCommitFiles,
1618
diff::{AsyncDiff, DiffParams},
1719
revlog::{AsyncLog, FetchStatus},
1820
status::{AsyncStatus, StatusParams},
@@ -27,14 +29,16 @@ use std::{
2729
};
2830

2931
/// this type is used to communicate events back through the channel
30-
#[derive(Copy, Clone, Debug)]
32+
#[derive(Copy, Clone, Debug, PartialEq)]
3133
pub enum AsyncNotification {
3234
///
3335
Status,
3436
///
3537
Diff,
3638
///
3739
Log,
40+
///
41+
CommitFiles,
3842
}
3943

4044
/// current working director `./`

asyncgit/src/revlog.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -41,10 +41,10 @@ static SLEEP_BACKGROUND: Duration = Duration::from_millis(1000);
4141

4242
impl AsyncLog {
4343
///
44-
pub fn new(sender: Sender<AsyncNotification>) -> Self {
44+
pub fn new(sender: &Sender<AsyncNotification>) -> Self {
4545
Self {
4646
current: Arc::new(Mutex::new(Vec::new())),
47-
sender,
47+
sender: sender.clone(),
4848
pending: Arc::new(AtomicBool::new(false)),
4949
background: Arc::new(AtomicBool::new(false)),
5050
}

asyncgit/src/sync/commit_details.rs

Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
use super::{utils::repo, CommitId};
2+
use crate::error::Result;
3+
use git2::Signature;
4+
use scopetime::scope_time;
5+
6+
///
7+
#[derive(Debug, PartialEq)]
8+
pub struct CommitSignature {
9+
///
10+
pub name: String,
11+
///
12+
pub email: String,
13+
/// time in secs since Unix epoch
14+
pub time: i64,
15+
}
16+
17+
impl CommitSignature {
18+
/// convert from git2-rs `Signature`
19+
pub fn from(s: Signature<'_>) -> Self {
20+
Self {
21+
name: s.name().unwrap_or("").to_string(),
22+
email: s.email().unwrap_or("").to_string(),
23+
24+
time: s.when().seconds(),
25+
}
26+
}
27+
}
28+
29+
///
30+
pub struct CommitMessage {
31+
/// first line
32+
pub subject: String,
33+
/// remaining lines if more than one
34+
pub body: Option<String>,
35+
}
36+
37+
impl CommitMessage {
38+
pub fn from(s: &str) -> Self {
39+
if let Some(idx) = s.find('\n') {
40+
let (first, rest) = s.split_at(idx);
41+
Self {
42+
subject: first.to_string(),
43+
body: if rest.is_empty() {
44+
None
45+
} else {
46+
Some(rest.to_string())
47+
},
48+
}
49+
} else {
50+
Self {
51+
subject: s.to_string(),
52+
body: None,
53+
}
54+
}
55+
}
56+
}
57+
58+
///
59+
pub struct CommitDetails {
60+
///
61+
pub author: CommitSignature,
62+
/// committer when differs to `author` otherwise None
63+
pub committer: Option<CommitSignature>,
64+
///
65+
pub message: Option<CommitMessage>,
66+
///
67+
pub hash: String,
68+
}
69+
70+
///
71+
pub fn get_commit_details(
72+
repo_path: &str,
73+
id: CommitId,
74+
) -> Result<CommitDetails> {
75+
scope_time!("get_commit_details");
76+
77+
let repo = repo(repo_path)?;
78+
79+
let commit = repo.find_commit(id.into())?;
80+
81+
let author = CommitSignature::from(commit.author());
82+
let committer = CommitSignature::from(commit.committer());
83+
let committer = if author == committer {
84+
None
85+
} else {
86+
Some(committer)
87+
};
88+
89+
let message = commit.message().map(|m| CommitMessage::from(m));
90+
91+
let details = CommitDetails {
92+
author,
93+
committer,
94+
message,
95+
hash: id.to_string(),
96+
};
97+
98+
Ok(details)
99+
}

0 commit comments

Comments
 (0)