remote-helper: Add patch.branch option
This commit is contained in:
parent
6a43e83dd6
commit
e70850cb36
|
|
@ -239,6 +239,90 @@ $ rad patch show 9580891
|
||||||
╰────────────────────────────────────────────────────╯
|
╰────────────────────────────────────────────────────╯
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## Detached HEAD
|
||||||
|
|
||||||
|
In some cases, we may be creating patches from a detached HEAD state, but we
|
||||||
|
still want to have a tracking branch. We can do this using the `patch.branch`
|
||||||
|
option.
|
||||||
|
|
||||||
|
```
|
||||||
|
$ git commit --allow-empty -m "Going into detached HEAD"
|
||||||
|
[feature/2 831e838] Going into detached HEAD
|
||||||
|
```
|
||||||
|
|
||||||
|
``` (stderr)
|
||||||
|
$ git checkout 831e838
|
||||||
|
Note: switching to '831e838'.
|
||||||
|
|
||||||
|
You are in 'detached HEAD' state. You can look around, make experimental
|
||||||
|
changes and commit them, and you can discard any commits you make in this
|
||||||
|
state without impacting any branches by switching back to a branch.
|
||||||
|
|
||||||
|
If you want to create a new branch to retain commits you create, you may
|
||||||
|
do so (now or later) by using -c with the switch command. Example:
|
||||||
|
|
||||||
|
git switch -c <new-branch-name>
|
||||||
|
|
||||||
|
Or undo this operation with:
|
||||||
|
|
||||||
|
git switch -
|
||||||
|
|
||||||
|
Turn off this advice by setting config variable advice.detachedHead to false
|
||||||
|
|
||||||
|
HEAD is now at 831e838 Going into detached HEAD
|
||||||
|
$ git push rad HEAD:refs/patches -o patch.branch
|
||||||
|
✓ Patch e0fd879ac0a7d3ddcf3b440d8db656f5d7cebea3 opened
|
||||||
|
✓ Branch patches/e0fd879ac0a7d3ddcf3b440d8db656f5d7cebea3 created
|
||||||
|
hint: to update, run `git push rad patches/e0fd879ac0a7d3ddcf3b440d8db656f5d7cebea3`
|
||||||
|
To rad://z42hL2jL4XNk6K8oHQaSWfMgCL7ji/z6MknSLrJoTcukLrE435hVNQT4JUhbvWLX4kUzqkEStBU8Vi
|
||||||
|
* [new reference] HEAD -> refs/patches
|
||||||
|
```
|
||||||
|
|
||||||
|
The default name used for the branch is `patches/<patch id>`. So let's checkout
|
||||||
|
the branch and push a new revision:
|
||||||
|
|
||||||
|
``` (stderr)
|
||||||
|
$ git checkout patches/e0fd879ac0a7d3ddcf3b440d8db656f5d7cebea3
|
||||||
|
Switched to branch 'patches/e0fd879ac0a7d3ddcf3b440d8db656f5d7cebea3'
|
||||||
|
$ git commit --allow-empty -m "Pushing new revision"
|
||||||
|
$ git push rad
|
||||||
|
✓ Patch e0fd879 updated to revision 943cbd9769e855d5e4eba419e68374c5141a2785
|
||||||
|
To compare against your previous revision e0fd879, run:
|
||||||
|
|
||||||
|
git range-diff [..] [..] [..]
|
||||||
|
|
||||||
|
To rad://z42hL2jL4XNk6K8oHQaSWfMgCL7ji/z6MknSLrJoTcukLrE435hVNQT4JUhbvWLX4kUzqkEStBU8Vi
|
||||||
|
831e838..d0ff2a1 patches/e0fd879ac0a7d3ddcf3b440d8db656f5d7cebea3 -> patches/e0fd879ac0a7d3ddcf3b440d8db656f5d7cebea3
|
||||||
|
```
|
||||||
|
|
||||||
|
However, we also allow you to name the branch yourself:
|
||||||
|
|
||||||
|
``` (stderr)
|
||||||
|
$ git checkout 831e838 -q
|
||||||
|
$ git push rad HEAD:refs/patches -o patch.branch='feature/3'
|
||||||
|
✓ Patch e0fd879ac0a7d3ddcf3b440d8db656f5d7cebea3 opened
|
||||||
|
✓ Branch feature/3 created
|
||||||
|
hint: to update, run `git push rad feature/3`
|
||||||
|
To rad://z42hL2jL4XNk6K8oHQaSWfMgCL7ji/z6MknSLrJoTcukLrE435hVNQT4JUhbvWLX4kUzqkEStBU8Vi
|
||||||
|
* [new reference] HEAD -> refs/patches
|
||||||
|
```
|
||||||
|
|
||||||
|
Let's checkout this branch and also push a new revision:
|
||||||
|
|
||||||
|
``` (stderr)
|
||||||
|
$ git checkout feature/3
|
||||||
|
Switched to branch 'feature/3'
|
||||||
|
$ git commit --allow-empty -m "Pushing new revision"
|
||||||
|
$ git push rad
|
||||||
|
✓ Patch e0fd879 updated to revision 943cbd9769e855d5e4eba419e68374c5141a2785
|
||||||
|
To compare against your previous revision e0fd879, run:
|
||||||
|
|
||||||
|
git range-diff [..] [..] [..]
|
||||||
|
|
||||||
|
To rad://z42hL2jL4XNk6K8oHQaSWfMgCL7ji/z6MknSLrJoTcukLrE435hVNQT4JUhbvWLX4kUzqkEStBU8Vi
|
||||||
|
831e838..d0ff2a1 feature/3 -> patches/e0fd879ac0a7d3ddcf3b440d8db656f5d7cebea3
|
||||||
|
```
|
||||||
|
|
||||||
## Empty patch
|
## Empty patch
|
||||||
|
|
||||||
If we try to open a patch without making any changes to our base branch (`master`),
|
If we try to open a patch without making any changes to our base branch (`master`),
|
||||||
|
|
|
||||||
|
|
@ -153,6 +153,35 @@ impl FromStr for Verbosity {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Branch creation options when creating a patch.
|
||||||
|
#[derive(Debug, Default, Clone)]
|
||||||
|
pub enum Branch {
|
||||||
|
/// Don't create a new branch.
|
||||||
|
#[default]
|
||||||
|
None,
|
||||||
|
/// Create a branch with the same name as the upstream branch (i.e. `patches/<patch id>`).
|
||||||
|
MirrorUpstream,
|
||||||
|
/// Create a branch with the provided name.
|
||||||
|
Provided(git::RefString),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Branch {
|
||||||
|
/// Return the branch name to be used for the local branch when creating a
|
||||||
|
/// patch.
|
||||||
|
pub fn to_branch_name(self, object: &radicle::patch::PatchId) -> Option<git::Qualified> {
|
||||||
|
match self {
|
||||||
|
Self::None => None,
|
||||||
|
Self::MirrorUpstream => Some(git::refs::patch(object)),
|
||||||
|
Self::Provided(name) => match name.clone().into_qualified() {
|
||||||
|
None => Some(git::fmt::lit::refs_heads(&name).into()),
|
||||||
|
// Ensure that if the reference is already qualified we do not
|
||||||
|
// add `refs/heads`
|
||||||
|
Some(name) => Some(name),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug, Default, Clone)]
|
#[derive(Debug, Default, Clone)]
|
||||||
pub struct Options {
|
pub struct Options {
|
||||||
/// Don't sync after push.
|
/// Don't sync after push.
|
||||||
|
|
@ -167,6 +196,8 @@ pub struct Options {
|
||||||
base: Option<git::Oid>,
|
base: Option<git::Oid>,
|
||||||
/// Patch message.
|
/// Patch message.
|
||||||
message: cli::patch::Message,
|
message: cli::patch::Message,
|
||||||
|
/// Create a branch and set its upstream when opening a patch.
|
||||||
|
branch: Branch,
|
||||||
verbosity: Verbosity,
|
verbosity: Verbosity,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -289,6 +320,7 @@ fn push_option(args: &[&str], opts: &mut Options) -> Result<(), Error> {
|
||||||
["sync.debug"] => opts.sync_debug = true,
|
["sync.debug"] => opts.sync_debug = true,
|
||||||
["no-sync"] => opts.no_sync = true,
|
["no-sync"] => opts.no_sync = true,
|
||||||
["patch.draft"] => opts.draft = true,
|
["patch.draft"] => opts.draft = true,
|
||||||
|
["patch.branch"] => opts.branch = Branch::MirrorUpstream,
|
||||||
_ => {
|
_ => {
|
||||||
let args = args.join(" ");
|
let args = args.join(" ");
|
||||||
|
|
||||||
|
|
@ -315,6 +347,7 @@ fn push_option(args: &[&str], opts: &mut Options) -> Result<(), Error> {
|
||||||
|
|
||||||
opts.base = Some(git::Oid::from(commit.id()));
|
opts.base = Some(git::Oid::from(commit.id()));
|
||||||
}
|
}
|
||||||
|
"patch.branch" => opts.branch = Branch::Provided(git::RefString::try_from(val)?),
|
||||||
other => {
|
other => {
|
||||||
return Err(Error::UnsupportedPushOption(other.to_owned()));
|
return Err(Error::UnsupportedPushOption(other.to_owned()));
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -564,17 +564,52 @@ where
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
if let Some(upstream) = upstream {
|
if let Some(upstream) = upstream {
|
||||||
|
if let Some(local_branch) = opts.branch.to_branch_name(&patch) {
|
||||||
|
fn strip_refs_heads(qualified: git::Qualified) -> git::RefString {
|
||||||
|
let (_refs, _heads, x, xs) = qualified.non_empty_components();
|
||||||
|
std::iter::once(x).chain(xs).collect()
|
||||||
|
}
|
||||||
|
|
||||||
|
working.reference(
|
||||||
|
&local_branch,
|
||||||
|
*head,
|
||||||
|
true,
|
||||||
|
"Create local branch for patch",
|
||||||
|
)?;
|
||||||
|
|
||||||
|
let remote_branch = git::refs::workdir::patch_upstream(&patch);
|
||||||
|
let remote_branch = working.reference(
|
||||||
|
&remote_branch,
|
||||||
|
*head,
|
||||||
|
true,
|
||||||
|
"Create remote tracking branch for patch",
|
||||||
|
)?;
|
||||||
|
debug_assert!(remote_branch.is_remote());
|
||||||
|
|
||||||
|
let local_branch = strip_refs_heads(local_branch);
|
||||||
|
let upstream_branch = git::refs::patch(&patch);
|
||||||
|
git::set_upstream(working, upstream, &local_branch, &upstream_branch)?;
|
||||||
|
|
||||||
|
eprintln!(
|
||||||
|
"{} Branch {} created",
|
||||||
|
term::PREFIX_SUCCESS,
|
||||||
|
term::format::tertiary(&local_branch),
|
||||||
|
);
|
||||||
|
hint(format!(
|
||||||
|
"to update, run `git push {upstream} {local_branch}`"
|
||||||
|
));
|
||||||
|
}
|
||||||
// Setup current branch so that pushing updates the patch.
|
// Setup current branch so that pushing updates the patch.
|
||||||
if let Some(branch) =
|
else if let Some(branch) =
|
||||||
rad::setup_patch_upstream(&patch, head, working, upstream, false)?
|
rad::setup_patch_upstream(&patch, head, working, upstream, false)?
|
||||||
{
|
{
|
||||||
if let Some(name) = branch.name()? {
|
if let Some(name) = branch.name()? {
|
||||||
if profile.hints() {
|
if profile.hints() {
|
||||||
// Remove the remote portion of the name, i.e.
|
// Remove the remote portion of the name, i.e.
|
||||||
// rad/patches/deadbeef -> patches/deadbeef
|
// rad/patches/deadbeef -> patches/deadbeef
|
||||||
let name = name.split('/').skip(1).collect::<Vec<_>>().join("/");
|
let name = name.split_once('/').unwrap_or_default().1;
|
||||||
hint(format!(
|
hint(format!(
|
||||||
"to update, run `git push` or `git push rad -f HEAD:{name}`"
|
"to update, run `git push` or `git push {upstream} -f HEAD:{name}`"
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -217,6 +217,11 @@ The full list of options follows:
|
||||||
*patch.draft*::
|
*patch.draft*::
|
||||||
Open the patch as a _draft_. Turned off by default.
|
Open the patch as a _draft_. Turned off by default.
|
||||||
|
|
||||||
|
*patch.branch[=<name>]*::
|
||||||
|
Create a branch when opening a patch. Turned off by default.
|
||||||
|
If a custom *name* is provided, this name is used. Otherwise
|
||||||
|
the branch name defaults to *patches/<patch id>*.
|
||||||
|
|
||||||
*patch.message*=_<message>_::
|
*patch.message*=_<message>_::
|
||||||
To prevent the editor from opening, you can specify the patch message via this
|
To prevent the editor from opening, you can specify the patch message via this
|
||||||
option. Multiple *patch.message* options are concatenated with a blank line
|
option. Multiple *patch.message* options are concatenated with a blank line
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue