What is the easiest way to rebase a git repository such that only the heads of current branches remain in tact, and all else is squashed? Preferably a non-interactive solution that I can apply to various repositories.
Background: my repository is too large because it is tracking a lot of legacy code from previous versions. I deleted the branches I am no longer interested in. For the remaining branches, I would like to keep the final versions around for archiving purposes, but clean up all of the intermediate changes.
Since I found this question interesting, I wrote a little bash script that does (something like) what you want.
You can download it as a file (or check out its repo on Github) and find the source below. Here's a little discussion:
Usage:
./start_git_repo_fresh.sh [-r] [<path to git repo>]
where-r
enables creation of a common root commit and the path let's you use the script from a different location; if the path is ommitted, the current directory (CWD) is used
This script's main task is to transplant currently existing branches, by simply using their latest commit (since Git commits are snapshots of the complete repository, that's easy).
If you specify the -r
flag, a new empty root commit is created, and all branches are created as direct descendants of that root commit. If you do not specify the flag, every new branch will have its own root (they'll be orphan branches) and contain exactly one commit each.
This is done by getting a branch of existing local branches and walking over them; for each branch (call it BR
) , the script will:
-r
is specifiedBR
-r
is not specifiedBR
BR
BR
using the commit message from BR
BR
BR
If a branch was checked out when the script started (the repo was not in a detached HEAD state), the new version of that branch is checked out at the end; if the repo was on a detached HEAD, the last created branch is left checked out.
Script source:
#!/bin/bash
COMMON_ROOT=0
TEMP_ROOT_BRANCH="NEW_ROOT_COMMIT"
if [ "$1" == "-r" ]; then
COMMON_ROOT=1
shift 1
fi
if [ "$#" -eq 1 ]; then
cd "$1"
fi
branches=$(git branch --color=never)
orig_branch=$(echo "$branches" | grep --color=never "^\* " | sed "s/^\* //")
if [ "$COMMON_ROOT" -eq 1 ]; then
echo "Creating new (empty) common root commit"
git checkout --orphan "$TEMP_ROOT_BRANCH" 2> /dev/null
git rm -r --cached . >/dev/null
git clean -dfx > /dev/null
git commit --allow-empty -m "Initial commit" > /dev/null
fi
echo "$branches" | sed "s/^\* //" | while read branch; do
echo "Transplanting branch $branch"
newbranch="${branch}_new"
if [ "$COMMON_ROOT" -eq 1 ]; then
git checkout -b "$newbranch" "$TEMP_ROOT_BRANCH" > /dev/null 2>/dev/null
git checkout "$branch" -- . > /dev/null 2>/dev/null
else
git checkout "$branch" > /dev/null 2>/dev/null
git checkout --orphan "$newbranch" > /dev/null 2>/dev/null
fi
git commit -C "$branch" > /dev/null
git branch -D "$branch" > /dev/null
git branch -m "$newbranch" "$branch" > /dev/null
done
if [ "$COMMON_ROOT" -eq 1 ]; then
git branch -D "$TEMP_ROOT_BRANCH" > /dev/null
fi
if [ -n "$orig_branch" ]; then
git checkout "$orig_branch" 2>/dev/null
fi