Skip to content

Instantly share code, notes, and snippets.

@x3ro
Created August 15, 2013 15:57
Show Gist options
  • Save x3ro/6242017 to your computer and use it in GitHub Desktop.
Save x3ro/6242017 to your computer and use it in GitHub Desktop.
#!/bin/bash
# We need the TAB character for SED (Mac OS X sed does not understand \t)
TAB="$(printf '\t')"
function abort {
echo "$(tput setaf 1)$1$(tput sgr0)"
exit 1
}
function request_input {
read -p "$(tput setaf 4)$1 $(tput sgr0)"
}
function request_confirmation {
read -p "$(tput setaf 4)$1 (y/n) $(tput sgr0)"
[ "$REPLY" == "y" ] || abort "Aborted!"
}
cat << "EOF"
This script rewrites your entire history, moving the current repository root
into a subdirectory. This can be useful if you want to merge a submodule into
its parent repository.
For example, your main repository might contain a submodule at the path src/lib/,
containing a file called "test.c".
If you would merge the submodule into the parent repository without further
modification, all the commits to "test.c" will have the path "/test.c", whereas
the file now actually lives in "src/lib/test.c".
If you rewrite your history using this script, adding "src/lib/" to the path
and the merging into the parent repository, all paths will be correct.
NOTE: This script might complete garble your repository, so PLEASE apply this
only to a clone of the repository where it does not matter if the repo is destroyed.
EOF
request_confirmation "Do you want to proceed?"
cat << "EOF"
Please provide the path which should be prepended to the current root. In the
above example, that would be "src/lib". Please note that the path MUST NOT contain
a trailing slash.
EOF
request_input "Please provide the desired path (e.g. 'src/lib'):"
# Escape input for SED, taken from http://stackoverflow.com/a/2705678/124257
TARGET_PATH=$(echo -n "$REPLY" | sed -e 's/[\/&]/\\&/g')
# Last confirmation
git ls-files -s | sed "s/${TAB}/${TAB}$TARGET_PATH\//"
request_confirmation "Please take a look at the printed file list. Does it look correct?"
# The actual processing happens here
CMD="git ls-files -s | sed \"s/${TAB}/${TAB}$TARGET_PATH\//\" | GIT_INDEX_FILE=\${GIT_INDEX_FILE}.new git update-index --index-info && mv \${GIT_INDEX_FILE}.new \${GIT_INDEX_FILE}"
git filter-branch \
--index-filter "$CMD" \
HEAD
@jeremysears
Copy link

This is awesome. Thank you! Also note that in your main article related to this, the syntax for the following command has changed:

$ git merge -s ours --no-commit sub/master

To:

$ git merge -s ours --no-commit --allow-unrelated-histories sub/master

@SuperSandro2000
Copy link

I wanted to say thank you for this script. It saved my life.

@Bjornelmers
Copy link

Love the script! However there seems to be a problem with it if the history contains empty commits. I managed to solve the problem by modifying line 60 to:

CMD="git ls-files -s | sed "s/${TAB}/${TAB}$TARGET_PATH//" | GIT_INDEX_FILE=${GIT_INDEX_FILE}.new git update-index --index-info && [ ! -f ${GIT_INDEX_FILE}.new ] || mv ${GIT_INDEX_FILE}.new ${GIT_INDEX_FILE}"

@spatil-git
Copy link

thank you for this script...

@christowiz
Copy link

Great article. Thanks for the script and thanks to @jeremysears for the updated command.

@brandesign
Copy link

Saved my day. Thx :)

@oksmelnik
Copy link

thanks for the script!

@misolo
Copy link

misolo commented Mar 31, 2020

Great! Thank you

@rdvencioneck
Copy link

Thank you!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment