Skip to content

Commit

Permalink
Add the push subcommand
Browse files Browse the repository at this point in the history
Add the push subcommand so that users will be able to easily push the
local branch changes up to the configured remote tracking branch of a
patch series by doing the following.

gps push <branch-name>

This is nice because you don't have to be checked out on the branch to
push it up to the remote, and you don't have to know what the remote is
or what the remote tracking branche's name is.

This is generally useful and a convenience. But more specifically, it
helps with the workflow where you create a patch series and request
review for it, and then append one or more patches to that it, and then
want to push those new patches up to the remote.

[changelog]
added: the push subcommand
  • Loading branch information
drewdeponte committed Jul 6, 2024
1 parent a9263e2 commit 8a74c81
Show file tree
Hide file tree
Showing 7 changed files with 124 additions and 0 deletions.
9 changes: 9 additions & 0 deletions src/cli.rs
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,11 @@ pub struct BackupStackCmdOpts {
pub branch_name: String,
}

#[derive(Debug, Args)]
pub struct PushCmdOpts {
pub branch_name: String,
}

#[derive(Debug, Subcommand)]
pub enum Command {
/// (b) - Create a branch for a patch or patch series
Expand Down Expand Up @@ -237,6 +242,10 @@ either a Git alias or a shell alias for that command.
#[command(name = "append", alias = "a")]
Append(AppendCmdOpts),

/// Push the local patches of the named patch series up to it's remote tracking branch
#[command(name = "push")]
Push(PushCmdOpts),

/// (iso) - isolate a patch or series of patches for manual testing or evaluation.
///
/// The `isolate` command isolates a patch or series of patches for manual testing or
Expand Down
1 change: 1 addition & 0 deletions src/commands/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ pub mod list;
pub mod patch_index_range;
pub mod patch_index_range_batch;
pub mod pull;
pub mod push;
pub mod rebase;
pub mod request_review;
pub mod sha;
Expand Down
13 changes: 13 additions & 0 deletions src/commands/push.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
use super::utils::print_error_chain;
use gps as ps;

pub fn push(branch_name: String, color: bool) {
let res = ps::push(branch_name);
match res {
Ok(_) => {}
Err(e) => {
print_error_chain(color, e.into());
std::process::exit(1);
}
}
}
1 change: 1 addition & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ pub use ps::public::isolate::{isolate, IsolateError};
pub use ps::public::latest_github_release::{newer_release_available, notify_of_newer_release};
pub use ps::public::list::list;
pub use ps::public::pull::{pull, PullError};
pub use ps::public::push::push;
pub use ps::public::rebase::rebase;
pub use ps::public::request_review::{request_review, RequestReviewError};
pub use ps::public::sha;
Expand Down
1 change: 1 addition & 0 deletions src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ fn main() {
cli::Command::Append(opts) => {
commands::append::append(opts.patch_index_or_range, opts.branch_name, cli.color)
}
cli::Command::Push(opts) => commands::push::push(opts.branch_name, cli.color),
cli::Command::Isolate(opts) => {
commands::isolate::isolate(opts.patch_index_or_range, cli.color)
}
Expand Down
1 change: 1 addition & 0 deletions src/ps/public/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ pub mod isolate;
pub mod latest_github_release;
pub mod list;
pub mod pull;
pub mod push;
pub mod rebase;
pub mod request_review;
pub mod sha;
Expand Down
98 changes: 98 additions & 0 deletions src/ps/public/push.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
use super::super::private::git;
use std::result::Result;

#[derive(Debug)]
pub enum PushError {
OpenRepositoryFailed(Box<dyn std::error::Error>),
FindBranchFailed(Box<dyn std::error::Error>),
GetUpstreamBranchFailed(Box<dyn std::error::Error>),
GetUpstreamBranchNameFailed(Box<dyn std::error::Error>),
RemoteBranchNameNotUtf8,
ExtractRemoteFailed(String),
ExtractRelativeBranchNameFailed(String),
PushFailed(Box<dyn std::error::Error>),
Unhandled(Box<dyn std::error::Error>),
}

impl std::fmt::Display for PushError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::OpenRepositoryFailed(e) => write!(f, "failed to open repository {}", e),
Self::FindBranchFailed(e) => {
write!(f, "failed to find branch with the provided name {}", e)
}
Self::GetUpstreamBranchFailed(e) => write!(f, "failed to get upstream branch, {}", e),
Self::GetUpstreamBranchNameFailed(e) => {
write!(f, "failed to get upstream branch name, {}", e)
}
Self::RemoteBranchNameNotUtf8 => write!(f, "remote branch name isn't valid utf-8"),
Self::ExtractRemoteFailed(from) => {
write!(f, "failed to extract remote name from {}", from)
}
Self::ExtractRelativeBranchNameFailed(from) => {
write!(f, "failed to extract relative branch name from {}", from)
}
Self::PushFailed(e) => write!(f, "failed when trying to push, {}", e),
Self::Unhandled(e) => write!(f, "{}", e),
}
}
}

impl std::error::Error for PushError {
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
match self {
Self::OpenRepositoryFailed(e) => Some(e.as_ref()),
Self::FindBranchFailed(e) => Some(e.as_ref()),
Self::GetUpstreamBranchFailed(e) => Some(e.as_ref()),
Self::GetUpstreamBranchNameFailed(e) => Some(e.as_ref()),
Self::RemoteBranchNameNotUtf8 => None,
Self::ExtractRemoteFailed(_) => None,
Self::ExtractRelativeBranchNameFailed(_) => None,
Self::PushFailed(e) => Some(e.as_ref()),
Self::Unhandled(e) => Some(e.as_ref()),
}
}
}

pub fn push(branch_name: String) -> Result<(), PushError> {
let repo = git::create_cwd_repo().map_err(|e| PushError::OpenRepositoryFailed(e.into()))?;

let branch = repo
.find_branch(&branch_name, git2::BranchType::Local)
.map_err(|e| PushError::FindBranchFailed(e.into()))?;

let remote_branch = branch
.upstream()
.map_err(|e| PushError::GetUpstreamBranchFailed(e.into()))?;

let remote_branch_name_str = remote_branch
.name()
.map_err(|e| PushError::GetUpstreamBranchNameFailed(e.into()))?
.ok_or(PushError::RemoteBranchNameNotUtf8)?;

let mut remote_branch_name_parts = remote_branch_name_str.split('/');
let remote_branch_remote =
remote_branch_name_parts
.next()
.ok_or(PushError::ExtractRemoteFailed(
remote_branch_name_str.to_owned(),
))?;
let remote_branch_relative_name =
remote_branch_name_parts
.next()
.ok_or(PushError::ExtractRelativeBranchNameFailed(
remote_branch_name_str.to_owned(),
))?;

// e.g. origin/the-branch so it is <remote>/<branch-name-on-remote>

git::ext_push(
false,
remote_branch_remote,
&branch_name,
remote_branch_relative_name,
)
.map_err(|e| PushError::PushFailed(e.into()))?;

Ok(())
}

0 comments on commit 8a74c81

Please sign in to comment.