Skip to content

Commit

Permalink
Added checkout subcommand
Browse files Browse the repository at this point in the history
This [`command`] allows for the checking out of a topic branch across
multiple repos. When working on a change that effects multiple repos,
the general flow is to create a branch with the same name for ever repo
that the change touches. These changes are then sent to gerrit and
linked together under a topic. When landing, these changes are applied
to their respective repos atomically. This change adds the checkout
subcommand which allows the developer to change topic branches in their
repo checkout easily.

[`command`]: https://gerrit.googlesource.com/git-repo/+/refs/heads/main/subcmds/branches.py
  • Loading branch information
StefanBossbaly committed Sep 18, 2024
1 parent b89f378 commit ba36a9d
Show file tree
Hide file tree
Showing 2 changed files with 72 additions and 0 deletions.
11 changes: 11 additions & 0 deletions src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,12 @@ enum Commands {
/// List the topic branches
Branches {},

/// Checkout a branch for development
Checkout {
/// Specify a branch to checkout
branch: String,
},

/// Checkout a new tree into a new directory
Clone {
/// The target to checkout in the format <REMOTE>[/<BRANCH>[:MANIFEST]]
Expand Down Expand Up @@ -397,6 +403,7 @@ impl std::fmt::Display for Commands {
match self {
Commands::Init { .. } => write!(f, "init"),
Commands::Branches { .. } => write!(f, "branches"),
Commands::Checkout { .. } => write!(f, "checkout"),
Commands::Clone { .. } => write!(f, "clone"),
Commands::Fetch { .. } => write!(f, "fetch"),
Commands::Sync { .. } => write!(f, "sync"),
Expand Down Expand Up @@ -1025,6 +1032,10 @@ fn main() {
let tree = Tree::find_from_path(cwd)?;
cmd_branches(config, &mut pool, &tree)
}
Commands::Checkout { branch } => {
let tree = Tree::find_from_path(cwd)?;
tree.checkout(&config, &mut pool, &branch)
}
Commands::Clone {
target,
directory,
Expand Down
61 changes: 61 additions & 0 deletions src/tree.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1066,6 +1066,67 @@ impl Tree {
Ok(())
}

pub fn checkout(&self, config: &Config, pool: &mut Pool, target_branch: &str) -> Result<i32, Error> {
let projects = self.collect_manifest_projects(config, &self.read_manifest()?, None, None)?;

let mut job = Job::with_name("checkout");

for project in &projects {
let path = self.path.join(&project.project_path);
job.add_task(&project.project_path, move || -> Result<Option<&str>, Error> {
let repo = git2::Repository::open(&path).context(format!("failed to open object repository {:?}", path))?;

let maybe_parse = match repo.revparse_ext(target_branch) {
Ok(result) => Ok(Some(result)),
Err(error) => {
if error.code() == git2::ErrorCode::NotFound {
Ok(None)
} else {
Err(error)
}
}
}?;

if let Some((object, reference)) = maybe_parse {
repo.checkout_tree(&object, None)?;

match reference {
Some(repo_ref) => repo.set_head(repo_ref.name().unwrap())?,
None => repo.set_head_detached(object.id())?,
}

Ok(Some(&project.project_path))
} else {
Ok(None)
}
});
}

let results = pool.execute(job);

if !results.failed.is_empty() {
for error in results.failed {
eprintln!("{}: {}", error.name, error.result);
}
return Ok(1);
}

let mut has_projects = false;
let checkout_projects = results.successful.into_iter().filter_map(|result| result.result);

for checkout_project in checkout_projects {
has_projects = true;
println!("Checked out {checkout_project}");
}

if has_projects {
Ok(0)
} else {
eprintln!("error: no project has branch {target_branch}");
Ok(1)
}
}

pub fn sync(
&mut self,
config: &Config,
Expand Down

0 comments on commit ba36a9d

Please sign in to comment.