store target upstream name separately

This commit is contained in:
Nikita Galaiko 2023-07-11 13:49:31 +02:00
parent 82939782eb
commit 9ba26fc5b0
9 changed files with 238 additions and 102 deletions

View File

@ -563,9 +563,9 @@ impl Repository {
// lookup a branch by name
let branch = repo.find_branch(target_branch, git2::BranchType::Remote)?;
let remote = repo.branch_remote_name(branch.get().name().unwrap())?;
let remote_url = repo.find_remote(remote.as_str().unwrap())?;
let remote_url_str = remote_url.url().unwrap();
let remote_name = repo.branch_remote_name(branch.get().name().unwrap())?;
let remote = repo.find_remote(remote_name.as_str().unwrap())?;
let remote_url = remote.url().unwrap();
// get a list of currently active virtual branches
@ -604,8 +604,9 @@ impl Repository {
}
let target = virtual_branches::target::Target {
name: branch.name().unwrap().unwrap().to_string(),
remote: remote_url_str.to_string(),
branch_name: branch.name()?.unwrap().to_string(),
remote_name: remote.name().unwrap().to_string(),
remote_url: remote_url.to_string(),
sha: commit_oid,
behind: 0,
};

View File

@ -101,8 +101,9 @@ mod tests {
fn test_target() -> target::Target {
target::Target {
name: format!("target_name_{}", unsafe { TEST_TARGET_INDEX }),
remote: format!("remote_{}", unsafe { TEST_TARGET_INDEX }),
branch_name: format!("branch name{}", unsafe { TEST_TARGET_INDEX }),
remote_name: format!("remote name {}", unsafe { TEST_TARGET_INDEX }),
remote_url: format!("remote url {}", unsafe { TEST_TARGET_INDEX }),
sha: git2::Oid::from_str(&format!(
"0123456789abcdef0123456789abcdef0123456{}",
unsafe { TEST_TARGET_INDEX }

View File

@ -752,7 +752,6 @@ pub fn list_virtual_branches(
let mut upstream_commit = None;
if !branch.upstream.is_empty() {
// get the target remote
let remote_url = &default_target.remote;
let remotes = repo.remotes()?;
let mut upstream_remote = None;
for remote_name in remotes.iter() {
@ -767,7 +766,7 @@ pub fn list_virtual_branches(
None => continue,
};
if url == remote_url {
if url == default_target.remote_url {
upstream_remote = Some(remote);
break;
}
@ -1564,12 +1563,12 @@ pub fn update_branch_target(
let repo = &project_repository.git_repository;
let branch = repo
.find_branch(&target.name, git2::BranchType::Remote)
.context(format!("failed to find branch {}", target.name))?;
let new_target_commit = branch
.get()
.peel_to_commit()
.context(format!("failed to peel branch {} to commit", target.name))?;
.find_branch(&target.branch_name, git2::BranchType::Remote)
.context(format!("failed to find branch {}", target.branch_name))?;
let new_target_commit = branch.get().peel_to_commit().context(format!(
"failed to peel branch {} to commit",
target.branch_name
))?;
let new_target_oid = new_target_commit.id();
//
// if the target has not changed, do nothing
@ -1938,13 +1937,13 @@ pub fn get_target_data(
Some(mut target) => {
let repo = &project_repository.git_repository;
let branch = repo
.find_branch(&target.name, git2::BranchType::Remote)
.find_branch(&target.branch_name, git2::BranchType::Remote)
.unwrap();
let commit = branch.get().peel_to_commit().unwrap();
let oid = commit.id();
target.behind = project_repository
.behind(oid, target.sha)
.context(format!("failed to get behind for {}", target.name))?;
.context(format!("failed to get behind for {}", target.branch_name))?;
Ok(Some(target))
}
}
@ -2026,8 +2025,9 @@ mod tests {
let project_repository = project_repository::Repository::open(&project)?;
target::Writer::new(&gb_repo).write_default(&target::Target {
name: "origin".to_string(),
remote: "origin".to_string(),
remote_name: "origin".to_string(),
branch_name: "master".to_string(),
remote_url: "origin".to_string(),
sha: repository.head().unwrap().target().unwrap(),
behind: 0,
})?;
@ -2093,8 +2093,9 @@ mod tests {
let project_repository = project_repository::Repository::open(&project)?;
target::Writer::new(&gb_repo).write_default(&target::Target {
name: "origin".to_string(),
remote: "origin".to_string(),
remote_name: "origin".to_string(),
branch_name: "master".to_string(),
remote_url: "origin".to_string(),
sha: repository.head().unwrap().target().unwrap(),
behind: 0,
})?;
@ -2153,8 +2154,9 @@ mod tests {
gb_repository::Repository::open(gb_repo_path, project.id, project_store, user_store)?;
target::Writer::new(&gb_repo).write_default(&target::Target {
name: "origin".to_string(),
remote: "origin".to_string(),
branch_name: "master".to_string(),
remote_name: "origin".to_string(),
remote_url: "origin".to_string(),
sha: repository.head().unwrap().target().unwrap(),
behind: 0,
})?;
@ -2200,8 +2202,9 @@ mod tests {
gb_repository::Repository::open(gb_repo_path, project.id, project_store, user_store)?;
target::Writer::new(&gb_repo).write_default(&target::Target {
name: "origin".to_string(),
remote: "origin".to_string(),
branch_name: "master".to_string(),
remote_name: "origin".to_string(),
remote_url: "origin".to_string(),
sha: repository.head().unwrap().target().unwrap(),
behind: 0,
})?;
@ -2242,8 +2245,9 @@ mod tests {
let project_repository = project_repository::Repository::open(&project)?;
target::Writer::new(&gb_repo).write_default(&target::Target {
name: "origin".to_string(),
remote: "origin".to_string(),
branch_name: "master".to_string(),
remote_name: "origin".to_string(),
remote_url: "origin".to_string(),
sha: repository.head().unwrap().target().unwrap(),
behind: 0,
})?;
@ -2329,8 +2333,9 @@ mod tests {
let project_repository = project_repository::Repository::open(&project)?;
target::Writer::new(&gb_repo).write_default(&target::Target {
name: "origin".to_string(),
remote: "origin".to_string(),
branch_name: "master".to_string(),
remote_name: "origin".to_string(),
remote_url: "origin".to_string(),
sha: repository.head().unwrap().target().unwrap(),
behind: 0,
})?;
@ -2388,8 +2393,9 @@ mod tests {
let project_repository = project_repository::Repository::open(&project)?;
target::Writer::new(&gb_repo).write_default(&target::Target {
name: "origin".to_string(),
remote: "origin".to_string(),
branch_name: "master".to_string(),
remote_name: "origin".to_string(),
remote_url: "origin".to_string(),
sha: repository.head().unwrap().target().unwrap(),
behind: 0,
})?;
@ -2509,8 +2515,9 @@ mod tests {
let project_repository = project_repository::Repository::open(&project)?;
target::Writer::new(&gb_repo).write_default(&target::Target {
name: "origin".to_string(),
remote: "origin".to_string(),
branch_name: "master".to_string(),
remote_name: "origin".to_string(),
remote_url: "origin".to_string(),
sha: repository.head().unwrap().target().unwrap(),
behind: 0,
})?;
@ -2624,8 +2631,9 @@ mod tests {
let project_repository = project_repository::Repository::open(&project)?;
target::Writer::new(&gb_repo).write_default(&target::Target {
name: "origin".to_string(),
remote: "origin".to_string(),
branch_name: "master".to_string(),
remote_name: "origin".to_string(),
remote_url: "origin".to_string(),
sha: repository.head().unwrap().target().unwrap(),
behind: 0,
})?;
@ -2719,8 +2727,9 @@ mod tests {
let project_repository = project_repository::Repository::open(&project)?;
target::Writer::new(&gb_repo).write_default(&target::Target {
name: "origin".to_string(),
remote: "origin".to_string(),
branch_name: "master".to_string(),
remote_name: "origin".to_string(),
remote_url: "origin".to_string(),
sha: repository.head().unwrap().target().unwrap(),
behind: 0,
})?;
@ -2784,8 +2793,9 @@ mod tests {
let project_repository = project_repository::Repository::open(&project)?;
target::Writer::new(&gb_repo).write_default(&target::Target {
name: "origin/master".to_string(),
remote: "origin".to_string(),
branch_name: "origin/master".to_string(),
remote_name: "origin".to_string(),
remote_url: "origin".to_string(),
sha: repository.head().unwrap().target().unwrap(),
behind: 0,
})?;
@ -2891,8 +2901,9 @@ mod tests {
let project_repository = project_repository::Repository::open(&project)?;
target::Writer::new(&gb_repo).write_default(&target::Target {
name: "origin/master".to_string(),
remote: "origin".to_string(),
branch_name: "origin/master".to_string(),
remote_name: "origin".to_string(),
remote_url: "origin".to_string(),
sha: repository.head().unwrap().target().unwrap(),
behind: 0,
})?;
@ -2970,8 +2981,9 @@ mod tests {
let project_repository = project_repository::Repository::open(&project)?;
target::Writer::new(&gb_repo).write_default(&target::Target {
name: "origin/master".to_string(),
remote: "origin".to_string(),
branch_name: "origin/master".to_string(),
remote_name: "origin".to_string(),
remote_url: "origin".to_string(),
sha: repository.head().unwrap().target().unwrap(),
behind: 0,
})?;
@ -3055,8 +3067,9 @@ mod tests {
commit_all(&repository)?;
target::Writer::new(&gb_repo).write_default(&target::Target {
name: "origin/master".to_string(),
remote: "origin".to_string(),
branch_name: "origin/master".to_string(),
remote_name: "origin".to_string(),
remote_url: "origin".to_string(),
sha: repository.head().unwrap().target().unwrap(),
behind: 0,
})?;
@ -3160,8 +3173,9 @@ mod tests {
commit_all(&repository)?;
target::Writer::new(&gb_repo).write_default(&target::Target {
name: "origin/master".to_string(),
remote: "origin".to_string(),
branch_name: "master".to_string(),
remote_name: "origin".to_string(),
remote_url: "origin".to_string(),
sha: repository.head().unwrap().target().unwrap(),
behind: 0,
})?;
@ -3249,8 +3263,9 @@ mod tests {
commit_all(&repository)?;
target::Writer::new(&gb_repo).write_default(&target::Target {
name: "origin/master".to_string(),
remote: "origin".to_string(),
branch_name: "master".to_string(),
remote_name: "origin".to_string(),
remote_url: "origin".to_string(),
sha: repository.head().unwrap().target().unwrap(),
behind: 0,
})?;
@ -3419,8 +3434,9 @@ mod tests {
commit_all(&repository)?;
target::Writer::new(&gb_repo).write_default(&target::Target {
name: "origin/master".to_string(),
remote: "http://origin.com/project".to_string(),
branch_name: "master".to_string(),
remote_name: "origin".to_string(),
remote_url: "http://origin.com/project".to_string(),
sha: repository.head().unwrap().target().unwrap(),
behind: 0,
})?;
@ -3529,8 +3545,9 @@ mod tests {
commit_all(&repository)?;
target::Writer::new(&gb_repo).write_default(&target::Target {
name: "origin/master".to_string(),
remote: "http://origin.com/project".to_string(),
branch_name: "master".to_string(),
remote_name: "origin".to_string(),
remote_url: "http://origin.com/project".to_string(),
sha: repository.head().unwrap().target().unwrap(),
behind: 0,
})?;
@ -3681,8 +3698,9 @@ mod tests {
)?;
target::Writer::new(&gb_repo).write_default(&target::Target {
name: "origin/master".to_string(),
remote: "http://origin.com/project".to_string(),
remote_name: "origin".to_string(),
branch_name: "master".to_string(),
remote_url: "http://origin.com/project".to_string(),
sha: upstream_commit,
behind: 0,
})?;
@ -3785,8 +3803,9 @@ mod tests {
commit_all(&repository)?;
target::Writer::new(&gb_repo).write_default(&target::Target {
name: "origin".to_string(),
remote: "origin".to_string(),
branch_name: "origin".to_string(),
remote_name: "origin".to_string(),
remote_url: "origin".to_string(),
sha: repository.head().unwrap().target().unwrap(),
behind: 0,
})?;
@ -3941,8 +3960,9 @@ mod tests {
let commit1_oid = repository.head().unwrap().target().unwrap();
let commit1 = repository.find_commit(commit1_oid).unwrap();
target::Writer::new(&gb_repo).write_default(&target::Target {
name: "origin".to_string(),
remote: "origin".to_string(),
branch_name: "master".to_string(),
remote_name: "origin".to_string(),
remote_url: "origin".to_string(),
sha: commit1_oid,
behind: 0,
})?;

View File

@ -8,8 +8,9 @@ pub use writer::TargetWriter as Writer;
#[derive(Debug, PartialEq, Clone)]
pub struct Target {
pub name: String,
pub remote: String,
pub branch_name: String,
pub remote_name: String,
pub remote_url: String,
pub sha: git2::Oid,
pub behind: u32,
}
@ -19,26 +20,56 @@ impl Serialize for Target {
where
S: Serializer,
{
let mut state = serializer.serialize_struct("Target", 3)?;
state.serialize_field("name", &self.name)?;
state.serialize_field("remote", &self.remote)?;
let mut state = serializer.serialize_struct("Target", 5)?;
state.serialize_field("branchName", &self.branch_name)?;
state.serialize_field("remoteName", &self.remote_name)?;
state.serialize_field("remoteUrl", &self.remote_url)?;
state.serialize_field("behind", &self.behind)?;
state.serialize_field("sha", &self.sha.to_string())?;
state.end()
}
}
// this is a backwards compatibile with the old format
fn read_remote_url(reader: &dyn crate::reader::Reader) -> Result<String, crate::reader::Error> {
match reader.read_string("remote_url") {
Ok(url) => Ok(url),
// fallback to the old format
Err(crate::reader::Error::NotFound) => reader.read_string("remote"),
Err(e) => Err(e),
}
}
// returns (remote_name, branch_name)
fn read_remote_name_branch_name(
reader: &dyn crate::reader::Reader,
) -> Result<(String, String), crate::reader::Error> {
match reader.read_string("name") {
Ok(branch) => {
let parts = branch.split('/').collect::<Vec<_>>();
Ok((parts[0].to_string(), branch))
}
Err(crate::reader::Error::NotFound) => {
// fallback to the old format
let remote_name = reader.read_string("remote_name")?;
let branch_name = reader.read_string("branch_name")?;
Ok((remote_name, branch_name))
}
Err(e) => Err(e),
}
}
impl TryFrom<&dyn crate::reader::Reader> for Target {
type Error = crate::reader::Error;
fn try_from(reader: &dyn crate::reader::Reader) -> Result<Self, Self::Error> {
let name = reader.read_string("name").map_err(|e| {
let (remote_name, branch_name) = read_remote_name_branch_name(reader).map_err(|e| {
crate::reader::Error::IOError(std::io::Error::new(
std::io::ErrorKind::Other,
format!("name: {}", e),
format!("branch: {}", e),
))
})?;
let remote = reader.read_string("remote").map_err(|e| {
let remote_url = read_remote_url(reader).map_err(|e| {
crate::reader::Error::IOError(std::io::Error::new(
std::io::ErrorKind::Other,
format!("remote: {}", e),
@ -61,8 +92,9 @@ impl TryFrom<&dyn crate::reader::Reader> for Target {
})?;
Ok(Self {
name,
remote,
branch_name,
remote_name,
remote_url,
sha,
behind: 0,
})

View File

@ -33,6 +33,7 @@ impl<'reader> TargetReader<'reader> {
#[cfg(test)]
mod tests {
use crate::writer::Writer;
use anyhow::Result;
use tempfile::tempdir;
@ -116,6 +117,54 @@ mod tests {
Ok(())
}
#[test]
fn test_read_deprecated_format() -> Result<()> {
let repository = test_repository()?;
let project = projects::Project::try_from(&repository)?;
let gb_repo_path = tempdir()?.path().to_str().unwrap().to_string();
let storage = storage::Storage::from_path(tempdir()?.path());
let user_store = users::Storage::new(storage.clone());
let project_store = projects::Storage::new(storage);
project_store.add_project(&project)?;
let gb_repo =
gb_repository::Repository::open(gb_repo_path, project.id, project_store, user_store)?;
let writer = crate::writer::DirWriter::open(gb_repo.root());
writer
.write_string("branches/target/name", "origin/master")
.unwrap();
writer
.write_string(
"branches/target/remote",
"git@github.com:gitbutlerapp/gitbutler-client.git",
)
.unwrap();
writer
.write_string(
"branches/target/sha",
"dd945831869e9593448aa622fa4342bbfb84813d",
)
.unwrap();
let session = gb_repo.get_or_create_current_session()?;
let session_reader = sessions::Reader::open(&gb_repo, &session)?;
let reader = TargetReader::new(&session_reader);
let read = reader.read_default().unwrap();
assert_eq!(read.branch_name, "origin/master");
assert_eq!(read.remote_name, "origin");
assert_eq!(
read.remote_url,
"git@github.com:gitbutlerapp/gitbutler-client.git"
);
assert_eq!(
read.sha,
git2::Oid::from_str("dd945831869e9593448aa622fa4342bbfb84813d").unwrap()
);
Ok(())
}
#[test]
fn test_read_override_target() -> Result<()> {
let repository = test_repository()?;
@ -131,15 +180,17 @@ mod tests {
let branch = test_branch();
let target = Target {
name: "target".to_string(),
remote: "remote".to_string(),
remote_name: "remote".to_string(),
branch_name: "branch".to_string(),
remote_url: "remote url".to_string(),
sha: git2::Oid::from_str("fedcba9876543210fedcba9876543210fedcba98").unwrap(),
behind: 0,
};
let default_target = Target {
name: "default_target".to_string(),
remote: "default_remote".to_string(),
remote_name: "default remote".to_string(),
branch_name: "default branch".to_string(),
remote_url: "default remote url".to_string(),
sha: git2::Oid::from_str("0123456789abcdef0123456789abcdef01234567").unwrap(),
behind: 0,
};

View File

@ -31,11 +31,14 @@ impl<'writer> TargetWriter<'writer> {
}
self.writer
.write_string("branches/target/name", &target.name)
.context("Failed to write default target name")?;
.write_string("branches/target/branch_name", &target.branch_name)
.context("Failed to write default target branch name")?;
self.writer
.write_string("branches/target/remote", &target.remote)
.context("Failed to write default target remote")?;
.write_string("branches/target/remote_name", &target.remote_name)
.context("Failed to write default target remote name ")?;
self.writer
.write_string("branches/target/remote_url", &target.remote_url)
.context("Failed to write default target remote name ")?;
self.writer
.write_string("branches/target/sha", &target.sha.to_string())
.context("Failed to write default target sha")?;
@ -53,10 +56,22 @@ impl<'writer> TargetWriter<'writer> {
}
self.writer
.write_string(&format!("branches/{}/target/name", id), &target.name)
.context("Failed to write branch target name")?;
.write_string(
&format!("branches/{}/target/branch_name", id),
&target.branch_name,
)
.context("Failed to write branch target branch name")?;
self.writer
.write_string(&format!("branches/{}/target/remote", id), &target.remote)
.write_string(
&format!("branches/{}/target/remote_name", id),
&target.remote_name,
)
.context("Failed to write branch target remote")?;
self.writer
.write_string(
&format!("branches/{}/target/remote_url", id),
&target.remote_url,
)
.context("Failed to write branch target remote")?;
self.writer
.write_string(
@ -143,8 +158,9 @@ mod tests {
let branch = test_branch();
let target = Target {
name: "target_name".to_string(),
remote: "target_remote".to_string(),
branch_name: "branch name".to_string(),
remote_name: "remote name".to_string(),
remote_url: "remote url".to_string(),
sha: git2::Oid::from_str("0123456789abcdef0123456789abcdef01234567").unwrap(),
behind: 0,
};
@ -163,14 +179,19 @@ mod tests {
branch.name
);
assert_eq!(
fs::read_to_string(root.join("target").join("name").to_str().unwrap())
fs::read_to_string(root.join("target").join("branch_name").to_str().unwrap())
.context("Failed to read branch target name")?,
target.name
target.branch_name
);
assert_eq!(
fs::read_to_string(root.join("target").join("remote").to_str().unwrap())
.context("Failed to read branch target remote")?,
target.remote
fs::read_to_string(root.join("target").join("remote_name").to_str().unwrap())
.context("Failed to read branch target name name")?,
target.remote_name
);
assert_eq!(
fs::read_to_string(root.join("target").join("remote_url").to_str().unwrap())
.context("Failed to read branch target remote url")?,
target.remote_url
);
assert_eq!(
fs::read_to_string(root.join("target").join("sha").to_str().unwrap())
@ -230,8 +251,9 @@ mod tests {
let branch = test_branch();
let target = Target {
name: "target_name".to_string(),
remote: "target_remote".to_string(),
remote_name: "remote name".to_string(),
branch_name: "branch name".to_string(),
remote_url: "remote url".to_string(),
sha: git2::Oid::from_str("0123456789abcdef0123456789abcdef01234567").unwrap(),
behind: 0,
};
@ -242,8 +264,9 @@ mod tests {
target_writer.write(&branch.id, &target)?;
let updated_target = Target {
name: "updated_target_name".to_string(),
remote: "updated_target_remote".to_string(),
remote_name: "updated remote name".to_string(),
branch_name: "updated branch name".to_string(),
remote_url: "updated remote url".to_string(),
sha: git2::Oid::from_str("fedcba9876543210fedcba9876543210fedcba98").unwrap(),
behind: 0,
};
@ -253,14 +276,20 @@ mod tests {
let root = gb_repo.root().join("branches").join(&branch.id);
assert_eq!(
fs::read_to_string(root.join("target").join("name").to_str().unwrap())
.context("Failed to read branch target name")?,
updated_target.name
fs::read_to_string(root.join("target").join("branch_name").to_str().unwrap())
.context("Failed to read branch target branch name")?,
updated_target.branch_name
);
assert_eq!(
fs::read_to_string(root.join("target").join("remote_name").to_str().unwrap())
.context("Failed to read branch target remote name")?,
updated_target.remote_name
);
assert_eq!(
fs::read_to_string(root.join("target").join("remote").to_str().unwrap())
.context("Failed to read branch target remote")?,
updated_target.remote
fs::read_to_string(root.join("target").join("remote_url").to_str().unwrap())
.context("Failed to read branch target remote url")?,
updated_target.remote_url
);
assert_eq!(
fs::read_to_string(root.join("target").join("sha").to_str().unwrap())

View File

@ -12,8 +12,9 @@ static mut TEST_TARGET_INDEX: usize = 0;
fn test_target() -> virtual_branches::target::Target {
virtual_branches::target::Target {
name: format!("target_name_{}", unsafe { TEST_TARGET_INDEX }),
remote: format!("remote_{}", unsafe { TEST_TARGET_INDEX }),
branch_name: format!("branch name {}", unsafe { TEST_TARGET_INDEX }),
remote_name: format!("remote name {}", unsafe { TEST_TARGET_INDEX }),
remote_url: format!("remote url {}", unsafe { TEST_TARGET_INDEX }),
sha: git2::Oid::from_str(&format!(
"0123456789abcdef0123456789abcdef0123456{}",
unsafe { TEST_TARGET_INDEX }

View File

@ -63,7 +63,8 @@ export class BranchData {
export class Target {
sha!: string;
name!: string;
remote!: string;
branchName!: string;
remoteName!: string;
remoteUrl!: string;
behind!: number;
}

View File

@ -69,7 +69,7 @@
<div
class="flex w-full flex-row items-center justify-between border-b border-light-400 pb-1 pl-2 pr-1 text-light-900 dark:border-dark-500 dark:bg-dark-700 dark:text-dark-100"
>
<div class="flex-grow pb-1 font-bold" title={behindMessage}>{target.name}</div>
<div class="flex-grow pb-1 font-bold" title={behindMessage}>{target.branchName}</div>
<div class="flex items-center gap-1">
<div class="pb-1">{target.behind > 0 ? `behind ${target.behind}` : 'up-to-date'}</div>
<div class="flex-shrink-0 text-light-700 dark:text-dark-100" title={behindMessage}>