Search code examples
gitgit-rewrite-history

How to add old project files midway to main branch


I have found 4 old versions of one file that I would like to add to a Git repository.

Initially, there is one single-ref main development line, as follows:

[ROOT] A - B - C - D - I - J - K - L - M [MAIN]

Currently I have added the old file to an orphaned branch with 4 commits in the past, resulting in:

[ROOT] E - F - G - H [ORPHAN]

Because of the dates of these commits, in the log (showing all branches) these branches are now presented as:

[ROOT] E - F - G - H [ORPHAN]   A - B - C - D - I - J - K - L - M [MAIN]

(Note the missing -)

The result I would like to have is as follows:

[ROOT] A - B - C - D - I - J - K - L - M [MAIN]
                     /
[ROOT] E - F - G - H

How should I perform this merge? Effectively, the only thing I want to change is that I gets H as parent too, whereby all files in I remain unchanged.

Considerations:

  • all SHA's may be altered,
  • E descends from D, but not its commit date, so for chronological reasons I would like E to remain orphaned.

The thing I have tried is:

(main) $ git checkout I
(I) $ git merge H --allow-unrelated-histories --strategy ours

Which says it merged, but when I checkout main again, nothing changed. From the comment below I understand this is because of a detached HEAD. So I tried something else:

(main) $ git merge H I --allow-unrelated-histories

But then this merge commit is on the present day with M as parent. How do I commit in the past on old commit I?


Solution

  • This is possible, with a few remarks.

    Although the wish to keep a single commit I is still a possibility, it is recommended to introduce a new seperate merge commit between commit I and commit J, which is the default Git merging behaviour.

    You can perform this type of rewriting history with an interactive rebase. But before that, since you want both author date and committer date to be in the past, you have to prepare the merge commit. As you figured out already, that merge commit will be in the present, but with the rebase later on, you will change the order of the commits.

    Note that all commit's SHA will be altered. (Read: do not do this when somebody has pulled the repository.)

    $ git checkout main
    $ git merge Orphan I --no-commit --allow-unrelated-histories
    $ GIT_AUTHOR_DATE='2001-01-31T13:41:20' GIT_COMMITTER_DATE=$GIT_AUTHOR_DATE git commit -m 'Lost and found'
    $ git rebase -i A --rebase-merges --committer-date-is-author-date
    

    This will present a so-called todo list, something like:

    label onto
    
    # Orphan branch
    reset [new root]
    pick 947c76a Message commit E
    pick a324ed5 Message commit F
    pick 336313f Message commit G
    pick 5c52b9d Message commit H
    label Message_commit_H
    
    reset onto
    pick 9a8f81d Message commit B
    pick 4442137 Message commit C
    pick 0c84d8f Message commit D
    pick 9151031 Message commit I
    pick 94ba177 Message commit J
    pick 01f54e4 Message commit K
    pick e8366f9 Message commit L
    pick 22dab36 Message commit M
    merge -C 2271f49 Message_commit_H # Lost and found
    

    Change this file into:

    label onto
    
    # Orphan branch
    reset [new root]
    pick 947c76a Message commit E
    pick a324ed5 Message commit F
    pick 336313f Message commit G
    pick 5c52b9d Message commit H
    label Message_commit_H
    
    reset onto
    pick 9a8f81d Message commit B
    pick 4442137 Message commit C
    pick 0c84d8f Message commit D
    pick 9151031 Message commit I
    merge -C 2271f49 Message_commit_H # Lost and found
    pick 94ba177 Message commit J
    pick 01f54e4 Message commit K
    pick e8366f9 Message commit L
    pick 22dab36 Message commit M
    

    Close the editor and let the rebase change the order of the commits. Possibly, there are merge conflicts which are to be dealt with manually, in which case the rebase program will tell you exactly what to do.

    Succes.