use branch::Name for virtual_branches

This commit is contained in:
Nikita Galaiko 2023-07-19 10:18:17 +02:00
parent e458bfd077
commit 2b5e981e28
18 changed files with 382 additions and 196 deletions

View File

@ -365,7 +365,7 @@ impl App {
pub async fn create_virtual_branch_from_branch(
&self,
project_id: &str,
branch: &str,
branch: &project_repository::branch::Name,
) -> Result<String> {
let gb_repository = self.gb_repository(project_id)?;
let project = self.gb_project(project_id)?;

View File

@ -607,11 +607,11 @@ async fn create_virtual_branch(
async fn create_virtual_branch_from_branch(
handle: tauri::AppHandle,
project_id: &str,
branch: &str,
branch: branch::Name,
) -> Result<String, Error> {
let app = handle.state::<app::App>();
let branch_id = app
.create_virtual_branch_from_branch(project_id, branch)
.create_virtual_branch_from_branch(project_id, &branch)
.await
.context("failed to create virtual branch from branch")?;
Ok(branch_id)

View File

@ -1,113 +0,0 @@
use std::fmt;
use serde::Serialize;
#[derive(Debug)]
pub struct RemoteName {
// contains name of the remote, e.x. "origin" or "upstream"
remote: String,
// contains name of the branch, e.x. "master" or "main"
branch: String,
}
impl fmt::Display for RemoteName {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}/{}", self.remote, self.branch)
}
}
impl Serialize for RemoteName {
fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
serializer.serialize_str(&self.to_string())
}
}
impl TryFrom<&git2::Branch<'_>> for RemoteName {
type Error = git2::Error;
fn try_from(value: &git2::Branch<'_>) -> std::result::Result<Self, Self::Error> {
if !value.get().is_remote() {
return Err(git2::Error::from_str("not a remote branch"));
}
let name = String::from_utf8(value.name_bytes()?.to_vec())
.map_err(|e| git2::Error::from_str(&e.to_string()))?;
if let Some((remote, branch)) = name.split_once('/') {
Ok(Self {
remote: remote.to_string(),
branch: branch.to_string(),
})
} else {
Err(git2::Error::from_str("invalid remote branch name"))
}
}
}
#[derive(Debug)]
pub struct LocalName {
// contains name of the branch, e.x. "master" or "main"
branch: String,
// contains name of the remote branch, if the local branch is tracking a remote branch
remote: Option<RemoteName>,
}
impl Serialize for LocalName {
fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
serializer.serialize_str(&self.branch)
}
}
impl fmt::Display for LocalName {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.branch)
}
}
impl TryFrom<&git2::Branch<'_>> for LocalName {
type Error = git2::Error;
fn try_from(value: &git2::Branch<'_>) -> std::result::Result<Self, Self::Error> {
if value.get().is_remote() {
return Err(git2::Error::from_str("not a local branch"));
}
let name = String::from_utf8(value.name_bytes()?.to_vec())
.map_err(|e| git2::Error::from_str(&e.to_string()))?;
match value.upstream() {
Ok(upstream) => {
let remote_branch_name = RemoteName::try_from(&upstream)?;
Ok(Self {
branch: name,
remote: Some(remote_branch_name),
})
}
Err(error) => {
if error.code() == git2::ErrorCode::NotFound {
Ok(Self {
branch: name,
remote: None,
})
} else {
Err(error)
}
}
}
}
}
#[derive(Debug)]
pub enum Name {
Remote(RemoteName),
Local(LocalName),
}
impl TryFrom<&git2::Branch<'_>> for Name {
type Error = git2::Error;
fn try_from(value: &git2::Branch<'_>) -> std::result::Result<Self, Self::Error> {
if value.get().is_remote() {
Ok(Self::Remote(RemoteName::try_from(value)?))
} else {
Ok(Self::Local(LocalName::try_from(value)?))
}
}
}

View File

@ -0,0 +1,3 @@
mod names;
pub use names::*;

View File

