Skip to content

Commit 02ce8bf

Browse files
authored
Merge pull request #44 from aganzha/rc-0.1.21
Rc 0.1.21
2 parents 5b5c23a + fd62562 commit 02ce8bf

File tree

15 files changed

+902
-472
lines changed

15 files changed

+902
-472
lines changed

run.sh

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,8 @@
11
#!/bin/bash
2-
clear && RUST_BACKTRACE=1 RUST_LOG=debug OUT_DIR=. cargo run $@
2+
clear
3+
export RUST_BACKTRACE=1
4+
export RUST_LOG=debug
5+
export RUSTFLAGS='-A deprecated'
6+
export OUT_DIR=.
7+
cargo run $@
38

src/commit_view.rs

Lines changed: 110 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -3,13 +3,13 @@
33
// SPDX-License-Identifier: GPL-3.0-or-later
44

55
use crate::dialogs::alert;
6-
use crate::git::commit;
6+
use crate::git::{blame, commit, stash::StashNum};
77
use crate::status_view::context::StatusRenderContext;
88
use crate::status_view::{
99
render::ViewContainer, stage_view::StageView, view::View, CursorPosition,
1010
Label as TextViewLabel,
1111
};
12-
use crate::{ApplyOp, CurrentWindow, Event, StageOp};
12+
use crate::{ApplyOp, BlameLine, CurrentWindow, Event, HunkLineNo, StageOp};
1313
use async_channel::Sender;
1414
use git2::Oid;
1515

@@ -23,7 +23,11 @@ use log::{debug, info, trace};
2323

2424
use std::path::PathBuf;
2525

