Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to prepend the past to a git repository?

Tags:

git

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.

like image 474
Tobias Kienzler Avatar asked Jun 30 '10 07:06

Tobias Kienzler


People also ask

How to add new changes to previous commit git?

Use git commit --amend to change your latest log message. Use git commit --amend to make modifications to the most recent commit. Use git rebase to combine commits and modify history of a branch.

How do I get a previous version of a repository?

Reverting To An Old Version of the RepositoryStart by navigating to the “History” tab. Right-click on the previous commit, and you'll see the option to revert this commit. If you click on Revert This Commit , two things will happen. The first is that the files in your repository will revert to their previous state.

How to override commit in git?

If you've already created a fresh commit, you'll want to use git rebase -i to squash your commit on top of the old one. After you've made this change locally, and verified your commit looks the way you want it to, you'll have to git push --force to overwrite history on the Github remote.


1 Answers

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 
like image 179
Chris Johnsen Avatar answered Sep 22 '22 05:09

Chris Johnsen