@ -0,0 +1,13 @@
#[derive(Debug, thiserror::Error)]
pub enum Error {
#[error("branch name is invalid")]
InvalidName(String),
#[error("branch is not local")]
NotLocal(String),
#[error("branch is not remote")]
NotRemote(String),
#[error(transparent)]
GitError(#[from] git2::Error),
#[error(transparent)]
Utf8Error(#[from] std::string::FromUtf8Error),
}

View File

@ -0,0 +1,85 @@
use std::fmt;
use serde::{Deserialize, Serialize};
use super::{error::Error, remote};
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct Name {
// contains name of the branch, e.x. "master" or "main"
branch: String,
// contains name of the remote branch, if the local branch is tracking a remote branch
remote: Option<remote::Name>,
}
impl Name {
pub fn branch(&self) -> &str {
&self.branch
}
}
impl Serialize for Name {
fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
serializer.serialize_str(&self.branch)
}
}
impl<'d> Deserialize<'d> for Name {
fn deserialize<D: serde::Deserializer<'d>>(deserializer: D) -> Result<Self, D::Error> {
let name = String::deserialize(deserializer)?;
Self::try_from(name.as_str()).map_err(serde::de::Error::custom)
}
}
impl fmt::Display for Name {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "refs/heads/{}", self.branch)
}
}
impl TryFrom<&str> for Name {
type Error = Error;
fn try_from(value: &str) -> Result<Self, Self::Error> {
if !value.starts_with("refs/heads/") {
return Err(Error::NotLocal(value.to_string()));
}
if let Some(branch) = value.strip_prefix("refs/heads/") {
Ok(Self {
branch: branch.to_string(),
remote: None,
})
} else {
Err(Error::InvalidName(value.to_string()))
}
}
}
impl TryFrom<&git2::Branch<'_>> for Name {
type Error = Error;
fn try_from(value: &git2::Branch<'_>) -> std::result::Result<Self, Self::Error> {
let branch = String::from_utf8(value.name_bytes()?.to_vec()).map_err(Error::Utf8Error)?;
if value.get().is_remote() {
Err(Error::NotLocal(branch))
} else {
match value.upstream() {
Ok(upstream) => Ok(Self {
branch,
remote: Some(remote::Name::try_from(&upstream)?),
}),
Err(error) => {
if error.code() == git2::ErrorCode::NotFound {
Ok(Self {
branch,
remote: None,
})
} else {
Err(error.into())
}
}
}
}
}
}

View File

@ -0,0 +1,81 @@
mod error;
mod local;
mod remote;
use std::fmt;
use serde::{Deserialize, Serialize};
pub use error::Error;
pub use local::Name as LocalName;
pub use remote::Name as RemoteName;
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub enum Name {
Remote(RemoteName),
Local(LocalName),
}
impl Name {
pub fn branch(&self) -> &str {
match self {
Self::Remote(remote) => remote.branch(),
Self::Local(local) => local.branch(),
}
}
}
impl TryFrom<&str> for Name {
type Error = Error;
fn try_from(value: &str) -> std::result::Result<Self, Self::Error> {
if value.starts_with("refs") {
if value.starts_with("refs/remotes/") {
Ok(Self::Remote(RemoteName::try_from(value)?))
} else if value.starts_with("refs/heads/") {
Ok(Self::Local(LocalName::try_from(value)?))
} else {
Err(Error::InvalidName(value.to_string()))
}
} else {
Ok(Self::Local(LocalName::try_from(value)?))
}
}
}
impl TryFrom<&git2::Branch<'_>> for Name {
type Error = Error;
fn try_from(value: &git2::Branch<'_>) -> std::result::Result<Self, Self::Error> {
if value.get().is_remote() {
Ok(Self::Remote(RemoteName::try_from(value)?))
} else {
Ok(Self::Local(LocalName::try_from(value)?))
}
}
}
impl fmt::Display for Name {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::Remote(remote) => remote.fmt(f),
Self::Local(local) => local.fmt(f),
}
}
}
impl Serialize for Name {
fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
match self {
Self::Remote(remote) => remote.serialize(serializer),
Self::Local(local) => local.serialize(serializer),
}
}
}
impl<'d> Deserialize<'d> for Name {
fn deserialize<D: serde::Deserializer<'d>>(deserializer: D) -> Result<Self, D::Error> {
let name = String::deserialize(deserializer)?;
Self::try_from(name.as_str()).map_err(serde::de::Error::custom)
}
}

