git: Add base branch support to create_branch (#42151)
Closes [#41674](https://github.com/zed-industries/zed/issues/41674) Description: Creating a branch from a base requires switching to the base branch first, then creating the new branch and checking out to it, which requires multiple operations. Add base_branch parameter to create_branch to allow a new branch from a base branch in one operation which is synonymous to the command `git switch -c <new-branch> <base-branch>`. Below is the video after solving the issue: (`master` branch is the default branch here, and I create a branch `new-branch-2` based off the `master` branch. I also show the error which used to appear before the fix.) [Screencast from 2025-11-07 05-14-32.webm](https://github.com/user-attachments/assets/d37d1b58-af5f-44e8-b867-2aa5d4ef3d90) Release Notes: - Fixed the branch-picking error by replacing multiple sequential switch operations with just one switch operation. Signed-off-by: ayu-ch <ayu.chandekar@gmail.com>
This commit is contained in:
@@ -7065,7 +7065,7 @@ async fn test_remote_git_branches(
|
||||
// Also try creating a new branch
|
||||
cx_b.update(|cx| {
|
||||
repo_b.update(cx, |repository, _cx| {
|
||||
repository.create_branch("totally-new-branch".to_string())
|
||||
repository.create_branch("totally-new-branch".to_string(), None)
|
||||
})
|
||||
})
|
||||
.await
|
||||
|
||||
@@ -326,7 +326,7 @@ async fn test_ssh_collaboration_git_branches(
|
||||
// Also try creating a new branch
|
||||
cx_b.update(|cx| {
|
||||
repo_b.update(cx, |repo_b, _cx| {
|
||||
repo_b.create_branch("totally-new-branch".to_string())
|
||||
repo_b.create_branch("totally-new-branch".to_string(), None)
|
||||
})
|
||||
})
|
||||
.await
|
||||
|
||||
@@ -407,7 +407,11 @@ impl GitRepository for FakeGitRepository {
|
||||
})
|
||||
}
|
||||
|
||||
fn create_branch(&self, name: String) -> BoxFuture<'_, Result<()>> {
|
||||
fn create_branch(
|
||||
&self,
|
||||
name: String,
|
||||
_base_branch: Option<String>,
|
||||
) -> BoxFuture<'_, Result<()>> {
|
||||
self.with_state_async(true, move |state| {
|
||||
state.branches.insert(name);
|
||||
Ok(())
|
||||
|
||||
@@ -431,7 +431,8 @@ pub trait GitRepository: Send + Sync {
|
||||
fn branches(&self) -> BoxFuture<'_, Result<Vec<Branch>>>;
|
||||
|
||||
fn change_branch(&self, name: String) -> BoxFuture<'_, Result<()>>;
|
||||
fn create_branch(&self, name: String) -> BoxFuture<'_, Result<()>>;
|
||||
fn create_branch(&self, name: String, base_branch: Option<String>)
|
||||
-> BoxFuture<'_, Result<()>>;
|
||||
fn rename_branch(&self, branch: String, new_name: String) -> BoxFuture<'_, Result<()>>;
|
||||
|
||||
fn worktrees(&self) -> BoxFuture<'_, Result<Vec<Worktree>>>;
|
||||
@@ -1358,14 +1359,28 @@ impl GitRepository for RealGitRepository {
|
||||
.boxed()
|
||||
}
|
||||
|
||||
fn create_branch(&self, name: String) -> BoxFuture<'_, Result<()>> {
|
||||
let repo = self.repository.clone();
|
||||
fn create_branch(
|
||||
&self,
|
||||
name: String,
|
||||
base_branch: Option<String>,
|
||||
) -> BoxFuture<'_, Result<()>> {
|
||||
let git_binary_path = self.any_git_binary_path.clone();
|
||||
let working_directory = self.working_directory();
|
||||
let executor = self.executor.clone();
|
||||
|
||||
self.executor
|
||||
.spawn(async move {
|
||||
let repo = repo.lock();
|
||||
let current_commit = repo.head()?.peel_to_commit()?;
|
||||
repo.branch(&name, ¤t_commit, false)?;
|
||||
Ok(())
|
||||
let mut args = vec!["switch", "-c", &name];
|
||||
let base_branch_str;
|
||||
if let Some(ref base) = base_branch {
|
||||
base_branch_str = base.clone();
|
||||
args.push(&base_branch_str);
|
||||
}
|
||||
|
||||
GitBinary::new(git_binary_path, working_directory?, executor)
|
||||
.run(&args)
|
||||
.await?;
|
||||
anyhow::Ok(())
|
||||
})
|
||||
.boxed()
|
||||
}
|
||||
|
||||
@@ -241,18 +241,10 @@ impl BranchListDelegate {
|
||||
return;
|
||||
};
|
||||
let new_branch_name = new_branch_name.to_string().replace(' ', "-");
|
||||
let base_branch = from_branch.map(|b| b.to_string());
|
||||
cx.spawn(async move |_, cx| {
|
||||
if let Some(based_branch) = from_branch {
|
||||
repo.update(cx, |repo, _| repo.change_branch(based_branch.to_string()))?
|
||||
.await??;
|
||||
}
|
||||
|
||||
repo.update(cx, |repo, _| {
|
||||
repo.create_branch(new_branch_name.to_string())
|
||||
})?
|
||||
.await??;
|
||||
repo.update(cx, |repo, _| {
|
||||
repo.change_branch(new_branch_name.to_string())
|
||||
repo.create_branch(new_branch_name, base_branch)
|
||||
})?
|
||||
.await??;
|
||||
|
||||
|
||||
@@ -2094,7 +2094,7 @@ impl GitStore {
|
||||
|
||||
repository_handle
|
||||
.update(&mut cx, |repository_handle, _| {
|
||||
repository_handle.create_branch(branch_name)
|
||||
repository_handle.create_branch(branch_name, None)
|
||||
})?
|
||||
.await??;
|
||||
|
||||
@@ -4747,29 +4747,35 @@ impl Repository {
|
||||
})
|
||||
}
|
||||
|
||||
pub fn create_branch(&mut self, branch_name: String) -> oneshot::Receiver<Result<()>> {
|
||||
pub fn create_branch(
|
||||
&mut self,
|
||||
branch_name: String,
|
||||
base_branch: Option<String>,
|
||||
) -> oneshot::Receiver<Result<()>> {
|
||||
let id = self.id;
|
||||
self.send_job(
|
||||
Some(format!("git switch -c {branch_name}").into()),
|
||||
move |repo, _cx| async move {
|
||||
match repo {
|
||||
RepositoryState::Local { backend, .. } => {
|
||||
backend.create_branch(branch_name).await
|
||||
}
|
||||
RepositoryState::Remote { project_id, client } => {
|
||||
client
|
||||
.request(proto::GitCreateBranch {
|
||||
project_id: project_id.0,
|
||||
repository_id: id.to_proto(),
|
||||
branch_name,
|
||||
})
|
||||
.await?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
let status_msg = if let Some(ref base) = base_branch {
|
||||
format!("git switch -c {branch_name} {base}").into()
|
||||
} else {
|
||||
format!("git switch -c {branch_name}").into()
|
||||
};
|
||||
self.send_job(Some(status_msg), move |repo, _cx| async move {
|
||||
match repo {
|
||||
RepositoryState::Local { backend, .. } => {
|
||||
backend.create_branch(branch_name, base_branch).await
|
||||
}
|
||||
},
|
||||
)
|
||||
RepositoryState::Remote { project_id, client } => {
|
||||
client
|
||||
.request(proto::GitCreateBranch {
|
||||
project_id: project_id.0,
|
||||
repository_id: id.to_proto(),
|
||||
branch_name,
|
||||
})
|
||||
.await?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
pub fn change_branch(&mut self, branch_name: String) -> oneshot::Receiver<Result<()>> {
|
||||
|
||||
@@ -1662,7 +1662,7 @@ async fn test_remote_git_branches(cx: &mut TestAppContext, server_cx: &mut TestA
|
||||
// Also try creating a new branch
|
||||
cx.update(|cx| {
|
||||
repository.update(cx, |repo, _cx| {
|
||||
repo.create_branch("totally-new-branch".to_string())
|
||||
repo.create_branch("totally-new-branch".to_string(), None)
|
||||
})
|
||||
})
|
||||
.await
|
||||
|
||||
Reference in New Issue
Block a user