How to prepend the past to a git repository?

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.

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

How to prepend the past to a git repository?, 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  The two trees aren’t connected, and in fact a git pull won’t even get the ancient_history branch at all. We need a way to make a connection between the two. Git has a facility called a graft, which basically fakes up a parent link between two commits. To make one, just insert a line into the .git/info/grafts file in this format: [ref] [parent]

If you don't want to change the commits in your repository, you can use grafts to override the parent information for a commit. This is what the Linux Kernel repo does to get history from before they started using Git.

This message: http://marc.info/?l=git&m=119636089519572 seems to have the best documentation that I can find.

You'd create a sequence of commits relating to your pre-git history, then use the .git/info/grafts file to make Git use the last commit in that sequence as the parent of the first commit you generated using Git.

How do I prepend commits to my history? : git, This will change all of the rebased commits hashes, so consider whether this might cause problems (e.g. you have published your repository and others are  path: string (a path to prepend) Return: string (modified PATH environment variable) It prepends the given path to the process.env.PATH , or its equivalent on Windows for example process.env.Path , along with the platform-specific path delimiter .

The easiest approach is of course creating a new git repo, commiting the history to prepend first and then reapplying the patches of the old repo. But I'd prefer a solution which is less time consuming by automation.

Prepend one git project to another as if they had one history? : git, the two git repos together but I want the old's history to append itself BEFORE for clarity git checkout ours/master # current head of the repo git rebase match​  Configure git account on a computer; Day by day work; How to pull from the original repository of a forked one; Initial Set Up of a git repository; Rebasing and merging; Revert the local copy of a repository to the current version fo the code on the cloud; Switch SVN repository to github.com; Switch to a previous state of the repository

If you just want to permanently merge 2 repositories, the best solution is to export all commits from the second repository (except the initial commit, which created the repository as a continuation of the other).

I think this is the best because when you do the steps as explained by Chris Johnsen, it will convert your initial commit on the second repository as deletion commit which deletes several files. And if you try to skip the initial commit, it will convert the your second commit, into a commit which deletes all files (of course, I had to try it). I am not sure how it affect the ability of git to track the file history in command as git log --follow -- file/name.txt

You can export the whole history (expect the first commit, which is already present on the first repository) of your second repository and import it on the first repository running these commands:

  1. Open a Linux command line on your second repository (to export the latest commits)
  2. commit_count=$(git rev-list HEAD --count)
  3. git format-patch --full-index -$(($commit_count - 1))
  4. Move all git patches .patch files created on the root of your second repository to a new directory called patches on the side of your first repository root directory
  5. Now, open a Linux command line on your first repository (to import the latest commits)
  6. git am ../patches/*.patch
  7. If you got problems while applying git patches, run git am --abort, then, see git: patch does not apply and try something like git am ../patches/*.patch --ignore-space-change --ignore-whitespace as suggested on the linked StackOverflow question.

Alternatively to using git from command line, you can use a git interface like SmartGit or GitExtensions

References:

  1. https://www.ivankristianto.com/create-patch-files-from-multiple-commits-in-git/
  2. Git: How to create patches for a merge?
  3. https://www.ivankristianto.com/create-patch-files-from-multiple-commits-in-git/
  4. how to apply multiple git patches in one shot
  5. https://davidwalsh.name/git-export-patch

For completeness, here I present a automated shell script which follows Chris Johnsen steps to permanently merge 2 repository. You need to run this on the first repository, where you would like to integrate the history from the second repository, which continues the history from the first repository. After a few hours of experimentation, I found this to be the best approach. If you know how you improve something, please, fix/share/comment.

Please, fully backup your both first and second repositories to .zip file before running this.

old_history=master
new_history=master-temp

old_remote_name=deathaxe
old_remote_url=second_remote_url

git remote add $old_remote_name $old_remote_url
git fetch $old_remote_name
git branch --no-track $new_history refs/remotes/$old_remote_name/$old_history
git branch --set-upstream-to=origin/$old_history $new_history

# the last commit of old history branch
oldhead=$(git rev-parse --verify $old_history)

# the initial commit of current branch
# newinit=$(git rev-list $new_history | tail -n 2 | head -n -1)
newinit=$(git rev-list $new_history | 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 <last commit> <first commit>
# git replace <object> <replacement>
git replace -f "$newinit" "$newfake"

# If you decide to make the replacement permanent, use git filter-branch
# (make a tar/zip backup of your .git directory first)
git filter-branch --tag-name-filter cat -- --all
git replace -d $newinit

git push -f --tags
git push -f origin $new_history

git checkout $old_history
git branch -d $new_history
git pull --rebase

References:

  1. https://feeding.cloud.geek.nz/posts/combining-multiple-commits-into-one/
  2. https://mirrors.edge.kernel.org/pub/software/scm/git/docs/git-replace.html
  3. Remove the last line from a file in Bash
  4. Force "git push" to overwrite remote files
  5. Git force push tag when the tag already exists on remote

Rewriting History, You can run rebase interactively by adding the -i option to git rebase . Don't include any commit you've already pushed to a central server – doing so will  You can list all existing tags git tag or you could filter the list with git tag -l 'v1.1.*', where * acts as a wildcard. It will return a list of tags marked with v1.1 . You will notice that when you call git tag you do not get to see the contents of your annotations.

git-archive Documentation, Instead of making a tar archive from the local repository, retrieve a tar archive you committed without adding an appropriate export-ignore in its .gitattributes )  HOWTO install this file. Go to the directory of the repository you want to enable this on. In that repository go to the directory .git/hooks (create it if it doesn't exist) Copy this gist into the directory.

Viewing the Commit History, By default, with no arguments, git log lists the commits made in that repository in however, adding the --all-match option further limits the output to just those  A broccoli plugin to prepend git commit hash of the source at top of all built html, css and js files. The present HEAD commit hash of your project will be added to the top of all built html, css and js files.

Git Revert, The git revert command is used for undoing changes to a repository's commit 86bb32e] prepend content to demo file 1 file changed, 1 insertion(+) $ git log  English verb conjugation for Ruby (and Rails). Contribute to rossmeissl/verbs development by creating an account on GitHub.

Comments
  • See also Edit/amend/modify/change the first/root/initial commit in Git?, Change first commit of project with Git?, and Git: how to add commits before first/initial/root commit?.
  • @Cupcake Thanks, I never saw the notification for your links
  • Warning: grafts have been removed in Git 2.18 (Q2 2018). See "What are .git/info/grafts for?".
  • thanks, this works great for a small test subset, now off to the complete one :) (I used the replacement option)
  • This is not an issue for me at the moment, but I'll ask anyway: Using the replace-option up to the point before git filter-branching does not rewrite history and is therefore easier to share, right?
  • Without git filter branch, neither grafts, nor replacements actually rewrite history (they just produce an effect on the commit DAG as if they had rewritten history). The benefits of replacements are 1) they can be disabled by command line argument or environment variable, 2) they can be pushed/fetched, 3) they work on any object, not just the parent "attritubes" of commits. The ability to push replacements makes them easy to share via the normal Git protocols (you can share graft entries, but you have to use some "out of band" mechanism (i.e. not push/fetch) to propagate them).
  • Reading your "undelete" question, I see the file in question was in the snapshots, but not in your Git history. If you have tags to bits of your Git history, this is what I would do: start with your original Git repository (before any filtering or rewriting; see refs/original/ if you do not have a plain backup/clone), use filter-branch --tag-name-filter cat --tree-filter … -- --all (or --index-filter) to add the file to your history while rewriting its tags, then do the graft/replace and git filter-branch --tag-name-filter cat -- --all to permanently establish the graft/replacement.
  • Warning: grafts have been removed in Git 2.18 (Q2 2018). See "What are .git/info/grafts for?".
  • +1 ah yes, I see, thank you. This is detailed as the graft-option in Chris Johnsen's answer