View File

@ -0,0 +1,77 @@
use std::fmt;
use serde::{Deserialize, Serialize};
use super::error::Error;
#[derive(Debug, Clone, Hash, PartialEq, Eq)]
pub struct Name {
// contains name of the remote, e.x. "origin" or "upstream"
remote: String,
// contains name of the branch, e.x. "master" or "main"
branch: String,
}
impl Name {
pub fn branch(&self) -> &str {
&self.branch
}
pub fn remote(&self) -> &str {
&self.remote
}
}
impl fmt::Display for Name {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "refs/remotes/{}/{}", self.remote, self.branch)
}
}
impl Serialize for Name {
fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
serializer.serialize_str(&self.to_string())
}
}
impl<'d> Deserialize<'d> for Name {
fn deserialize<D: serde::Deserializer<'d>>(deserializer: D) -> Result<Self, D::Error> {
let name = String::deserialize(deserializer)?;
Self::try_from(name.as_str()).map_err(serde::de::Error::custom)
}
}
impl TryFrom<&str> for Name {
type Error = Error;
fn try_from(value: &str) -> std::result::Result<Self, Self::Error> {
if !value.starts_with("refs/remotes/") {
return Err(Error::NotRemote(value.to_string()));
};
if let Some((remote, branch)) = value.strip_prefix("refs/remotes/").unwrap().split_once('/')
{
Ok(Self {
remote: remote.to_string(),
branch: branch.to_string(),
})
} else {
Err(Error::InvalidName(value.to_string()))
}
}
}
impl TryFrom<&git2::Branch<'_>> for Name {
type Error = Error;
fn try_from(value: &git2::Branch<'_>) -> std::result::Result<Self, Self::Error> {
let refname =
String::from_utf8(value.get().name_bytes().to_vec()).map_err(Error::Utf8Error)?;
if !value.get().is_remote() {
return Err(Error::NotRemote(refname));
}
Self::try_from(refname.as_str())
}
}

View File

