Allow hunk locking to multiple commits

- adds `locked_to` field to `GitHunk` to avoid looking it up again
- sends array to ui instead of single commit id
This commit is contained in:
Mattias Granlund 2024-04-23 19:46:41 +02:00
parent a664c85a9a
commit c202d83b6b
5 changed files with 32 additions and 59 deletions

View File

@ -1,3 +1,7 @@
export function isDefined<T>(file: T | undefined): file is T { export function isDefined<T>(file: T | undefined | null): file is T {
return file !== undefined; return file !== undefined;
} }
export function notNull<T>(file: T | undefined | null): file is T {
return file !== null;
}

View File

@ -1,6 +1,7 @@
import 'reflect-metadata'; import 'reflect-metadata';
import { splitMessage } from '$lib/utils/commitMessage'; import { splitMessage } from '$lib/utils/commitMessage';
import { hashCode } from '$lib/utils/string'; import { hashCode } from '$lib/utils/string';
import { isDefined, notNull } from '$lib/utils/typeguards';
import { Type, Transform } from 'class-transformer'; import { Type, Transform } from 'class-transformer';
export type ChangeType = export type ChangeType =
@ -21,7 +22,7 @@ export class Hunk {
filePath!: string; filePath!: string;
hash?: string; hash?: string;
locked!: boolean; locked!: boolean;
lockedTo!: string | undefined; lockedTo!: string[] | undefined;
changeType!: ChangeType; changeType!: ChangeType;
} }
@ -58,14 +59,15 @@ export class LocalFile {
get locked(): boolean { get locked(): boolean {
return this.hunks return this.hunks
? this.hunks.map((hunk) => hunk.lockedTo).reduce((a, b) => !!(a || b), false) ? this.hunks.map((hunk) => hunk.locked).reduce((a, b) => !!(a || b), false)
: false; : false;
} }
get lockedIds(): string[] { get lockedIds(): string[] {
return this.hunks return this.hunks
.map((hunk) => hunk.lockedTo) .flatMap((hunk) => hunk.lockedTo)
.filter((lockedTo): lockedTo is string => !!lockedTo); .filter(notNull)
.filter(isDefined);
} }
} }

View File

@ -53,6 +53,7 @@ pub struct GitHunk {
#[serde(rename = "diff", serialize_with = "crate::serde::as_string_lossy")] #[serde(rename = "diff", serialize_with = "crate::serde::as_string_lossy")]
pub diff_lines: BString, pub diff_lines: BString,
pub binary: bool, pub binary: bool,
pub locked_to: Box<[git::Oid]>,
pub change_type: ChangeType, pub change_type: ChangeType,
} }
@ -69,6 +70,7 @@ impl GitHunk {
diff_lines: hex_id.into(), diff_lines: hex_id.into(),
binary: true, binary: true,
change_type, change_type,
locked_to: Box::new([]),
} }
} }
@ -82,6 +84,7 @@ impl GitHunk {
diff_lines: Default::default(), diff_lines: Default::default(),
binary: false, binary: false,
change_type: ChangeType::Modified, change_type: ChangeType::Modified,
locked_to: Box::new([]),
} }
} }
} }
@ -91,6 +94,11 @@ impl GitHunk {
pub fn contains(&self, line: u32) -> bool { pub fn contains(&self, line: u32) -> bool {
self.new_start <= line && self.new_start + self.new_lines >= line self.new_start <= line && self.new_start + self.new_lines >= line
} }
pub fn with_locks(mut self, locks: &[git::Oid]) -> Self {
self.locked_to = locks.to_owned().into();
self
}
} }
#[derive(Debug, PartialEq, Clone, Serialize, Default)] #[derive(Debug, PartialEq, Clone, Serialize, Default)]
@ -298,6 +306,7 @@ fn hunks_by_filepath(repo: Option<&Repository>, diff: &git2::Diff) -> Result<Dif
diff_lines: line.into_owned(), diff_lines: line.into_owned(),
binary: false, binary: false,
change_type, change_type,
locked_to: Box::new([]),
} }
} }
LineOrHexHash::HexHashOfBinaryBlob(id) => { LineOrHexHash::HexHashOfBinaryBlob(id) => {
@ -404,6 +413,7 @@ pub fn reverse_hunk(hunk: &GitHunk) -> Option<GitHunk> {
diff_lines: diff, diff_lines: diff,
binary: hunk.binary, binary: hunk.binary,
change_type: hunk.change_type, change_type: hunk.change_type,
locked_to: Box::new([]),
}) })
} }
} }

View File

@ -23,7 +23,7 @@ impl From<&diff::GitHunk> for Hunk {
end: hunk.new_start + hunk.new_lines, end: hunk.new_start + hunk.new_lines,
hash: Some(Hunk::hash_diff(hunk.diff_lines.as_ref())), hash: Some(Hunk::hash_diff(hunk.diff_lines.as_ref())),
timestamp_ms: None, timestamp_ms: None,
locked_to: vec![], locked_to: hunk.locked_to.to_vec(),
} }
} }
} }

