Last active
August 29, 2015 14:21
-
-
Save michal-niedzwiedzki/e5fb4d355006fc2b5c90 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/bin/bash | |
## | |
## Candidate | |
## | |
## A tool to manage remote branches selected as candidates for release. | |
## Run it inside git repo working copy. Make sure all changes are staged. | |
## Local database saved in .candidate.sqlite in repo root. | |
## | |
## Usage: | |
## | |
## candidate show | |
## - to seee list of all candidate branches selected for release | |
## in "repo branch commit" format | |
## | |
## candidate pick <remote-repo> <remote-branch> | |
## - to select remote repo/branch as a candidate for release | |
## (will perform hard reset if unsuccessful) | |
## | |
## candidate drop <repote-repo> <remote-branch> | |
## - to withdraw remote repo/branch candidacy | |
## | |
## candidate rebuild | |
## - to pull all branches previously selected as candidates | |
## (will perform hard reset on conflicting branches) | |
## | |
## candidate dup <local-branch> | |
## - to duplicate all candidate branches from another on another local branch to current | |
## | |
## candidate start-over | |
## - to scrap current local branch and its remote ustream branch | |
## | |
## candidate --help | |
## - to display this very help message and exit | |
## | |
PROGRAM=$0 | |
PROGNAME=$( basename $0 ) | |
# database setup | |
sqlite=sqlite3 | |
schema=2 | |
# check tooling | |
which $sqlite > /dev/null | |
if [ "$?" != "0" ] | |
then | |
echo "$PROGNAME: could not find sqlite executable: $sqlite" >&2 | |
exit 1 | |
fi | |
which git > /dev/null | |
if [ "$?" != "0" ] | |
then | |
echo "$PROGNAME: could not find git executable" >&2 | |
exit 1 | |
fi | |
# check command | |
COMMAND=$1 | |
if [ -z $COMMAND ] | |
then | |
echo "$PROGNAME: command parameter missing, check $PROGNAME --help" >&2 | |
exit 1 | |
fi | |
shift | |
# check --help | |
if [ "--help" == "$COMMAND" ] | |
then | |
cat $0 | grep -e "^##" | sed "s/^##/ /g" | |
exit 0 | |
fi | |
# check repo root dir and current branch | |
ROOT=$( git rev-parse --show-toplevel ) | |
CURRENT=$( git symbolic-ref --short HEAD ) | |
if [ -z $CURRENT ] | |
then | |
echo "$PROGNAME: could not determine branch" >&2 | |
exit 1 | |
fi | |
git diff --exit-code --quiet | |
if [ "$?" != "0" ] | |
then | |
echo "$PROGNAME: unstaged changes found, aborting" >&2 | |
exit 1 | |
fi | |
# check sqlite file and update schema if needed | |
DB=$ROOT/.candidate.sqlite | |
if [ ! -f $DB ] | |
then | |
$sqlite $DB "CREATE TABLE schema ( version INT NOT NULL )" | |
fi | |
current_schema=`$sqlite $DB "SELECT COALESCE(max(version), 0) FROM schema"` | |
if [ "$schema" != "$current_schema" ] | |
then | |
test $current_schema -lt 1 && $sqlite $DB "CREATE TABLE candidates ( base VARCHAR NOT NULL, repo VARCHAR NOT NULL, branch VARCHAR NOT NULL )" | |
test $current_schema -lt 2 && $sqlite $DB "ALTER TABLE candidates ADD COLUMN hash VARCHAR" | |
$sqlite $DB "INSERT INTO schema VALUES ($schema)" | |
fi | |
# command "show" | |
if [ "show" == "$COMMAND" ] | |
then | |
$sqlite $DB --separator " " "SELECT repo, branch, hash FROM candidates WHERE base = '$CURRENT'" | |
fi | |
# command "pick" | |
if [ "pick" == "$COMMAND" ] | |
then | |
repo=$1 | |
test -z $repo && echo "$PROGNAME: missing remote repo name" >&2 && exit 1 | |
shift | |
branch=$1 | |
test -z $branch && echo "$PROGNAME: missing remote branch name" >&2 && exit 1 | |
shift | |
git pull --no-ff --no-edit $repo $branch | |
if [ $? -ne 0 ] | |
then | |
echo "$PROGNAME: conflicts, resetting head, not picking as candidate" >&2 | |
git reset HEAD --hard | |
exit 1 | |
fi | |
commit=$( git rev-parse --short --verify HEAD ) | |
$sqlite $DB "INSERT INTO candidates VALUES ('$CURRENT', '$repo', '$branch', '$commit')" | |
echo "$PROGNAME: $repo/$branch selected as candidate for $CURRENT at $commit" | |
fi | |
# command "drop" | |
if [ "drop" == "$COMMAND" ] | |
then | |
repo=$1 | |
test -z $repo && echo "$PROGNAME: missing remote repo name" >&2 && exit 1 | |
shift | |
branch=$1 | |
test -z $branch && echo "$PROGNAME: missing remote branch name" >&2 && exit 1 | |
shift | |
$sqlite $DB "DELETE FROM candidates WHERE base = '$CURRENT' AND repo = '$repo' AND branch = '$branch'" | |
fi | |
# command "rebuild" | |
if [ "rebuild" == "$COMMAND" ] | |
then | |
updates=$( $sqlite $DB "SELECT DISTINCT repo, branch FROM candidates WHERE base = '$CURRENT'" ) | |
for ln in $updates | |
do | |
repo=$( echo $ln | cut -d "|" -f 1 ) | |
branch=$( echo $ln | cut -d "|" -f 2 ) | |
git pull --no-ff --no-edit $repo $branch | |
if [ $? -ne 0 ] | |
then | |
echo "$PROGNAME: conflicts, resetting head, skipping $repo $branch" >&2 | |
git reset HEAD --hard | |
fi | |
done | |
fi | |
# command "dup" | |
if [ "dup" == "$COMMAND" ] | |
then | |
branch=$1 | |
test -z $branch && echo "$PROGNAME: missing local branch name" >&2 && exit 1 | |
shift | |
$sqlite $DB "INSERT INTO candidates SELECT '$CURRENT', repo, branch FROM candidates WHERE base = '$branch'" | |
fi | |
# command "start-over" | |
if [ "start-over" == "$COMMAND" ] | |
then | |
git branch -D $CURRENT && git push :$CURRENT | |
fi |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
This program helps selecting branches as candidates for release. All it really does is: it keeps record of merged branches and automates pulling and recreating integration branches. Here's the flow:
git checkout -b release-1.0.0 master
candidate pick JohnDoe awesome-feature
candidate rebuild
candidate drop SloppyCoder shitty-code
andgit reset HEAD --hard``, then rebuild:
candidate rebuild```To show list of branches in build:
candidate show
Something went terribly bad?
git checkout -b release-1.0.0-FIXED master
candidate dup release-1.0.0
candidate drop SloppyCoder shitty-code
To redo the integration from scratch:
candidate start-over
. Warning: this will remove current branch from local AND remote upstream.