mirror of
https://github.com/gitbutlerapp/gitbutler.git
synced 2024-08-16 15:50:37 +03:00
move conflicts module to branches crate
This commit is contained in:
parent
0924b506d7
commit
6dcbb8bddb
@ -22,6 +22,7 @@ git2-hooks = "0.3"
|
||||
url = { version = "2.5.2", features = ["serde"] }
|
||||
md5 = "0.7.0"
|
||||
futures = "0.3"
|
||||
itertools = "0.13"
|
||||
|
||||
[[test]]
|
||||
name="branches"
|
||||
@ -35,4 +36,3 @@ gitbutler-git = { workspace = true, features = ["test-askpass-path"] }
|
||||
glob = "0.3.1"
|
||||
serial_test = "3.1.1"
|
||||
tempfile = "3.10"
|
||||
itertools = "0.13"
|
||||
|
@ -8,6 +8,7 @@ use serde::Serialize;
|
||||
|
||||
use super::r#virtual as vb;
|
||||
use super::r#virtual::convert_to_real_branch;
|
||||
use crate::conflicts::RepoConflicts;
|
||||
use crate::integration::{get_workspace_head, update_gitbutler_integration};
|
||||
use crate::remote::{commit_to_remote_commit, RemoteCommit};
|
||||
use crate::VirtualBranchHunk;
|
||||
|
167
crates/gitbutler-branch/src/conflicts.rs
Normal file
167
crates/gitbutler-branch/src/conflicts.rs
Normal file
@ -0,0 +1,167 @@
|
||||
// stuff to manage merge conflict state
|
||||
// this is the dumbest possible way to do this, but it is a placeholder
|
||||
// conflicts are stored one path per line in .git/conflicts
|
||||
// merge parent is stored in .git/base_merge_parent
|
||||
// conflicts are removed as they are resolved, the conflicts file is removed when there are no more conflicts
|
||||
// the merge parent file is removed when the merge is complete
|
||||
|
||||
use std::{
|
||||
io::{BufRead, Write},
|
||||
path::{Path, PathBuf},
|
||||
};
|
||||
|
||||
use anyhow::{anyhow, Context, Result};
|
||||
use itertools::Itertools;
|
||||
|
||||
use gitbutler_core::{error::Marker, project_repository::ProjectRepo};
|
||||
|
||||
pub fn mark<P: AsRef<Path>, A: AsRef<[P]>>(
|
||||
repository: &ProjectRepo,
|
||||
paths: A,
|
||||
parent: Option<git2::Oid>,
|
||||
) -> Result<()> {
|
||||
let paths = paths.as_ref();
|
||||
if paths.is_empty() {
|
||||
return Ok(());
|
||||
}
|
||||
let conflicts_path = repository.repo().path().join("conflicts");
|
||||
// write all the file paths to a file on disk
|
||||
let mut file = std::fs::File::create(conflicts_path)?;
|
||||
for path in paths {
|
||||
file.write_all(path.as_ref().as_os_str().as_encoded_bytes())?;
|
||||
file.write_all(b"\n")?;
|
||||
}
|
||||
|
||||
if let Some(parent) = parent {
|
||||
let merge_path = repository.repo().path().join("base_merge_parent");
|
||||
// write all the file paths to a file on disk
|
||||
let mut file = std::fs::File::create(merge_path)?;
|
||||
file.write_all(parent.to_string().as_bytes())?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn merge_parent(repository: &ProjectRepo) -> Result<Option<git2::Oid>> {
|
||||
let merge_path = repository.repo().path().join("base_merge_parent");
|
||||
if !merge_path.exists() {
|
||||
return Ok(None);
|
||||
}
|
||||
|
||||
let file = std::fs::File::open(merge_path)?;
|
||||
let reader = std::io::BufReader::new(file);
|
||||
let mut lines = reader.lines();
|
||||
if let Some(parent) = lines.next() {
|
||||
let parent = parent?;
|
||||
let parent: git2::Oid = parent.parse()?;
|
||||
Ok(Some(parent))
|
||||
} else {
|
||||
Ok(None)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn resolve<P: AsRef<Path>>(repository: &ProjectRepo, path: P) -> Result<()> {
|
||||
let path = path.as_ref();
|
||||
let conflicts_path = repository.repo().path().join("conflicts");
|
||||
let file = std::fs::File::open(conflicts_path.clone())?;
|
||||
let reader = std::io::BufReader::new(file);
|
||||
let mut remaining = Vec::new();
|
||||
for line in reader.lines().map_ok(PathBuf::from) {
|
||||
let line = line?;
|
||||
if line != path {
|
||||
remaining.push(line);
|
||||
}
|
||||
}
|
||||
|
||||
// remove file
|
||||
std::fs::remove_file(conflicts_path)?;
|
||||
|
||||
// re-write file if needed
|
||||
if !remaining.is_empty() {
|
||||
mark(repository, &remaining, None)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn conflicting_files(repository: &ProjectRepo) -> Result<Vec<String>> {
|
||||
let conflicts_path = repository.repo().path().join("conflicts");
|
||||
if !conflicts_path.exists() {
|
||||
return Ok(vec![]);
|
||||
}
|
||||
|
||||
let file = std::fs::File::open(conflicts_path)?;
|
||||
let reader = std::io::BufReader::new(file);
|
||||
Ok(reader.lines().map_while(Result::ok).collect())
|
||||
}
|
||||
|
||||
/// Check if `path` is conflicting in `repository`, or if `None`, check if there is any conflict.
|
||||
// TODO(ST): Should this not rather check the conflicting state in the index?
|
||||
pub fn is_conflicting(repository: &ProjectRepo, path: Option<&Path>) -> Result<bool> {
|
||||
let conflicts_path = repository.repo().path().join("conflicts");
|
||||
if !conflicts_path.exists() {
|
||||
return Ok(false);
|
||||
}
|
||||
|
||||
let file = std::fs::File::open(conflicts_path)?;
|
||||
let reader = std::io::BufReader::new(file);
|
||||
// TODO(ST): This shouldn't work on UTF8 strings.
|
||||
let mut files = reader.lines().map_ok(PathBuf::from);
|
||||
if let Some(pathname) = path {
|
||||
// check if pathname is one of the lines in conflicts_path file
|
||||
for line in files {
|
||||
let line = line?;
|
||||
|
||||
if line == pathname {
|
||||
return Ok(true);
|
||||
}
|
||||
}
|
||||
Ok(false)
|
||||
} else {
|
||||
Ok(files.next().transpose().map(|x| x.is_some())?)
|
||||
}
|
||||
}
|
||||
|
||||
// is this project still in a resolving conflict state?
|
||||
// - could be that there are no more conflicts, but the state is not committed
|
||||
pub fn is_resolving(repository: &ProjectRepo) -> bool {
|
||||
repository.repo().path().join("base_merge_parent").exists()
|
||||
}
|
||||
|
||||
pub fn clear(repository: &ProjectRepo) -> Result<()> {
|
||||
let merge_path = repository.repo().path().join("base_merge_parent");
|
||||
std::fs::remove_file(merge_path)?;
|
||||
|
||||
for file in conflicting_files(repository)? {
|
||||
resolve(repository, &file)?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub trait RepoConflicts {
|
||||
fn assure_unconflicted(&self) -> Result<()>;
|
||||
fn assure_resolved(&self) -> Result<()>;
|
||||
fn is_resolving(&self) -> bool;
|
||||
}
|
||||
|
||||
impl RepoConflicts for ProjectRepo {
|
||||
fn is_resolving(&self) -> bool {
|
||||
is_resolving(self)
|
||||
}
|
||||
|
||||
fn assure_resolved(&self) -> Result<()> {
|
||||
if self.is_resolving() {
|
||||
Err(anyhow!("project has active conflicts")).context(Marker::ProjectConflict)
|
||||
} else {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
fn assure_unconflicted(&self) -> Result<()> {
|
||||
if is_conflicting(self, None)? {
|
||||
Err(anyhow!("project has active conflicts")).context(Marker::ProjectConflict)
|
||||
} else {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
}
|
@ -13,10 +13,12 @@ use gitbutler_core::virtual_branches::{
|
||||
};
|
||||
use gitbutler_core::{
|
||||
git::CommitExt,
|
||||
project_repository::{self, conflicts, LogUntil},
|
||||
project_repository::{self, LogUntil},
|
||||
virtual_branches::branch::BranchCreateRequest,
|
||||
};
|
||||
|
||||
use crate::conflicts;
|
||||
|
||||
const WORKSPACE_HEAD: &str = "Workspace Head";
|
||||
|
||||
pub fn get_integration_commiter<'a>() -> Result<git2::Signature<'a>> {
|
||||
|
@ -14,3 +14,5 @@ pub mod integration;
|
||||
pub mod files;
|
||||
|
||||
pub mod remote;
|
||||
|
||||
pub mod conflicts;
|
||||
|
@ -21,6 +21,7 @@ use gitbutler_core::virtual_branches::Author;
|
||||
use hex::ToHex;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use crate::conflicts::{self, RepoConflicts};
|
||||
use crate::integration::{get_integration_commiter, get_workspace_head};
|
||||
use crate::remote::{branch_to_remote_branch, RemoteBranch};
|
||||
use gitbutler_core::error::Code;
|
||||
@ -46,7 +47,7 @@ use gitbutler_core::{
|
||||
diff::{self},
|
||||
Refname, RemoteRefname,
|
||||
},
|
||||
project_repository::{self, conflicts, LogUntil},
|
||||
project_repository::{self, LogUntil},
|
||||
};
|
||||
|
||||
type AppliedStatuses = Vec<(branch::Branch, BranchStatus)>;
|
||||
|
@ -1,5 +1,5 @@
|
||||
mod config;
|
||||
pub mod conflicts;
|
||||
// pub mod conflicts;
|
||||
mod repository;
|
||||
|
||||
pub use config::Config;
|
||||
|
@ -6,7 +6,8 @@ use std::{
|
||||
|
||||
use anyhow::{anyhow, Context, Result};
|
||||
|
||||
use super::conflicts;
|
||||
// use super::conflicts;
|
||||
use crate::git::RepositoryExt;
|
||||
use crate::{
|
||||
askpass,
|
||||
git::{self, Url},
|
||||
@ -15,7 +16,6 @@ use crate::{
|
||||
virtual_branches::{Branch, BranchId},
|
||||
};
|
||||
use crate::{error::Code, git::CommitHeadersV2};
|
||||
use crate::{error::Marker, git::RepositoryExt};
|
||||
|
||||
pub struct ProjectRepo {
|
||||
git_repository: git2::Repository,
|
||||
@ -140,32 +140,9 @@ pub trait RepoActions {
|
||||
fn git_index_size(&self) -> Result<usize>;
|
||||
fn config(&self) -> super::Config;
|
||||
fn path(&self) -> &path::Path;
|
||||
fn assure_unconflicted(&self) -> Result<()>;
|
||||
fn assure_resolved(&self) -> Result<()>;
|
||||
fn is_resolving(&self) -> bool;
|
||||
}
|
||||
|
||||
impl RepoActions for ProjectRepo {
|
||||
fn is_resolving(&self) -> bool {
|
||||
conflicts::is_resolving(self)
|
||||
}
|
||||
|
||||
fn assure_resolved(&self) -> Result<()> {
|
||||
if self.is_resolving() {
|
||||
Err(anyhow!("project has active conflicts")).context(Marker::ProjectConflict)
|
||||
} else {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
fn assure_unconflicted(&self) -> Result<()> {
|
||||
if conflicts::is_conflicting(self, None)? {
|
||||
Err(anyhow!("project has active conflicts")).context(Marker::ProjectConflict)
|
||||
} else {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
fn path(&self) -> &path::Path {
|
||||
path::Path::new(&self.project.path)
|
||||
}
|
||||
|
@ -1,7 +1,8 @@
|
||||
use anyhow::{Context, Result};
|
||||
use gitbutler_branch::conflicts;
|
||||
use gitbutler_core::{
|
||||
git,
|
||||
project_repository::{self, conflicts, RepoActions},
|
||||
project_repository::{self, RepoActions},
|
||||
projects::{self, ProjectId},
|
||||
virtual_branches::BranchId,
|
||||
};
|
||||
|
Loading…
Reference in New Issue
Block a user