View File

@ -142,7 +142,7 @@ pub struct VirtualBranchHunk {
pub end: u32, pub end: u32,
pub binary: bool, pub binary: bool,
pub locked: bool, pub locked: bool,
pub locked_to: Option<git::Oid>, pub locked_to: Option<Box<[git::Oid]>>,
pub change_type: diff::ChangeType, pub change_type: diff::ChangeType,
} }
@ -164,8 +164,8 @@ impl VirtualBranchHunk {
end: hunk.new_start + hunk.new_lines, end: hunk.new_start + hunk.new_lines,
binary: hunk.binary, binary: hunk.binary,
hash: Hunk::hash_diff(hunk.diff_lines.as_ref()), hash: Hunk::hash_diff(hunk.diff_lines.as_ref()),
locked: false, locked: hunk.locked_to.len() > 0,
locked_to: None, locked_to: Some(hunk.locked_to.clone()),
change_type: hunk.change_type, change_type: hunk.change_type,
} }
} }
@ -937,9 +937,7 @@ pub fn list_virtual_branches(
branches.push(branch); branches.push(branch);
} }
let branches = branches_with_large_files_abridged(branches); let mut branches = branches_with_large_files_abridged(branches);
let mut branches = branches_with_hunk_locks(branches, project_repository)?;
branches.sort_by(|a, b| a.order.cmp(&b.order)); branches.sort_by(|a, b| a.order.cmp(&b.order));
Ok((branches, skipped_files)) Ok((branches, skipped_files))
@ -960,51 +958,6 @@ fn branches_with_large_files_abridged(mut branches: Vec<VirtualBranch>) -> Vec<V
branches branches
} }
fn branches_with_hunk_locks(
mut branches: Vec<VirtualBranch>,
project_repository: &project_repository::Repository,
) -> Result<Vec<VirtualBranch>> {
let all_commits: Vec<VirtualBranchCommit> = branches
.clone()
.iter()
.filter(|branch| branch.active)
.flat_map(|vbranch| vbranch.commits.clone())
.collect();
for commit in all_commits {
let commit = project_repository.git_repository.find_commit(commit.id)?;
let parent = commit.parent(0).context("failed to get parent commit")?;
let commit_tree = commit.tree().context("failed to get commit tree")?;
let parent_tree = parent.tree().context("failed to get parent tree")?;
let commited_file_diffs = diff::trees(
&project_repository.git_repository,
&parent_tree,
&commit_tree,
)?;
for branch in &mut branches {
for file in &mut branch.files {
for hunk in &mut file.hunks {
let locked = commited_file_diffs.get(&file.path).map_or(false, |file| {
file.hunks.iter().any(|committed_hunk| {
joined(
committed_hunk.new_start,
committed_hunk.new_start + committed_hunk.new_lines,
hunk.start,
hunk.end,
)
})
});
if locked {
hunk.locked = true;
hunk.locked_to = Some(commit.id());
}
}
}
}
}
Ok(branches)
}
fn joined(start_a: u32, end_a: u32, start_b: u32, end_b: u32) -> bool { fn joined(start_a: u32, end_a: u32, start_b: u32, end_b: u32) -> bool {
((start_a >= start_b && start_a <= end_b) || (end_a >= start_b && end_a <= end_b)) ((start_a >= start_b && start_a <= end_b) || (end_a >= start_b && end_a <= end_b))
|| ((start_b >= start_a && start_b <= end_a) || (end_b >= start_a && end_b <= end_a)) || ((start_b >= start_a && start_b <= end_a) || (end_b >= start_a && end_b <= end_a))
@ -1906,7 +1859,7 @@ fn get_applied_status(
end: git_diff_hunk.new_start + git_diff_hunk.new_lines, end: git_diff_hunk.new_start + git_diff_hunk.new_lines,
timestamp_ms: Some(mtime), timestamp_ms: Some(mtime),
hash: Some(hash), hash: Some(hash),
locked_to: vec![], locked_to: git_diff_hunk.locked_to.to_vec(),
}; };
git_diff_hunks.remove(i); git_diff_hunks.remove(i);
return Some(updated_hunk); return Some(updated_hunk);
@ -1977,12 +1930,16 @@ fn get_applied_status(
.with_hash(Hunk::hash_diff(hunk.diff_lines.as_ref()))], .with_hash(Hunk::hash_diff(hunk.diff_lines.as_ref()))],
}); });
let hunk = match locked_to {
Some(locks) => hunk.with_locks(locks),
_ => hunk,
};
diffs_by_branch diffs_by_branch
.entry(virtual_branches[vbranch_pos].id) .entry(virtual_branches[vbranch_pos].id)
.or_default() .or_default()
.entry(filepath.clone()) .entry(filepath.clone())
.or_default() .or_default()
.push(hunk.clone()); .push(hunk);
} }
} }