@ -357,10 +357,18 @@ impl<'repository> Repository<'repository> {
allowed_types.get()
}
pub fn push(&self, head: &git2::Oid, upstream: &str) -> Result<(), Error> {
pub fn push(&self, head: &git2::Oid, branch: &branch::Name) -> Result<(), Error> {
let upstream = match branch {
branch::Name::Local(_) => {
// TODO: no need to actually push to local branch
return Ok(());
}
branch::Name::Remote(upstream) => upstream,
};
let mut remote = self
.git_repository
.find_remote("origin")
.find_remote(upstream.remote())
.context("failed to find remote")
.map_err(Error::Other)?;
@ -372,8 +380,8 @@ impl<'repository> Repository<'repository> {
let output = Command::new("git")
.arg("push")
.arg("origin")
.arg(format!("{}:{}", head, upstream))
.arg(upstream.remote())
.arg(format!("{}:{}", head, upstream.to_string()))
.current_dir(&self.project.path)
.output()
.context("failed to fork exec")

View File

@ -14,6 +14,8 @@ use serde::{Deserialize, Serialize};
use anyhow::Result;
use crate::project_repository::branch;
// this is the struct for the virtual branch data that is stored in our data
// store. it is more or less equivalent to a git branch reference, but it is not
// stored or accessible from the git repository itself. it is stored in our
@ -23,7 +25,7 @@ pub struct Branch {
pub id: String,
pub name: String,
pub applied: bool,
pub upstream: Option<String>,
pub upstream: Option<branch::Name>,
pub created_timestamp_ms: u128,
pub updated_timestamp_ms: u128,
// tree is the last git tree written to a session, or merge base tree if this is new. use this for delta calculation from the session data
@ -92,18 +94,19 @@ impl TryFrom<&dyn crate::reader::Reader> for Branch {
if upstream.is_empty() {
Ok(None)
} else {
Ok(Some(upstream))
branch::Name::try_from(upstream.as_str())
.map(Some)
.map_err(|e| {
crate::reader::Error::IOError(std::io::Error::new(
std::io::ErrorKind::Other,
format!("meta/upstream: {}", e),
))
})
}
}
Err(crate::reader::Error::NotFound) => Ok(None),
Err(e) => Err(e),
}
.map_err(|e| {
crate::reader::Error::IOError(std::io::Error::new(
std::io::ErrorKind::Other,
format!("meta/upstream: {}", e),
))
})?;
}?;
let tree = reader.read_string("meta/tree").map_err(|e| {
crate::reader::Error::IOError(std::io::Error::new(

View File

@ -48,7 +48,12 @@ mod tests {
name: format!("branch_name_{}", unsafe { TEST_INDEX }),
applied: true,
order: unsafe { TEST_INDEX },
upstream: Some(format!("upstream_{}", unsafe { TEST_INDEX })),
upstream: Some(
format!("refs/remotes/origin/upstream_{}", unsafe { TEST_INDEX })
.as_str()
.try_into()
.unwrap(),
),
created_timestamp_ms: unsafe { TEST_INDEX } as u128,
updated_timestamp_ms: unsafe { TEST_INDEX + 100 } as u128,
head: git2::Oid::from_str(&format!(

View File

@ -62,7 +62,10 @@ impl<'writer> BranchWriter<'writer> {
.context("Failed to write branch applied")?;
if let Some(upstream) = &branch.upstream {
self.writer
.write_string(&format!("branches/{}/meta/upstream", branch.id), upstream)
.write_string(
&format!("branches/{}/meta/upstream", branch.id),
&upstream.to_string(),
)
.context("Failed to write branch upstream")?;
};
self.writer
@ -121,7 +124,12 @@ mod tests {
id: format!("branch_{}", unsafe { TEST_INDEX }),
name: format!("branch_name_{}", unsafe { TEST_INDEX }),
applied: true,
upstream: Some(format!("upstream_{}", unsafe { TEST_INDEX })),
upstream: Some(
format!("refs/remotes/origin/upstream_{}", unsafe { TEST_INDEX })
.as_str()
.try_into()
.unwrap(),
),
created_timestamp_ms: unsafe { TEST_INDEX } as u128,
updated_timestamp_ms: unsafe { TEST_INDEX + 100 } as u128,
head: git2::Oid::from_str(&format!(
@ -194,7 +202,7 @@ mod tests {
assert_eq!(
fs::read_to_string(root.join("meta").join("upstream").to_str().unwrap())
.context("Failed to read branch upstream")?,
branch.upstream.clone().unwrap()
branch.upstream.clone().unwrap().to_string()
);
assert_eq!(
fs::read_to_string(
@ -269,7 +277,7 @@ mod tests {
let updated_branch = Branch {
name: "updated_name".to_string(),
applied: false,
upstream: Some("updated_upstream".to_string()),
upstream: Some("refs/remotes/origin/upstream_updated".try_into().unwrap()),
created_timestamp_ms: 2,
updated_timestamp_ms: 3,
ownership: branch::Ownership { files: vec![] },
@ -294,7 +302,7 @@ mod tests {
assert_eq!(
fs::read_to_string(root.join("meta").join("upstream").to_str().unwrap())
.context("Failed to read branch upstream")?,
updated_branch.upstream.unwrap()
updated_branch.upstream.unwrap().to_string()
);
assert_eq!(
fs::read_to_string(

View File

@ -79,7 +79,12 @@ mod tests {
id: format!("branch_{}", unsafe { TEST_INDEX }),
name: format!("branch_name_{}", unsafe { TEST_INDEX }),
applied: true,
upstream: Some(format!("upstream_{}", unsafe { TEST_INDEX })),
upstream: Some(
format!("refs/remotes/origin/upstream_{}", unsafe { TEST_INDEX })
.as_str()
.try_into()
.unwrap(),
),
created_timestamp_ms: unsafe { TEST_INDEX } as u128,
updated_timestamp_ms: unsafe { TEST_INDEX + 100 } as u128,
head: git2::Oid::from_str(&format!(

View File

@ -44,7 +44,7 @@ pub struct VirtualBranch {
pub merge_conflicts: Vec<String>,
pub conflicted: bool,
pub order: usize,
pub upstream: Option<String>,
pub upstream: Option<project_repository::branch::Name>,
pub base_current: bool, // is this vbranch based on the current base branch?
}
@ -122,7 +122,7 @@ pub struct RemoteBranch {
pub first_commit_ts: u128,
pub ahead: u32,
pub behind: u32,
pub upstream: Option<String>,
pub upstream: Option<project_repository::branch::RemoteName>,
pub authors: Vec<Author>,
pub mergeable: bool,
pub merge_conflicts: Vec<String>,
@ -458,7 +458,8 @@ pub fn remote_branches(
.collect::<Result<Vec<branch::Branch>, reader::Error>>()
.context("failed to read virtual branches")?
.into_iter()
.filter_map(|branch| branch.upstream.map(|u| u.replace("refs/heads/", "")))
.filter_map(|branch| branch.upstream)
.map(|upstream| upstream.branch().to_string())
.collect::<HashSet<_>>();
let mut most_recent_branches_by_hash: HashMap<git2::Oid, (git2::Branch, u64)> = HashMap::new();
@ -481,20 +482,17 @@ pub fn remote_branches(
continue;
}
let branch_name = branch
.name()
.context("could not get branch name")?
.context("could not get branch name")?
.to_string();
let branch_name = branch_name.replace("origin/", "");
let branch_name = project_repository::branch::Name::try_from(&branch);
println!("branch_name: {:?}", branch_name);
let branch_name = branch_name.context("could not get branch name")?;
if virtual_branches_names.contains(&branch_name) {
if virtual_branches_names.contains(branch_name.branch()) {
continue;
}
if branch_name == "HEAD" {
if branch_name.branch().eq("HEAD") {
continue;
}
if branch_name == "gitbutler/integration" {
if branch_name.branch().eq("gitbutler/integration") {
continue;
}
@ -539,7 +537,6 @@ pub fn remote_branches(
let mut branches: Vec<RemoteBranch> = Vec::new();
for branch in &top_branches {
let branch_name = branch.get().name().context("could not get branch name")?;
let upstream_branch = branch.upstream();
match branch.get().target() {
Some(branch_oid) => {
// get the branch ref
@ -564,12 +561,13 @@ pub fn remote_branches(
.map(Author::from)
.collect::<HashSet<_>>();
let upstream = match upstream_branch {
Ok(upstream_branch) => {
upstream_branch.get().name().map(|name| name.to_string())
}
Err(_) => None,
};
let upstream = branch
.upstream()
.ok()
.map(|upstream_branch| {
project_repository::branch::RemoteName::try_from(&upstream_branch)
})
.transpose()?;
if count_ahead > 0 {
if let Ok(base_tree) = find_base_tree(repo, &branch_commit, &target_commit) {
@ -788,8 +786,7 @@ pub fn list_virtual_branches(
}
}
if let Some(remote) = upstream_remote {
// remove "refs/heads/" from the branch name
let branch_name = branch_upstream.replace("refs/heads/", "");
let branch_name = branch_upstream.branch();
let full_branch_name =
format!("refs/remotes/{}/{}", remote.name().unwrap(), branch_name);
if let Ok(upstream_oid) = repo.refname_to_id(&full_branch_name) {
@ -880,10 +877,7 @@ pub fn list_virtual_branches(
commits,
mergeable,
merge_conflicts,
upstream: branch
.upstream
.as_ref()
.map(|u| u.replace("refs/heads/", "")),
upstream: branch.upstream.clone(),
conflicted: conflicts::is_resolving(project_repository),
base_current,
};
@ -896,13 +890,8 @@ pub fn list_virtual_branches(
pub fn create_virtual_branch_from_branch(
gb_repository: &gb_repository::Repository,
project_repository: &project_repository::Repository,
branch_ref: &str,
upstream: &project_repository::branch::Name,
) -> Result<String> {
let name = branch_ref
.replace("refs/heads/", "")
.replace("refs/remotes/", "")
.replace("origin/", ""); // TODO: get this properly
let upstream = Some(format!("refs/heads/{}", &name));
let current_session = gb_repository
.get_or_create_current_session()
.context("failed to get or create current session")?;
@ -914,7 +903,7 @@ pub fn create_virtual_branch_from_branch(
.context("no default target found")?;
let repo = &project_repository.git_repository;
let head = repo.revparse_single(branch_ref)?;
let head = repo.revparse_single(&upstream.to_string())?;
let head_commit = head.peel_to_commit()?;
let tree = head_commit.tree().context("failed to find tree")?;
@ -933,9 +922,9 @@ pub fn create_virtual_branch_from_branch(
let branch_id = Uuid::new_v4().to_string();
let mut branch = Branch {
id: branch_id.clone(),
name,
name: upstream.branch().to_string(),
applied: false,
upstream,
upstream: Some(upstream.clone()),
tree: tree.id(),
head: head_commit.id(),
created_timestamp_ms: now,
@ -2015,13 +2004,17 @@ pub fn commit(
}
Ok(())
}
fn name_to_branch(name: &str) -> String {
fn name_to_branch(name: &str) -> project_repository::branch::LocalName {
let cleaned_name = name
.chars()
.map(|c| if c.is_ascii_alphanumeric() { c } else { '-' })
.collect::<String>();
format!("refs/heads/{}", cleaned_name)
.as_str()
.try_into()
.unwrap()
}
#[derive(Debug, thiserror::Error)]
@ -2065,15 +2058,16 @@ pub fn push(
let upstream = vbranch
.upstream
.unwrap_or_else(|| name_to_branch(&vbranch.name));
.unwrap_or_else(|| project_repository::branch::Name::Local(name_to_branch(&vbranch.name)));
match project_repository.push(&vbranch.head, &upstream) {
Ok(_) => Ok(()),
Err(project_repository::Error::UnsupportedAuthCredentials(cred_type)) => {
return Err(Error::UnsupportedAuthCredentials(cred_type))
}
Err(err) => Err(Error::Other(err.into())),
}?;
project_repository
.push(&vbranch.head, &upstream)
.map_err(|err| match err {
project_repository::Error::UnsupportedAuthCredentials(cred_type) => {
Error::UnsupportedAuthCredentials(cred_type)
}
err => Error::Other(err.into()),
})?;
vbranch.upstream = Some(upstream);
branch_writer

View File

@ -54,7 +54,12 @@ mod tests {
id: format!("branch_{}", unsafe { TEST_INDEX }),
name: format!("branch_name_{}", unsafe { TEST_INDEX }),
applied: true,
upstream: Some(format!("upstream_{}", unsafe { TEST_INDEX })),
upstream: Some(
format!("refs/remotes/origin/upstream_{}", unsafe { TEST_INDEX })
.as_str()
.try_into()
.unwrap(),
),
created_timestamp_ms: unsafe { TEST_INDEX } as u128,
updated_timestamp_ms: unsafe { TEST_INDEX + 100 } as u128,
head: git2::Oid::from_str(&format!(

View File

@ -104,8 +104,13 @@ mod tests {
id: format!("branch_{}", unsafe { TEST_INDEX }),
name: format!("branch_name_{}", unsafe { TEST_INDEX }),
applied: true,
upstream: Some(format!("upstream_{}", unsafe { TEST_INDEX })),
created_timestamp_ms: unsafe { TEST_INDEX } as u128,
upstream: Some(
format!("refs/remotes/origin/upstream_{}", unsafe { TEST_INDEX })
.as_str()
.try_into()
.unwrap(),
),
updated_timestamp_ms: unsafe { TEST_INDEX + 100 } as u128,
head: git2::Oid::from_str(&format!(
"0123456789abcdef0123456789abcdef0123456{}",
@ -207,7 +212,7 @@ mod tests {
assert_eq!(
fs::read_to_string(root.join("meta").join("upstream").to_str().unwrap())
.context("Failed to read branch upstream")?,
branch.upstream.unwrap()
branch.upstream.unwrap().to_string()
);
assert_eq!(
fs::read_to_string(

View File

@ -1918,15 +1918,12 @@ fn test_detect_remote_commits() -> Result<()> {
// push the commit upstream
let branch1 = branch_reader.read(&branch1_id)?;
let up_target = branch1.head;
repository.reference(
"refs/remotes/origin/remote_branch",
up_target,
true,
"update target",
)?;
let remote_branch: project_repository::branch::Name =
"refs/remotes/origin/remote_branch".try_into().unwrap();
repository.reference(&remote_branch.to_string(), up_target, true, "update target")?;
// set the upstream reference
branch_writer.write(&Branch {
upstream: Some("remote_branch".to_string()),
upstream: Some(remote_branch),
..branch1
})?;
@ -1995,14 +1992,18 @@ fn test_create_vbranch_from_remote_branch() -> Result<()> {
"line1\nline2\nline3\nline4\nbranch\n",
)?;
commit_all(&repository)?;
let upstream: project_repository::branch::Name =
"refs/remotes/origin/branch1".try_into().unwrap();
repository.reference(
"refs/remotes/branch1",
&upstream.to_string(),
repository.head().unwrap().target().unwrap(),
true,
"update target",
)?;
repository.set_head("refs/heads/gitbutler/integration")?;
repository.set_head(&upstream.to_string())?;
repository.checkout_head(Some(&mut git2::build::CheckoutBuilder::default().force()))?;
// reset the first file
@ -2021,8 +2022,7 @@ fn test_create_vbranch_from_remote_branch() -> Result<()> {
assert_eq!(branches[0].files.len(), 0);
// create a new virtual branch from the remote branch
let branch2_id =
create_virtual_branch_from_branch(&gb_repo, &project_repository, "refs/remotes/branch1")?;
let branch2_id = create_virtual_branch_from_branch(&gb_repo, &project_repository, &upstream)?;
// shouldn't be anything on either of our branches
let branches = list_virtual_branches(&gb_repo, &project_repository, true)?;
@ -2157,8 +2157,10 @@ fn test_create_vbranch_from_behind_remote_branch() -> Result<()> {
commit_all(&repository)?;
let remote_commit = repository.head().unwrap().target().unwrap();
let remote_branch: project_repository::branch::Name =
"refs/remotes/origin/branch1".try_into().unwrap();
repository.reference(
"refs/remotes/origin/branch1",
&remote_branch.to_string(),
remote_commit,
true,
"update target",
@ -2181,7 +2183,7 @@ fn test_create_vbranch_from_behind_remote_branch() -> Result<()> {
let branch1_id = create_virtual_branch_from_branch(
&gb_repo,
&project_repository,
"refs/remotes/origin/branch1",
&remote_branch,
)?;
let branches = list_virtual_branches(&gb_repo, &project_repository, true)?;

View File

@ -34,7 +34,12 @@ fn test_branch() -> virtual_branches::branch::Branch {
id: format!("branch_{}", unsafe { TEST_INDEX }),
name: format!("branch_name_{}", unsafe { TEST_INDEX }),
applied: true,
upstream: Some(format!("upstream_{}", unsafe { TEST_INDEX })),
upstream: Some(
format!("refs/remotes/origin/upstream_{}", unsafe { TEST_INDEX })
.as_str()
.try_into()
.unwrap(),
),
created_timestamp_ms: unsafe { TEST_INDEX } as u128,
updated_timestamp_ms: unsafe { TEST_INDEX + 100 } as u128,
head: git2::Oid::from_str(&format!(