Skip to content

Commit 5672cfd

Browse files
author
Stephan Dilly
authored
compare two commits (#860)
1 parent e35b196 commit 5672cfd

24 files changed

+901
-136
lines changed

CHANGELOG.md

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
77

88
## Unreleased
99

10+
**compare commits**
11+
12+
![compare](assets/compare.gif)
13+
1014
**options**
1115

1216
![options](assets/options.gif)
@@ -20,7 +24,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
2024
![name-validation](assets/branch-validation.gif)
2125

2226
## Added
23-
- allow opening top commit of a branch
27+
- allow inspecting top commit of a branch from list
28+
- compare commits in revlog and head against branch ([#852](https://github.com/extrawurst/gitui/issues/852))
2429
- new options popup (show untracked files, diff settings) ([#849](https://github.com/extrawurst/gitui/issues/849))
2530
- mark and drop multiple stashes ([#854](https://github.com/extrawurst/gitui/issues/854))
2631
- check branch name validity while typing ([#559](https://github.com/extrawurst/gitui/issues/559))

assets/compare.gif

653 KB
Loading

asyncgit/src/commit_files.rs

Lines changed: 37 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -12,9 +12,34 @@ use std::sync::{
1212
type ResultType = Vec<StatusItem>;
1313
struct Request<R, A>(R, A);
1414

15+
///
16+
#[derive(Debug, Copy, Clone, PartialEq)]
17+
pub struct CommitFilesParams {
18+
///
19+
pub id: CommitId,
20+
///
21+
pub other: Option<CommitId>,
22+
}
23+
24+
impl From<CommitId> for CommitFilesParams {
25+
fn from(id: CommitId) -> Self {
26+
Self { id, other: None }
27+
}
28+
}
29+
30+
impl From<(CommitId, CommitId)> for CommitFilesParams {
31+
fn from((id, other): (CommitId, CommitId)) -> Self {
32+
Self {
33+
id,
34+
other: Some(other),
35+
}
36+
}
37+
}
38+
1539
///
1640
pub struct AsyncCommitFiles {
17-
current: Arc<Mutex<Option<Request<CommitId, ResultType>>>>,
41+
current:
42+
Arc<Mutex<Option<Request<CommitFilesParams, ResultType>>>>,
1843
sender: Sender<AsyncGitNotification>,
1944
pending: Arc<AtomicUsize>,
2045
}
@@ -32,7 +57,7 @@ impl AsyncCommitFiles {
3257
///
3358
pub fn current(
3459
&mut self,
35-
) -> Result<Option<(CommitId, ResultType)>> {
60+
) -> Result<Option<(CommitFilesParams, ResultType)>> {
3661
let c = self.current.lock()?;
3762

3863
c.as_ref()
@@ -45,17 +70,17 @@ impl AsyncCommitFiles {
4570
}
4671

4772
///
48-
pub fn fetch(&mut self, id: CommitId) -> Result<()> {
73+
pub fn fetch(&mut self, params: CommitFilesParams) -> Result<()> {
4974
if self.is_pending() {
5075
return Ok(());
5176
}
5277

53-
log::trace!("request: {}", id.to_string());
78+
log::trace!("request: {:?}", params);
5479

5580
{
5681
let current = self.current.lock()?;
5782
if let Some(c) = &*current {
58-
if c.0 == id {
83+
if c.0 == params {
5984
return Ok(());
6085
}
6186
}
@@ -68,7 +93,7 @@ impl AsyncCommitFiles {
6893
self.pending.fetch_add(1, Ordering::Relaxed);
6994

7095
rayon_core::spawn(move || {
71-
Self::fetch_helper(id, &arc_current)
96+
Self::fetch_helper(params, &arc_current)
7297
.expect("failed to fetch");
7398

7499
arc_pending.fetch_sub(1, Ordering::Relaxed);
@@ -82,22 +107,19 @@ impl AsyncCommitFiles {
82107
}
83108

84109
fn fetch_helper(
85-
id: CommitId,
110+
params: CommitFilesParams,
86111
arc_current: &Arc<
87-
Mutex<Option<Request<CommitId, ResultType>>>,
112+
Mutex<Option<Request<CommitFilesParams, ResultType>>>,
88113
>,
89114
) -> Result<()> {
90-
let res = sync::get_commit_files(CWD, id)?;
115+
let res =
116+
sync::get_commit_files(CWD, params.id, params.other)?;
91117

92-
log::trace!(
93-
"get_commit_files: {} ({})",
94-
id.to_string(),
95-
res.len()
96-
);
118+
log::trace!("get_commit_files: {:?} ({})", params, res.len());
97119

98120
{
99121
let mut current = arc_current.lock()?;
100-
*current = Some(Request(id, res));
122+
*current = Some(Request(params, res));
101123
}
102124

103125
Ok(())

asyncgit/src/diff.rs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@ use std::{
1616
///
1717
#[derive(Debug, Hash, Clone, PartialEq)]
1818
pub enum DiffType {
19+
/// diff two commits
20+
Commits((CommitId, CommitId)),
1921
/// diff in a given commit
2022
Commit(CommitId),
2123
/// diff against staged file
@@ -167,6 +169,11 @@ impl AsyncDiff {
167169
id,
168170
params.path.clone(),
169171
)?,
172+
DiffType::Commits(ids) => sync::diff::get_diff_commits(
173+
CWD,
174+
ids,
175+
params.path.clone(),
176+
)?,
170177
};
171178

172179
let mut notify = false;

asyncgit/src/lib.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ mod tags;
4141

4242
pub use crate::{
4343
blame::{AsyncBlame, BlameParams},
44-
commit_files::AsyncCommitFiles,
44+
commit_files::{AsyncCommitFiles, CommitFilesParams},
4545
diff::{AsyncDiff, DiffParams, DiffType},
4646
fetch::{AsyncFetch, FetchRequest},
4747
push::{AsyncPush, PushRequest},

asyncgit/src/sync/commit.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -205,7 +205,7 @@ mod tests {
205205
let details = get_commit_details(repo_path, new_id)?;
206206
assert_eq!(details.message.unwrap().subject, "amended");
207207

208-
let files = get_commit_files(repo_path, new_id)?;
208+
let files = get_commit_files(repo_path, new_id, None)?;
209209

210210
assert_eq!(files.len(), 2);
211211

asyncgit/src/sync/commit_files.rs

Lines changed: 50 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
use std::cmp::Ordering;
2+
13
use super::{stash::is_stash_commit, utils::repo, CommitId};
24
use crate::{
35
error::Error, error::Result, StatusItem, StatusItemType,
@@ -9,12 +11,17 @@ use scopetime::scope_time;
911
pub fn get_commit_files(
1012
repo_path: &str,
1113
id: CommitId,
14+
other: Option<CommitId>,
1215
) -> Result<Vec<StatusItem>> {
1316
scope_time!("get_commit_files");
1417

1518
let repo = repo(repo_path)?;
1619

17-
let diff = get_commit_diff(&repo, id, None)?;
20+
let diff = if let Some(other) = other {
21+
get_compare_commits_diff(&repo, (id, other), None)?
22+
} else {
23+
get_commit_diff(&repo, id, None)?
24+
};
1825

1926
let mut res = Vec::new();
2027

@@ -38,6 +45,44 @@ pub fn get_commit_files(
3845
Ok(res)
3946
}
4047

48+
#[allow(clippy::needless_pass_by_value)]
49+
pub fn get_compare_commits_diff(
50+
repo: &Repository,
51+
ids: (CommitId, CommitId),
52+
pathspec: Option<String>,
53+
) -> Result<Diff<'_>> {
54+
// scope_time!("get_compare_commits_diff");
55+
56+
let commits = (
57+
repo.find_commit(ids.0.into())?,
58+
repo.find_commit(ids.1.into())?,
59+
);
60+
61+
let commits = if commits.0.time().cmp(&commits.1.time())
62+
== Ordering::Greater
63+
{
64+
(commits.1, commits.0)
65+
} else {
66+
commits
67+
};
68+
69+
let trees = (commits.0.tree()?, commits.1.tree()?);
70+
71+
let mut opts = DiffOptions::new();
72+
if let Some(p) = &pathspec {
73+
opts.pathspec(p.clone());
74+
}
75+
opts.show_binary(true);
76+
77+
let diff = repo.diff_tree_to_tree(
78+
Some(&trees.0),
79+
Some(&trees.1),
80+
Some(&mut opts),
81+
)?;
82+
83+
Ok(diff)
84+
}
85+
4186
#[allow(clippy::redundant_pub_crate)]
4287
pub(crate) fn get_commit_diff(
4388
repo: &Repository,
@@ -48,6 +93,7 @@ pub(crate) fn get_commit_diff(
4893

4994
let commit = repo.find_commit(id.into())?;
5095
let commit_tree = commit.tree()?;
96+
5197
let parent = if commit.parent_count() > 0 {
5298
repo.find_commit(commit.parent_id(0)?)
5399
.ok()
@@ -116,7 +162,7 @@ mod tests {
116162

117163
let id = commit(repo_path, "commit msg")?;
118164

119-
let diff = get_commit_files(repo_path, id)?;
165+
let diff = get_commit_files(repo_path, id, None)?;
120166

121167
assert_eq!(diff.len(), 1);
122168
assert_eq!(diff[0].status, StatusItemType::New);
@@ -136,7 +182,7 @@ mod tests {
136182

137183
let id = stash_save(repo_path, None, true, false)?;
138184

139-
let diff = get_commit_files(repo_path, id)?;
185+
let diff = get_commit_files(repo_path, id, None)?;
140186

141187
assert_eq!(diff.len(), 1);
142188
assert_eq!(diff[0].status, StatusItemType::New);
@@ -164,7 +210,7 @@ mod tests {
164210

165211
let id = stash_save(repo_path, None, true, false)?;
166212

167-
let diff = get_commit_files(repo_path, id)?;
213+
let diff = get_commit_files(repo_path, id, None)?;
168214

169215
assert_eq!(diff.len(), 2);
170216
assert_eq!(diff[0].status, StatusItemType::Modified);

asyncgit/src/sync/diff.rs

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
//! sync git api for fetching a diff
22
33
use super::{
4-
commit_files::get_commit_diff,
4+
commit_files::{get_commit_diff, get_compare_commits_diff},
55
utils::{self, get_head_repo, work_dir},
66
CommitId,
77
};
@@ -222,6 +222,22 @@ pub fn get_diff_commit(
222222
raw_diff_to_file_diff(&diff, work_dir)
223223
}
224224

225+
/// get file changes of a diff between two commits
226+
pub fn get_diff_commits(
227+
repo_path: &str,
228+
ids: (CommitId, CommitId),
229+
p: String,
230+
) -> Result<FileDiff> {
231+
scope_time!("get_diff_commits");
232+
233+
let repo = utils::repo(repo_path)?;
234+
let work_dir = work_dir(&repo)?;
235+
let diff =
236+
get_compare_commits_diff(&repo, (ids.0, ids.1), Some(p))?;
237+
238+
raw_diff_to_file_diff(&diff, work_dir)
239+
}
240+
225241
///
226242
//TODO: refactor into helper type with the inline closures as dedicated functions
227243
#[allow(clippy::too_many_lines)]

asyncgit/src/sync/remotes/push.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -284,7 +284,8 @@ mod tests {
284284
assert_eq!(
285285
sync::get_commit_files(
286286
tmp_repo_dir.path().to_str().unwrap(),
287-
repo_1_commit
287+
repo_1_commit,
288+
None
288289
)
289290
.unwrap()[0]
290291
.path,

asyncgit/src/sync/stash.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -231,7 +231,7 @@ mod tests {
231231

232232
let stash = get_stashes(repo_path)?[0];
233233

234-
let diff = get_commit_files(repo_path, stash)?;
234+
let diff = get_commit_files(repo_path, stash, None)?;
235235

236236
assert_eq!(diff.len(), 1);
237237

0 commit comments

Comments
 (0)