26-
pub fn headerbar_factory(sender: Sender<Event>, oid: Oid, stash_num: Option<usize>) -> HeaderBar {
26+
pub fn headerbar_factory(
27+
sender: Sender<Event>,
28+
oid: Oid,
29+
stash_num: Option<StashNum>,
30+
) -> HeaderBar {
2731
let hb = HeaderBar::builder().build();
2832
let (btn_tooltip, title) = if stash_num.is_some() {
2933
("Apply stash", "Stash")
@@ -178,6 +182,7 @@ impl commit::CommitDiff {
178182
ctx: &mut StatusRenderContext<'a>,
179183
labels: &'a mut [TextViewLabel],
180184
body_label: &'a mut MultiLineLabel,
185+
blame_line: Option<BlameLine>,
181186
) {
182187
let buffer = txt.buffer();
183188
let mut iter = buffer.iter_at_offset(0);
@@ -193,14 +198,50 @@ impl commit::CommitDiff {
193198
// ??? why it was commented out?
194199
// body_label.update_content(&self.message, txt.calc_max_char_width());
195200
body_label.render(&buffer, &mut iter, ctx);
196-
201+
let mut found_line_index: Option<(usize, usize, usize)> = None;
197202
if !self.diff.files.is_empty() {
198-
self.diff.files[0].view.make_current(true);
203+
if let Some(blame_line) = blame_line {
204+
for (f, file) in self.diff.files.iter().enumerate() {
205+
if file.path == blame_line.file_path {
206+
file.view.expand(true);
207+
for (h, hunk) in file.hunks.iter().enumerate() {
208+
let mut found = false;
209+
if found_line_index.is_none() {
210+
for (l, line) in hunk.lines.iter().enumerate() {
211+
if let Some(found_line_no) = line.new_line_no {
212+
if found_line_no >= blame_line.hunk_start
213+
&& line.content(hunk) == blame_line.content
214+
{
215+
line.view.make_current(true);
216+
found = true;
217+
found_line_index.replace((f, h, l));
218+
break;
219+
}
220+
}
221+
}
222+
}
223+
if !found {
224+
hunk.view.expand(false);
225+
}
226+
}
227+
if found_line_index.is_some() {
228+
break;
229+
}
230+
}
231+
}
232+
} else {
233+
self.diff.files[0].view.make_current(true);
234+
}
199235
}
200236

201237
self.diff.render(&buffer, &mut iter, ctx);
202-
203-
if !self.diff.files.is_empty() {
238+
if let Some((f, h, l)) = found_line_index {
239+
let line_no = self.diff.files[f].hunks[h].lines[l].view.line_no.get();
240+
let buffer = txt.buffer();
241+
iter = buffer.iter_at_line(line_no).unwrap();
242+
buffer.place_cursor(&iter);
243+
txt.scroll_to_iter(&mut iter, 0.0, false, 0.0, 0.0);
244+
} else if !self.diff.files.is_empty() {
204245
let buffer = txt.buffer();
205246
iter = buffer
206247
.iter_at_line(self.diff.files[0].view.line_no.get())
@@ -215,7 +256,8 @@ impl commit::CommitDiff {
215256
pub fn show_commit_window(
216257
repo_path: PathBuf,
217258
oid: Oid,
218-
stash_num: Option<usize>,
259+
stash_num: Option<StashNum>,
260+
blame_line: Option<BlameLine>,
219261
app_window: CurrentWindow,
220262
main_sender: Sender<Event>, // i need that to trigger revert and cherry-pick.
221263
) -> Window {
@@ -228,7 +270,7 @@ pub fn show_commit_window(
228270
let mut builder = Window::builder()
229271
.default_width(MAX_WIDTH)
230272
.default_height(960);
231-
match app_window {
273+
match app_window.clone() {
232274
CurrentWindow::Window(w) => {
233275
builder = builder.transient_for(&w);
234276
}
@@ -270,14 +312,14 @@ pub fn show_commit_window(
270312
let mut body_label: Option<MultiLineLabel> = None;
271313

272314
let path = repo_path.clone();
273-
274315
let mut cursor_position: CursorPosition = CursorPosition::None;
275316

276317
glib::spawn_future_local({
277318
let window = window.clone();
278319
let sender = sender.clone();
320+
let path = path.clone();
279321
async move {
280-
let diff = gio::spawn_blocking(move || commit::get_commit_diff(path, oid))
322+
let diff = gio::spawn_blocking(move || commit::get_commit_diff(path.clone(), oid))
281323
.await
282324
.unwrap_or_else(|e| {
283325
alert(format!("{:?}", e)).present(Some(&window));
@@ -300,6 +342,7 @@ pub fn show_commit_window(
300342
];
301343

302344
glib::spawn_future_local({
345+
let window = window.clone();
303346
async move {
304347
while let Ok(event) = receiver.recv().await {
305348
let mut ctx = crate::StatusRenderContext::new(&txt);
@@ -324,6 +367,7 @@ pub fn show_commit_window(
324367
&mut ctx,
325368
&mut labels,
326369
body_label.as_mut().unwrap(),
370+
blame_line.clone(),
327371
);
328372
cursor_position = CursorPosition::from_context(&ctx);
329373
// it should be called after cursor in ViewContainer
@@ -356,12 +400,6 @@ pub fn show_commit_window(
356400
Event::TextViewResize(w) => {
357401
info!("TextViewResize {} {:?}", w, ctx);
358402
}
359-
Event::TextCharVisibleWidth(w) => {
360-
info!("TextCharVisibleWidth {}", w);
361-
if let Some(d) = &mut diff {
362-
d.render(&txt, &mut ctx, &mut labels, body_label.as_mut().unwrap());
363-
}
364-
}
365403
Event::Debug => {
366404
let buffer = txt.buffer();
367405
let pos = buffer.cursor_position();
@@ -410,6 +448,61 @@ pub fn show_commit_window(
410448
.expect("cant send through channel");
411449
}
412450
}
451+
Event::Blame => {
452+
let mut line_no: Option<HunkLineNo> = None;
453+
let mut ofile_path: Option<PathBuf> = None;
454+
let mut oline_content: Option<String> = None;
455+
if let CursorPosition::CursorLine(_, file_idx, hunk_idx, line_idx) =
456+
cursor_position
457+
{
458+
if let Some(diff) = &diff {
459+
let file = &diff.diff.files[file_idx];
460+
let hunk = &file.hunks[hunk_idx];
461+
ofile_path.replace(file.path.clone());
462+
let line = &hunk.lines[line_idx];
463+
oline_content.replace(line.content(hunk).to_string());
464+
// IMPORTANT - here we use new_line_no
465+
line_no = line.new_line_no;
466+
}
467+
}
468+
if let Some(line_no) = line_no {
469+
glib::spawn_future_local({
470+
let path = path.clone();
471+
let sender = main_sender.clone();
472+
let file_path = ofile_path.clone().unwrap();
473+
let window = window.clone();
474+
async move {
475+
let ooid = gio::spawn_blocking({
476+
let file_path = file_path.clone();
477+
move || blame(path, file_path.clone(), line_no, Some(oid))
478+
})
479+
.await
480+
.unwrap();
481+
match ooid {
482+
Ok((blame_oid, hunk_line_start)) => {
483+
if blame_oid == oid {
484+
alert(format!("This is the same commit {:?}", oid))
485+
.present(Some(&window));
486+
return;
487+
}
488+
sender
489+
.send_blocking(crate::Event::ShowOid(
490+
blame_oid,
491+
None,
492+
Some(BlameLine {
493+
file_path,
494+
hunk_start: hunk_line_start,
495+
content: oline_content.unwrap(),
496+
}),
497+
))
498+
.expect("Could not send through channel");
499+
}
500+
Err(e) => alert(e).present(Some(&window)),
501+
}
502+
}
503+
});
504+
}
505+
}
413506
_ => {
414507
trace!("unhandled event in commit_view {:?}", event);
415508
}

src/git.rs

Lines changed: 26 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@ use crate::commit::CommitRepr;
1616
use crate::gio;
1717
use crate::status_view::view::View;
1818
use crate::syntax;
19+
20+
use anyhow::{Context, Result};
1921
use async_channel::Sender;
2022

2123
use chrono::{DateTime, FixedOffset};
@@ -52,12 +54,12 @@ impl HunkLineNo {
5254
pub fn new(num: u32) -> Self {
5355
Self(num)
5456
}
55-
pub fn as_u32(&self) -> u32 {
56-
self.0
57-
}
5857
pub fn as_i32(&self) -> i32 {
5958
self.0 as i32
6059
}
60+
pub fn as_usize(&self) -> usize {
61+
self.0 as usize
62+
}
6163
}
6264
impl FromStr for HunkLineNo {
6365
type Err = ParseIntError;
@@ -699,9 +701,7 @@ pub fn get_current_repo_status(
699701
}
700702
});
701703

702-
// get staged
703704
gio::spawn_blocking({
704-
// get_staged
705705
let sender = sender.clone();
706706
let path = path.clone();
707707
move || {
@@ -719,7 +719,6 @@ pub fn get_current_repo_status(
719719
}
720720
});
721721

722-
// get untracked
723722
gio::spawn_blocking({
724723
let sender = sender.clone();
725724
let path = path.clone();
@@ -1256,3 +1255,24 @@ pub fn rebase(
12561255
}
12571256
Ok(true)
12581257
}
1258+
1259+
pub fn blame(
1260+
path: PathBuf,
1261+
file_path: PathBuf,
1262+
line_no: HunkLineNo,
1263+
start_oid: Option<Oid>,
1264+
) -> Result<(git2::Oid, HunkLineNo)> {
1265+
let repo = git2::Repository::open(path.clone())?;
1266+
let mut opts = git2::BlameOptions::new();
1267+
if let Some(oid) = start_oid {
1268+
opts.newest_commit(oid);
1269+
}
1270+
let blame = repo.blame_file(&file_path, Some(&mut opts))?;
1271+
let blame_hunk = blame
1272+
.get_line(line_no.as_usize())
1273+
.context("Can`t get line to blame")?;
1274+
Ok((
1275+
blame_hunk.orig_commit_id(),
1276+
HunkLineNo(blame_hunk.orig_start_line() as u32),
1277+
))
1278+
}

src/git/commit.rs

Lines changed: 17 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -198,7 +198,7 @@ pub fn create(
198198
repo.commit(Some("HEAD"), &me, &me, &message, &tree, &[])?;
199199
}
200200

201-
// update staged changes
201+
// update staged changes.
202202
let ob = repo.revparse_single("HEAD^{tree}")?;
203203
let current_tree = repo.find_tree(ob.id())?;
204204
let git_diff =
@@ -313,39 +313,37 @@ pub fn partial_apply(
313313
sender: Sender<crate::Event>,
314314
) -> Result<(), git2::Error> {
315315
let _defer = DeferRefresh::new(path.clone(), sender.clone(), true, true);
316-
316+
info!(
317+
"partial apply {:?} {:?} {:?}",
318+
file_path, hunk_header, revert
319+
);
317320
let repo = git2::Repository::open(path.clone())?;
318321

319322
sender
320323
.send_blocking(crate::Event::LockMonitors(true))
321324
.expect("Could not send through channel");
322325

323326
let commit = repo.find_commit(oid)?;
324-
let tree = commit.tree()?;
325-
326-
let ob = repo.revparse_single("HEAD^{tree}")?;
327-
let head_tree = repo.find_tree(ob.id())?;
328327

328+
let head_ref = repo.head()?;
329+
let ob = head_ref.peel(git2::ObjectType::Commit)?;
330+
let our_commit = ob.peel_to_commit()?;
331+
let memory_index = if revert {
332+
repo.revert_commit(&commit, &our_commit, 0, None)?
333+
} else {
334+
repo.cherrypick_commit(&commit, &our_commit, 0, None)?
335+
};
329336
let mut diff_opts = make_diff_options();
330-
let git_diff = repo.diff_tree_to_tree(
331-
Some(&head_tree),
332-
Some(&tree),
333-
Some(if revert {
334-
diff_opts.reverse(true)
335-
} else {
336-
&mut diff_opts
337-
}),
338-
)?;
337+
diff_opts.reverse(true);
338+
339+
let git_diff = repo.diff_index_to_workdir(Some(&memory_index), Some(&mut diff_opts))?;
340+
339341
let mut options = git2::ApplyOptions::new();
340342

341343
options.hunk_callback(|odh| -> bool {
342344
if let Some(hunk_header) = &hunk_header {
343345
if let Some(dh) = odh {
344346
let mut header = Hunk::get_header_from(&dh);
345-
println!(
346-
"-----------------------> MINE: {:?} REAL: {:?}",
347-
hunk_header, header
348-
);
349347
if revert {
350348
header = Hunk::reverse_header(&header);
351349
}

0 commit comments

Comments
 (0)