Skip to content

support discard selected lines #571

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 15 commits into from
Mar 8, 2021
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
## Unreleased

### Added
- support discarding diff by lines ([#59](https://github.com/extrawurst/gitui/issues/59))
- support for pushing tags ([#568](https://github.com/extrawurst/gitui/issues/568))

## [0.12.0] - 2020-03-03
Expand Down
2 changes: 1 addition & 1 deletion assets/vim_style_key_config.ron
Original file line number Diff line number Diff line change
Expand Up @@ -50,8 +50,8 @@
edit_file: ( code: Char('I'), modifiers: ( bits: 1,),),

status_stage_all: ( code: Char('a'), modifiers: ( bits: 0,),),

status_reset_item: ( code: Char('U'), modifiers: ( bits: 1,),),
status_reset_lines: ( code: Char('u'), modifiers: ( bits: 0,),),
status_ignore_file: ( code: Char('i'), modifiers: ( bits: 0,),),

stashing_save: ( code: Char('w'), modifiers: ( bits: 0,),),
Expand Down
5 changes: 4 additions & 1 deletion asyncgit/src/error.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use std::string::FromUtf8Error;
use std::{num::TryFromIntError, string::FromUtf8Error};
use thiserror::Error;

#[derive(Error, Debug)]
Expand Down Expand Up @@ -26,6 +26,9 @@ pub enum Error {

#[error("utf8 error:{0}")]
Utf8Error(#[from] FromUtf8Error),

#[error("TryFromInt error:{0}")]
IntError(#[from] TryFromIntError),
}

pub type Result<T> = std::result::Result<T, Error>;
Expand Down
1 change: 1 addition & 0 deletions asyncgit/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
#![forbid(missing_docs)]
#![deny(unsafe_code)]
#![deny(unused_imports)]
#![deny(unused_must_use)]
#![deny(clippy::all)]
#![deny(clippy::unwrap_used)]
#![deny(clippy::panic)]
Expand Down
3 changes: 1 addition & 2 deletions asyncgit/src/sync/branch/merge_commit.rs
Original file line number Diff line number Diff line change
Expand Up @@ -92,14 +92,13 @@ pub fn merge_upstream_commit(

#[cfg(test)]
mod test {
use super::super::merge_ff::test::write_commit_file;
use super::*;
use crate::sync::{
branch_compare_upstream,
remotes::{fetch_origin, push::push},
tests::{
debug_cmd_print, get_commit_ids, repo_clone,
repo_init_bare,
repo_init_bare, write_commit_file,
},
RepoState,
};
Expand Down
31 changes: 1 addition & 30 deletions asyncgit/src/sync/branch/merge_ff.rs
Original file line number Diff line number Diff line change
Expand Up @@ -49,41 +49,12 @@ pub fn branch_merge_upstream_fastforward(
pub mod test {
use super::*;
use crate::sync::{
commit,
remotes::{fetch_origin, push::push},
stage_add_file,
tests::{
debug_cmd_print, get_commit_ids, repo_clone,
repo_init_bare,
repo_init_bare, write_commit_file,
},
CommitId,
};
use git2::Repository;
use std::{fs::File, io::Write, path::Path};

// write, stage and commit a file
pub fn write_commit_file(
repo: &Repository,
file: &str,
content: &str,
commit_name: &str,
) -> CommitId {
File::create(
repo.workdir().unwrap().join(file).to_str().unwrap(),
)
.unwrap()
.write_all(content.as_bytes())
.unwrap();

stage_add_file(
repo.workdir().unwrap().to_str().unwrap(),
Path::new(file),
)
.unwrap();

commit(repo.workdir().unwrap().to_str().unwrap(), commit_name)
.unwrap()
}

#[test]
fn test_merge_fastforward() {
Expand Down
42 changes: 37 additions & 5 deletions asyncgit/src/sync/diff.rs
Original file line number Diff line number Diff line change
Expand Up @@ -39,14 +39,41 @@ pub struct DiffLine {
pub content: String,
///
pub line_type: DiffLineType,
///
pub position: DiffLinePosition,
}

///
#[derive(Clone, Copy, Default, Hash, Debug, PartialEq, Eq)]
pub struct DiffLinePosition {
///
pub old_lineno: Option<u32>,
///
pub new_lineno: Option<u32>,
}

impl PartialEq<&git2::DiffLine<'_>> for DiffLinePosition {
fn eq(&self, other: &&git2::DiffLine) -> bool {
other.new_lineno() == self.new_lineno
&& other.old_lineno() == self.old_lineno
}
}

impl From<&git2::DiffLine<'_>> for DiffLinePosition {
fn from(line: &git2::DiffLine<'_>) -> Self {
Self {
old_lineno: line.old_lineno(),
new_lineno: line.new_lineno(),
}
}
}

#[derive(Debug, Default, Clone, Copy, PartialEq, Hash)]
pub(crate) struct HunkHeader {
old_start: u32,
old_lines: u32,
new_start: u32,
new_lines: u32,
pub old_start: u32,
pub old_lines: u32,
pub new_start: u32,
pub new_lines: u32,
}

impl From<DiffHunk<'_>> for HunkHeader {
Expand Down Expand Up @@ -89,10 +116,14 @@ pub(crate) fn get_diff_raw<'a>(
p: &str,
stage: bool,
reverse: bool,
context: Option<u32>,
) -> Result<Diff<'a>> {
// scope_time!("get_diff_raw");

let mut opt = DiffOptions::new();
if let Some(context) = context {
opt.context_lines(context);
}
opt.pathspec(p);
opt.reverse(reverse);

Expand Down Expand Up @@ -133,7 +164,7 @@ pub fn get_diff(

let repo = utils::repo(repo_path)?;
let work_dir = work_dir(&repo)?;
let diff = get_diff_raw(&repo, &p, stage, false)?;
let diff = get_diff_raw(&repo, &p, stage, false, None)?;

raw_diff_to_file_diff(&diff, work_dir)
}
Expand Down Expand Up @@ -209,6 +240,7 @@ fn raw_diff_to_file_diff<'a>(
};

let diff_line = DiffLine {
position: DiffLinePosition::from(&line),
content: String::from_utf8_lossy(line.content())
.to_string(),
line_type,
Expand Down
11 changes: 6 additions & 5 deletions asyncgit/src/sync/hunks.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ pub fn stage_hunk(

let repo = repo(repo_path)?;

let diff = get_diff_raw(&repo, &file_path, false, false)?;
let diff = get_diff_raw(&repo, &file_path, false, false, None)?;

let mut opt = ApplyOptions::new();
opt.hunk_callback(|hunk| {
Expand All @@ -46,7 +46,7 @@ pub fn reset_hunk(

let repo = repo(repo_path)?;

let diff = get_diff_raw(&repo, &file_path, false, false)?;
let diff = get_diff_raw(&repo, &file_path, false, false, None)?;

let hunk_index = find_hunk_index(&diff, hunk_hash);
if let Some(hunk_index) = hunk_index {
Expand All @@ -58,7 +58,8 @@ pub fn reset_hunk(
res
});

let diff = get_diff_raw(&repo, &file_path, false, true)?;
let diff =
get_diff_raw(&repo, &file_path, false, true, None)?;

repo.apply(&diff, ApplyLocation::WorkDir, Some(&mut opt))?;

Expand Down Expand Up @@ -104,7 +105,7 @@ pub fn unstage_hunk(

let repo = repo(repo_path)?;

let diff = get_diff_raw(&repo, &file_path, true, false)?;
let diff = get_diff_raw(&repo, &file_path, true, false, None)?;
let diff_count_positive = diff.deltas().len();

let hunk_index = find_hunk_index(&diff, hunk_hash);
Expand All @@ -113,7 +114,7 @@ pub fn unstage_hunk(
Ok,
)?;

let diff = get_diff_raw(&repo, &file_path, true, true)?;
let diff = get_diff_raw(&repo, &file_path, true, true, None)?;

if diff.deltas().len() != diff_count_positive {
return Err(Error::Generic(format!(
Expand Down
26 changes: 25 additions & 1 deletion asyncgit/src/sync/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,10 @@ mod hooks;
mod hunks;
mod ignore;
mod logwalker;
mod patches;
pub mod remotes;
mod reset;
mod staging;
mod stash;
mod state;
pub mod status;
Expand Down Expand Up @@ -47,6 +49,7 @@ pub use remotes::{
tags::PushTagsProgress,
};
pub use reset::{reset_stage, reset_workdir};
pub use staging::discard_lines;
pub use stash::{get_stashes, stash_apply, stash_drop, stash_save};
pub use state::{repo_state, RepoState};
pub use tags::{get_tags, CommitTags, Tags};
Expand All @@ -58,12 +61,14 @@ pub use utils::{
#[cfg(test)]
mod tests {
use super::{
commit, stage_add_file,
staging::repo_write_file,
status::{get_status, StatusType},
CommitId, LogWalker,
};
use crate::error::Result;
use git2::Repository;
use std::process::Command;
use std::{path::Path, process::Command};
use tempfile::TempDir;

/// Calling `set_search_path` with an empty directory makes sure that there
Expand All @@ -88,6 +93,25 @@ mod tests {
});
}

/// 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();

stage_add_file(
repo.workdir().unwrap().to_str().unwrap(),
Path::new(file),
)
.unwrap();

commit(repo.workdir().unwrap().to_str().unwrap(), commit_name)
.unwrap()
}

///
pub fn repo_init_empty() -> Result<(TempDir, Repository)> {
sandbox_config_files();
Expand Down
74 changes: 74 additions & 0 deletions asyncgit/src/sync/patches.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
use super::diff::{get_diff_raw, HunkHeader};
use crate::error::{Error, Result};
use git2::{Diff, DiffLine, Patch, Repository};

//
pub(crate) struct HunkLines<'a> {
pub hunk: HunkHeader,
pub lines: Vec<DiffLine<'a>>,
}

///
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")));
}

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))
}

//
fn patch_get_hunklines<'a>(
patch: &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_lines = patch.num_lines_in_hunk(hunk_idx)?;

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);
}

res.push(hunk);
}

Ok(res)
}

//
fn get_patches<'a>(diff: &Diff<'a>) -> Result<Vec<Patch<'a>>> {
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);
}
}

Ok(res)
}
Loading