Skip to content

Instantly share code, notes, and snippets.

@ORBAT
Last active February 3, 2020 13:46
Show Gist options
  • Save ORBAT/ff1bb5e659d7c94fe7f0c293d00b8c33 to your computer and use it in GitHub Desktop.
Save ORBAT/ff1bb5e659d7c94fe7f0c293d00b8c33 to your computer and use it in GitHub Desktop.
Clones only one subdirectory of a git repo. Preserves history, but removes all refs that don't contain the subdirectory
#! /bin/bash
# https://stackoverflow.com/questions/39956637/running-git-filter-branch-subdirectory-filter-to-also-remove-get-rid-of-other-su
repo=${1:?"Repo in first argument"}
subdir=${2:?"subdirectory name in second argument"}
temp_dir=$(mktemp -d)
function log {
>&2 echo
>&2 echo -e "\t $@"
>&2 echo
}
log Using temporary directory ${temp_dir}
function rm_temp_dir {
[[ ! -z ${temp_dir} ]] && [[ -z ${NO_TEMP_RM} ]] && rm -rf ${temp_dir}
}
trap rm_temp_dir EXIT
pushd . >/dev/null 2>&1
cd ${temp_dir}
git clone ${1} $(pwd) || exit 1
log Removing refs that don\'t reference ${subdir}
git for-each-ref --format='%(refname)' | \
while read ref; do
if test "$(git rev-list --count "$ref" -- "${subdir}")" = 0; then
log "delete $ref"
echo "delete $ref"
fi
done | git update-ref --stdin || exit 2
log "Making ${subdir} the root of the new repo"
git filter-branch -f --prune-empty --tag-name-filter cat --subdirectory-filter "${subdir}" -- --all || exit 3
log Checking out remaining remote branches
git for-each-ref --format="%(refname:lstrip=3)" refs/remotes | \
while read branch; do
git checkout ${branch} || exit 4
done
log Removing ref backups
git for-each-ref --format="%(refname)" refs/original/ | xargs -n 1 git update-ref -d
log Done, moving back to original directory
popd >/dev/null 2>&1
NO_TEMP_RM=1
mv -nv $temp_dir $subdir || exit 5
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment