The checkout step mangles branches, messes the history

git
circle.yml
2.0
#1

Where I work we deal with a bit of legacy and sometimes voluntarily stack up technical debt, to move faster, stay ahead of the competition. But when we wanted to pay it back, we got a hard time applying some of the checks we run as git hooks on the branch being tested and not the whole project (too slow, too much noise).

The reason is that the checkout step basically squashes the master branch with the branch being built. There was then no way for our checks to find the root (fork) commit of the branch. Hence no way to find what files have been modified on that branch, or what commits, or how many commits and anything like that.

All in all I don’t see any reason why you would mangle branches like this. Hence I am posting this as a bug report. And it is fairly easy to fix. Below is how we rewrote it; see comments near the lines we changed, near the end of the script. I don’t pretend to be a git guru so comments are welcome :

set -e

# Workaround old docker images with incorrect $HOME
# check https://github.com/docker/docker/issues/2968 for details
if [ "${HOME}" = "/" ]
then
    export HOME=$(getent passwd $(id -un) | cut -d: -f6)
fi

mkdir -p ~/.ssh

echo 'github.com ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAQEAq2A7hRGmdnm9tUDbO9IDSwBK6TbQa+PXYPCPy6rbTrTtw7PHkccKrpp0yVhp5HdEIcKr6pLlVDBfOLX9QUsyCOV0wzfjIJNlGEYsdlLJizHhbn2mUjvSAHQqZETYP81eFzLQNnPHt4EVVUh7VfDESU84KezmD5QlWpXLmvU31/yMf+Se8xhHTvKSCZIFImWwoG6mbUoWf9nzpIoaSjB+weqqUUmpaaasXVal72J+UX2B+2RPW3RcT0eOzQgqlJL3RKrTJvdsjE3JEAvGq3lGHSZXy28G3skua2SmVi/w4yCE6gbODqnTWlg7+wC604ydGXA8VJiS5ap43JXiUFFAaQ==
bitbucket.org ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAQEAubiN81eDcafrgMeLzaFPsw2kNvEcqTKl/VqLat/MaB33pZy0y3rJZtnqwR2qOOvbwKZYKiEO1O6VqNEBxKvJJelCq0dTXWT5pbO2gDXC6h6QDXCaHo6pOHGPUy+YBaGQRGuSusMEASYiWunYN0vCAI8QaXnWMXNMdFP3jHAJH0eDsoiGnLPBlBp4TNm6rYI74nMzgz3B9IikW4WVK+dc8KZJZWYjAuORU3jc1c/NPskD2ASinf8v3xnfXeukU0sJ5N6m5E8VLjObPEO+mN2t/FZTMZLiFqPWc/ALSqnMnnhwrNi2rbfg/rd/IpL8Le3pSBne8+seeFVBoGqzHM9yXw==
' >> ~/.ssh/known_hosts

(umask 077; touch ~/.ssh/id_rsa)
chmod 0600 ~/.ssh/id_rsa
(cat <<EOF > ~/.ssh/id_rsa
$CHECKOUT_KEY
EOF
)

# use git+ssh instead of https
git config --global url."ssh://git@github.com".insteadOf "https://github.com" || true

if [ -e /home/circleci/ct-pipeline/.git ]
then
    cd /home/circleci/ct-pipeline
    git remote set-url origin "$CIRCLE_REPOSITORY_URL" || true
else
    mkdir -p /home/circleci/ct-pipeline
    cd /home/circleci/ct-pipeline
    git clone "$CIRCLE_REPOSITORY_URL" .
fi

if [ -n "$CIRCLE_TAG" ]
then
    git fetch --force origin "refs/tags/${CIRCLE_TAG}"
else
    git fetch --force origin "${CIRCLE_BRANCH}:remotes/origin/${CIRCLE_BRANCH}"
fi

if [ -n "$CIRCLE_TAG" ]
then
    # git reset --hard "$CIRCLE_SHA1"  # We commented that out.
    git checkout -q "$CIRCLE_TAG"
elif [ -n "$CIRCLE_BRANCH" ]
then
    # git reset --hard "$CIRCLE_SHA1"  # We commented that out.
    git checkout -q "$CIRCLE_BRANCH"
fi
# Ensures the remote and local branch are in sync after the fetch (see above).
git reset --hard "$CIRCLE_SHA1"

Thanks to that, we managed to implement a “boy scout rule” policy, that helped gradually and quite quickly improve our code base. Hopefully that will help others. And again: comments are welcome.

3 Likes
Git checkout of a branch destroys local reference to master