# describe the pattern for lines that specify branch names
PATTERN="^BRANCH NAME: \([a-zA-Z0-9-]*\)$"

# iterate through all commits since we diverged from master
for commit in $(git rev-list master...HEAD); do
  # find the branch name if one exists
  ref=$(git show --format=%B $commit | sed -ne "s/$PATTERN/\1/p")

  # if a branch name was specified then log it and force push it
  if [[ ! -z "$ref" ]]; then
    echo "Pushing commit $commit to branch $ref."
    git push --force-with-lease origin $commit:refs/heads/$ref
  fi
done

I generally try to avoid situations where I'm developing multiple new features that directly depend on each other. However, when it's unavoidable I've been relying on a system of:

  • specifying remote branch names in the body of commit messages that I want to make into pull requests (as BRANCH NAME: ro-some-feature)
  • using a script to iterate through all commits since master and push ones with the above named branches to the remote without ever creating more local branches for them
  • making pull requests out of each of those branches and defining them relative to each other

This means that I don't have to manage a fleet of branches on my end. This flow lets me develop a series of features on a single branch and apply revisions to earlier features with an interactive rebase. It feels like a much more natural git flow than maintaining a bunch of branches with a lot of shared history and continually rebasing all of them on top of each other.

This idea definitely isn't new and other tools out there also include metadata like this in commit messages. Arcanist (used in the Phabricator ecosystem), for example, bakes things like reviewers and patch ids into commit messages.