Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Merging two git repositories to obtain linear history

Tags:

git

merge

I have two git repositories and a lot of untracked changes between them:

   ftp -->            C-- (untracked changes) --D
                     /                           \
   git        A--B--C <-- old/master              \
                                                   \
                                                    \
                                  new/master -->     D--E--F 

How can I merge old repository into new repository to have a linear history like

A--B--C--D--E--F

EDIT:

inspired by How can I combine Git repositories into a linear history?

I've done:

git clone url://new new
cd new/
git remote add old url://old
git fetch old
git reset --hard origin/master
git filter-branch --parent-filter 'sed "s_^\$_-p old/master_"' HEAD
git push origin master

Only problem is that every commit from new/master was doubled (due to change of parent I think) so I've now (M is merge commit)

         D---E---F--         
                    \
A--B--C--D'--E'--F'--M 

How can I easily remove unnecessary commits (D - F and maybe M)?

like image 785
ts. Avatar asked Jun 06 '13 21:06

ts.


People also ask

How do I merge two Git repository and keep history?

To combine two separate Git repositories into one, add the repository to merge in as a remote to the repository to merge into. Then, combine their histories by merging while using the --allow-unrelated-histories command line option.

Does Git merge keep history?

In the Conceptual Overview section, we saw how a feature branch can incorporate upstream changes from main using either git merge or git rebase . Merging is a safe option that preserves the entire history of your repository, while rebasing creates a linear history by moving your feature branch onto the tip of main .

Which command is used to display merge history in Git?

The most basic and powerful tool to do this is the git log command. By default, with no arguments, git log lists the commits made in that repository in reverse chronological order — that is, the most recent commits show up first.


2 Answers

Fixing the results of git filter-branch

If you have a repository that looks like this:

         D---E---F--
                    \
A--B--C--D'--E'--F'--M <-master

and you want the result to look like this:

A--B--C--D'--E'--F' <-master

then you can simply force master to point to F':

git checkout master
git reset --hard <sha1-of-F'>

This will cause commits D, E, F, and M to become unreachable, effectively deleting them (they will be garbage collected after a while).

Starting over from scratch

Assuming you have two repositories that look like this:

  • old: A--B--C <-master
  • new: D--E--F <-master

and you want the result to be:

  • combined: A--B--C--D'--E'--F' <- master

then you can perform the following steps:

  1. Initialize the combined repository:

    git init combined
    cd combined
    git remote add old url:/to/old
    git remote add new url:/to/new
    git remote update
    

    At this point your combined repository looks like:

    A--B--C <-old/master
    
    D--E--F <-new/master
    

    Note that the two branches aren't connected in any way.

  2. Set your master branch to point to C:

    git reset --hard old/master
    

    Now your repository looks like this:

          old/master
          |
          v
    A--B--C <-master
    
    D--E--F <-new/master
    
  3. Find the sha1 of D:

    d=$(git rev-list --reverse new/master | head -n 1)
    
  4. Import D into your working directory and index by reading the contents of the commit

    git read-tree -u --reset $d
    
  5. commit the contents of D using the same commit message, author, date, etc. as the original D commit:

    git commit -C $d
    

    Now your repository looks like this:

          old/master
          |
          v
    A--B--C--D' <-master
    
    D--E--F <-new/master
    
  6. cherry-pick the rest of the commits:

    git cherry-pick $d..new/master
    

    Now your repository looks like this:

          old/master
          |
          v
    A--B--C--D'--E'--F' <-master
    
    D--E--F <-new/master
    
  7. Clean up:

    git remote rm old
    git remote rm new
    

    Now your repository looks like this:

    A--B--C--D'--E'--F' <-master
    
like image 57
Richard Hansen Avatar answered Oct 17 '22 07:10

Richard Hansen


Just check out your branch and run:

  git reset --hard **SHA-OF-F'**

That will remove M and D-F from your branch.

like image 29
Chronial Avatar answered Oct 17 '22 08:10

Chronial