I face this situation pretty often in my company, and we found 2 main solutions to this problem. Both of them use the ability to git to keep commits in history in certain situations. By the way, the good practice is to never push submodules refs that are pointing to deletable branches (i.e. feature branches).
I assume that, like me, you dont want to push feature_branch in order to not pollute your remote repository and be robust to inquisitor-mode work mates that clean all old pushed feature branches
The problem is that when you do a rebase, commits are beeing re-played on top of history of master. They are new commits (let's say X', Y' and Z'), with new hash, and branch's head is moved on the top of Z'. So X,Y,Z will stay on a dead branch and will be deleted with time (90 day with default settings). In addition to that, they will not be pushed by default when you will push you rebased branch.
- Classic, clean way : do a merge instead of a rebase
Do a merge. E,F,G will be linked to the merge commit. In that way, X,Y,Z will remain for ever and unchanged, even if the branch is deleted (only the ref of the branch would be in that case. But at the expense of a un-linear history on master). When you will push parent branch, X,Y,Z will be pushed too.
- Keep commits in history with tags
One solution to keep X,Y,Z commits in history is to tag Z (before the rebase (tag will survive to it), or after (but you will have to re-found it in the history with reflog), it doesn't matter). If this tag is then pushed, X,Y,Z will not be cleaned. This is pretty dirty and has side effects, but does the job. Of course, you will have to push that tag ! (which is pretty equivalent to push a branch, to be honest. The difference is that you can name it specifically, for example DONT_DELETE_THIS_FOOL ;) )
Here is a small script that you can run to ensure that any submodule dependency will not be lost (by tagging each submodule and push them) (use -h to get help):
#!/bin/bash
check_tags () {
slength=${#1}
slength=$((slength+1))
refmajor=`echo $2`
refminor=`echo $3`
refrevision=`echo $4`
echo "Check for anterior tags in $5..."
for tag in $(git tag -l -n "$1*.*.*" | cut -d" " -f1 | cut -c "$slength"- ); do
temptagMajor=`echo $tag | cut -d. -f1`
temptagMinor=`echo $tag | cut -d. -f2`
temptagRevision=`echo $tag | cut -d. -f3`
if [[ temptagMajor -gt tagMajor ]] || [[ temptagMajor -eq tagMajor && temptagMinor -gt tagMinor ]] || [[ temptagMajor -eq tagMajor && temptagMinor -eq tagMinor && temptagRevision -gt tagRevision ]]; then
tagMajor=$temptagMajor
tagMinor=$temptagMinor
tagRevision=$temptagRevision
fi
done
echo "Latest versioning tag found : $1$tagMajor.$tagMinor.$tagRevision (want to apply $1$refmajor.$refminor.$refrevision)"
if [[ tagMajor -gt refmajor ]]; then
echo "Cannot tag with $1$refmajor.$refminor.$refrevision : anterior tag with greater major revision number (found $tagMajor, wanted $refmajor). Use -f to ignore."
return 1
elif [[ tagMajor -eq refmajor ]] && [[ tagMinor -gt refminor ]]; then
echo "Cannot tag with $1$refmajor.$refminor.$refrevision : anterior tag with equal major revision number but greater minor revision number (found $tagMinor, wanted $refminor). Use -f to ignore."
return 1
elif [[ tagMajor -eq refmajor ]] && [[ tagMinor -eq refminor ]] && [[ tagRevision -gt refrevision ]]; then
echo "Cannot tag with $1$refmajor.$refminor.$refrevision : anterior tag with equal major and minor revision number, but greater revision number (found $tagRevision, wanted $refrevision). Use -f to ignore."
return 1
elif [[ tagMajor -eq refmajor ]] && [[ tagMinor -eq refminor ]] && [[ tagRevision -eq refrevision ]]; then
echo "Cannot tag with $1$refmajor.$refminor.$refrevision : anterior tag with equal major, minor, and revision number (found $1$tagMajor.$tagMinor.$tagRevision, wanted $1$refmajor.$refminor.$refrevision). Use -f to ignore."
return 1
else
return 0
fi
}
function subcheck() {
if [[ ! -d .git ]]; then
echo "not a git repo"
return 1
fi;
if ! [[ `git submodule status` ]]; then
echo 'no submodule'
return 1
fi
submodules=($(git config --file .gitmodules --get-regexp path | awk '{ print $2 }'))
currentDirectory=$(pwd)
for submodule in "${submodules[@]}"
do
printf "\n\nEntering '$submodule'\n"
cd "$currentDirectory/$submodule"
check_tags $1 $2 $3 $4 $submodule
if [[ $? -eq 1 ]]; then
cd "$currentDirectory"
return 1
fi
done
cd "$currentDirectory"
}
#export -f check_tags
# Check arguments
while getopts v:hfti: option
do
case "${option}"
in
v) VERSION=${OPTARG};;
h) HELP='help';;
f) FORCE='force';;
t) TEST='test';;
i) INDEX=${OPTARG};;
esac
done
printf "Tag release script v0.1\n"
# Help
if [ "$HELP" != "" ]; then
echo 'GIT Release Script'
echo "Options :"
echo 'Use -v to specify version (mandatory). Ex : "-v 1.0.2"'
echo 'Use -t to run unit test of -v inputs'
echo 'Use -f to force tagging / skip anterior tag versions check'
echo 'Use -i to specify index (optional). Ex : "-v 1.0.2 -i A" for a indA + v1.0.2 double tag.'
exit 1
fi
# Tests for bad inputs
if [ "$HELP" != "" ]; then
array=( ".2.3" "1..3" "1.2." "A.2.3" "1.A.3" "1.2.A" "1A3.123.123" "123.1D3.123" "123.123.1A3" "nougatine" "1.3" )
arr=(${array[*]})
echo "Tested valudes : ${#arr[*]}"
for ix in ${!arr[*]}
do
printf " %s\n" "${arr[$ix]}"
. release_script.sh -v ${arr[$ix]}
done
fi
# Version
if [ "$VERSION" == "" ]; then
echo "Argument missing"
echo "Run -h for help"
exit 1
fi
major=`echo $VERSION | cut -d. -f1`
minor=`echo $VERSION | cut -d. -f2`
revision=`echo $VERSION | cut -d. -f3`
if [ -n "$(printf '%s\n' "$major" | sed 's/[0-9]//g')" ] || [ "$major" == "" ]; then
echo "Invalid major version argument (was \"$major\")"
echo "Run -h for help"
exit 1
fi
if [ -n "$(printf '%s\n' "$minor" | sed 's/[0-9]//g')" ] || [ "$minor" == "" ] ; then
echo "Invalid minor version argument (was \"$minor\")"
echo "Run -h for help"
exit 1
fi
if [ -n "$(printf '%s\n' "$revision" | sed 's/[0-9]//g')" ] || [ "$revision" == "" ]; then
echo "Invalid revision version argument (was \"$revision\")"
echo "Run -h for help"
exit 1
fi
# Fetching
git fetch --all
echo "Fetching tags"
git fetch --tag
tagMajor=0
tagMinor=0
tagRevision=0
versionLabel=v$VERSION
url=$(git config --get remote.origin.url)
basename=$(basename "$url" .git)
# Check previous available versions if no -f specified
if [ "$FORCE" == "" ]; then
if [ "$INDEX" != "" ]; then
echo "Check for anterior index tag in $basename..."
for tag in $(git tag -l -n "ind$INDEX" ); do
if [[ "ind$INDEX" == $tag ]] ; then
echo "\"ind$INDEX\" tag already exists in $basename. Use -f to ignore."
exit 1
fi
done
echo "No conflict found for "ind$INDEX" index tag"
fi
check_tags v "$major" "$minor" "$revision" "$basename"
if [[ $? -eq 1 ]]; then
#echo "Error found, won't tag"
exit 1
fi
subcheck ${basename}_v $major $minor $revision
if [[ $? -eq 1 ]]; then
exit 1
fi
fi
# Release tag script
versionLabel=v$VERSION
url=$(git config --get remote.origin.url)
basename=$(basename "$url" .git)
echo "Tagging project $basename (\"$versionLabel\")"
git tag $versionLabel
#TODO : check index
if [ "$INDEX" != "" ]; then
echo "Tagging index $INDEX"
git tag "ind$INDEX"
fi
echo "Tagging submodules (\"${basename}_$versionLabel\")"
git submodule foreach "git tag ${basename}_$versionLabel || :"
echo "Pushing project tag"
git push --tags
echo "Pushing submodules tags"
git submodule foreach 'git push --tags || :'