Skip to content

Instantly share code, notes, and snippets.

@PEZ
Last active March 10, 2022 17:18
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save PEZ/f8d1c9d1184efa9e6ea929e7da69efce to your computer and use it in GitHub Desktop.
Save PEZ/f8d1c9d1184efa9e6ea929e7da69efce to your computer and use it in GitHub Desktop.
Zsh functions to help rebasing git branches safely
# Previous branch is `baz`
~/Foo(bar|✔) % rebobl -
logging to: /var/folders/t5/gqxhj8pd6p9_tnvy6sbtmy480000gn/T/rebob.YJJZirAY
rebob: No branch given, checking REBOBS in /Users/pez/.my-configs/rebob_config.zshrc
rebob: rebob /Users/pez/Foo:bar on -
rebob: pulling?: false
rebob: Created: .rebobing
Saved working directory and index state WIP on bar: 17fa37c Add baz
rebob: Stashed -u (.rebobing stashed)
Current branch bar is up to date.
rebob: Rebased on: baz
Already up to date!
rebob: Popped the stack (.rebobing is back)
rebob: Removed: .rebobing
# rebob = rebase branch on branch
function echo2_and_log() {
log=$1
msg=$2
log_msg="$(date +"%Y-%m-%dT%H:%M:%S") ${msg}"
echo "rebob: ${msg}" >&2
echo ${log_msg} >>${log}
}
function _rebob() {
start_path_branch="$(git rev-parse --show-toplevel):$(git branch --show-current)" || { echo "Error. Are you really in a git repo? Aborting." >&2 ; return 1 }
log=$(mktemp -t rebob)
echo logging to: ${log} >&2
if [[ -z "$1" ]]; then
echo2_and_log ${log} "No branch given. Aborting"
return 1
else
rebob_on_branch=$1
fi
echo2_and_log ${log} "rebob ${rebob_on_branch} on ${start_path_branch}"
echo2_and_log ${log} "pulling?: $2"
{ date > .rebobing && echo2_and_log ${log} "Created: .rebobing" } || { echo2_and_log ${log} "Creating .rebobing failed. Aborting." ; return 1; }
{ git stash -u && echo2_and_log ${log} "Stashed -u (.rebobing stashed)" } || { echo2_and_log ${log} "stash failed. Aborting." ; return 1; }
if [ "$2" = true ]; then
{ git checkout ${rebob_on_branch} && echo2_and_log ${log} "Checked out: ${rebob_on_branch}" } || { echo2_and_log ${log} "checkout ${rebob_on_branch} failed. Aborting." ; return 1; }
{ git pull && echo2_and_log ${log} "Pulled" } || { echo2_and_log ${log} "pull failed. Aborting." ; return 1; }
{ git checkout - && echo2_and_log ${log} "Checkout back on: ${start_path_branch}" } || { echo2_and_log ${log} "checkout ${start_path_branch} failed. Aborting." ; return 1; }
fi
{ git rebase ${rebob_on_branch} && echo2_and_log ${log} "Rebased on: ${rebob_on_branch}" } || { echo2_and_log ${log} "rebase ${rebob_on_branch} failed. Aborting." ; return 1; }
{ git stash pop -q && echo2_and_log ${log} "Popped the stack (.rebobing is back)" } || { echo2_and_log ${log} "stash pop failed. Aborting." ; return 1; }
{ rm .rebobing && echo2_and_log ${log} "Removed: .rebobing" } || { echo2_and_log ${log} "rm .rebobing failed" ; return 1; }
return 0
}
function rebob() {
if [[ ! -z "$1" ]]; then
_rebob $1 true
else
_rebob "" true
fi
}
# rebobl = rebase branch on branch locally (w/o pull)
function rebobl() {
if [[ ! -z "$1" ]]; then
_rebob $1 false
else
_rebob "" false
fi
}
# remaster - rebob special case =)
function remaster() {
rebob master
}
@PEZ
Copy link
Author

PEZ commented Oct 7, 2021

Why?

In my current workflow (in a busy monorepo) i often need to rebase my branches on origin/master. And I also often need to rebase my branch B which is branched of branch A, sometimes only locally and sometimes from origin.

For the first case, I just type remaster when on my branch to have it rebased on origin/master.

For the second case I use rebob A when on B to rebase on origin/A and rebobl A to rebase on my local A. Often A is my previously checkout branch, then I can dorebob - (or rebobl -, depending).

@PEZ
Copy link
Author

PEZ commented Oct 7, 2021

The sample run is from when bar was already rebased on baz, so it is just a very complicated and wordy noop there. 😄

@PEZ
Copy link
Author

PEZ commented Oct 7, 2021

To use: source rebob.zshrc.

@PEZ
Copy link
Author

PEZ commented Oct 20, 2021

After some weeks of using this I declare huge success! 🎉

The rebob_config.zshrc (see ealier revisions if you are curious) was a YAGNI, though. Not used it at all because rebob - covers my use case perfectly. My use case is building a library in a monorepo where I am also implementing it in one of the projects. The library is being merged to master continuously from various branch-As, while the PoC implementation is yet-to-be-merged at all, just living in a PR of its own, based on bramch-B.

So when some branch-A is ready for PR I do:

$ remaster
$ git push
$ git checkout -b branch-next-A
$ git checkout `branch-B`
$ rebobl -
$ git push -f

Then do new work where some of it is committed to branch-next-A and some to branch-B, and the cycle continues.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment