How to prepend the past to a git repository?
git
Solution
For importing the old snapshots, you find some of the tools in Git's contrib/fast-import directory useful. Or, if you already have each old snapshot in a directory, you might do something like this:
# Assumes the v* glob will sort in the right order
# (i.e. zero padded, fixed width numeric fields)
# For v1, v2, v10, v11, ... you might try:
# v{1..23} (1 through 23)
# v?{,?} (v+one character, then v+two characters)
# v?{,?{,?}} (v+{one,two,three} characters)
# $(ls -v v*) (GNU ls has "version sorting")
# Or, just list them directly: ``for d in foo bar baz quux; do''
(git init import)
for d in v*; do
if mv import/.git "$d/"; then
(cd "$d" && git add --all && git commit -m"pre-Git snapshot $d")
mv "$d/.git" import/
fi
done
(cd import && git checkout HEAD -- .)
Then fetch the old history into your working repository:
cd work && git fetch ../import master:old-history
Once you have both the old history and your Git-based history in the same repository, you have a couple of options for the prepend operation: grafts and replacements.
Grafts are a per-repository mechanism to (possibly temporarily) edit the parentage of various existing commits. Grafts are controlled by the `$GIT_DIR/info/grafts` file (described under “info/grafts” of the gitrepository-layout manpage).
INITIAL_SHA1=$(git rev-list --reverse master | head -1)
TIP_OF_OLD_HISTORY_SHA1=$(git rev-parse old-history)
echo $INITIAL_SHA1 $TIP_OF_OLD_HISTORY_SHA1 >> .git/info/grafts
With the graft in place (the original initial commit did not have any parents, the graft gave it one parent), you can use all the normal Git tools to search through and view the extended history (e.g. `git log` should now show you the old history after your commits).
The main problem with grafts is that they are limited to your repository. But, if you decide that they should be a permanent part of the history, you can use git filter-branch to make them so (make a tar/zip backup of your `.git` dir first; git filter-branch will save original refs, but sometime it is just easier to use a plain backup).
git filter-branch --tag-name-filter cat -- --all
rm .git/info/grafts
The replacement mechanism is newer (Git 1.6.5+), but they can be disabled on a per-command basis (`git --no-replace-objects …`) and they can pushed for easier sharing. Replacement works on individual objects (blobs, trees, commits, or annotated tags), so the mechanism is also more general. The replace mechanism is documented in the git replace manpage. Due to the generality, the “prepending” setup is a little more involved (we have to create a new commit instead of just naming the new parent):
# the last commit of old history branch
oldhead=$(git rev-parse --verify old-history)
# the initial commit of current branch
newinit=$(git rev-list master | tail -n 1)
# create a fake commit based on $newinit, but with a parent
# (note: at this point, $oldhead must be a full commit ID)
newfake=$(git cat-file commit "$newinit" \
| sed "/^tree [0-9a-f]\+\$/aparent $oldhead" \
| git hash-object -t commit -w --stdin)
# replace the initial commit with the fake one
git replace -f "$newinit" "$newfake"
Sharing this replacement is not automatic. You have to push part of (or all of) `refs/replace` to share the replacement.
git push some-remote 'refs/replace/*'
If you decide to make the replacement permanent, use git filter-branch (same as with grafts; make a tar/zip backup of your `.git` directory first):
git filter-branch --tag-name-filter cat -- --all
git replace -d $INITIAL_SHA1
Problem
I received some source code and decided to use git for it since my co-worker used the `mkdir $VERSION` etc. approach. While the past of the code currently seems unimportant, I'd still like to put it under git control as well to better understand the development process. So: What is a convenient way to put those past versions into my already existing git repo? There is currently no remote repo so I don't mind rewriting history, but a solution that takes remote repositories into account will of course be preferred unless it is much more coplicated then. Bonus points for a script which does not need any more interaction based on either a directory or a archive file based history.
Related problems
- Force "git push" to overwrite remote files
- Change first commit of project with Git?
- How to undelete a file previously deleted in git's history?
- Setting git parent pointer to a different parent
- git apply fails with "patch does not apply" error
- Remove the last line from a file in Bash
- Remove sensitive files and their commits from Git history
- Edit the root commit in Git?