Last active
September 25, 2022 12:45
-
-
Save vermiculus/c869aa83811de734440918ec062c43d3 to your computer and use it in GitHub Desktop.
Squash a Git repository to just include specific snapshots
This file contains hidden or 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 | |
# usage: ./squash-no-error-handling.sh <new-branch-name> | |
# | |
# Expects a list of refs on standard input. One commit will be made | |
# per ref. The last line provided will be the tip of the new branch. | |
# | |
# This version is provided to illustrate the concept without any | |
# error-handling. It is unforgiving against any mistakes -- I strongly | |
# recommend you use the 'squash.sh' script instead! | |
while read ref; do | |
head=$(git commit-tree $(git rev-parse "$ref^{tree}") $parent -m "$ref") | |
parent="-p $head" | |
done | |
git update-ref refs/heads/$1 $head |
This file contains hidden or 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 | |
# usage: ./squash.sh <new-branch-name> | |
# | |
# Expects a list of refs on standard input. One commit will be made | |
# per ref. The last line provided will be the tip of the new branch. | |
# | |
# For example, if you run | |
# | |
# git tag --list >tags.txt | |
# | |
# you'll get a file called tags.txt that looks something like this: | |
# | |
# v0.1.0 | |
# v0.1.1 | |
# v0.1.2 | |
# v0.2.0 | |
# v0.2.1 | |
# v0.3.0 | |
# v0.3.1 | |
# v0.3.2 | |
# v1.0.0 | |
# | |
# Say you want to provide a branch that only includes more important | |
# snapshots. If you trim tags.txt to just | |
# | |
# v0.1.0 | |
# v0.2.0 | |
# v0.3.0 | |
# v1.0.0 | |
# | |
# and then run the script, | |
# | |
# ./squash.sh new-main <tags.txt | |
# | |
# the 'new-main' branch will contain just the snapshots from v0.1.0 to | |
# v1.0.0 and will be pointing to the same snapshot as v1.0.0. After | |
# that, you should rebase/reword the entire range to write useful | |
# commit messages. | |
# | |
# ---- | |
# | |
# The basic idea of this script is the following loop: | |
# | |
# git commit-tree $tag^{tree} -p $parent | |
# | |
# where $parent is null the first go around. This low-level command | |
# will output the commit hash; this becomes the next $parent. | |
# | |
# When all the commits are made, we finally update the branch name | |
# given to point at the last commit using `git update-ref`. | |
# | |
# The rest of the code you see is error-handling and pretty output. | |
# See file 'squash-no-error-handling.sh' for a stripped-down version | |
# of this functionality that might make it easier to understand. | |
if ! git branch "$1"; then | |
echo "failed to create branch: $1" >&2 | |
exit 1 | |
fi | |
while read ref; do | |
tree=$(git rev-parse --verify --quiet "$ref^{tree}") | |
if [[ $? != 0 ]]; then | |
echo "does not point to a tree (not a commit or a ref): $ref" >&2 | |
exit 1 | |
fi | |
head=$(git commit-tree $tree $parent -m "auto-generated commit for snapshot '$ref'") | |
parent="-p $head" | |
echo "$ref -> ${head:0:8}" | |
done | |
if git update-ref refs/heads/$1 $head; then | |
echo "Branch '$1' created successfully:" | |
git log --oneline $1 | sed 's/^/ /' | |
echo "Run 'git rebase -i --root' to reword your commits." | |
else | |
echo "couldn't create branch; head should be $head" >&2 | |
exit 1 | |
fi |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment