How do you merge two Git repositories?

后端 未结 23 3248
耶瑟儿~
耶瑟儿~ 2020-11-21 05:45

Consider the following scenario:

I have developed a small experimental project A in its own Git repo. It has now matured, and I\'d like A to be part of larger projec

相关标签:
23条回答
  • 2020-11-21 05:51

    I merge projects slightly manually, which allows me to avoid needing to deal with merge conflicts.

    first, copy in the files from the other project however you want them.

    cp -R myotherproject newdirectory
    git add newdirectory
    

    next pull in the history

    git fetch path_or_url_to_other_repo
    

    tell git to merge in the history of last fetched thing

    echo 'FETCH_HEAD' > .git/MERGE_HEAD
    

    now commit however you normally would commit

    git commit
    
    0 讨论(0)
  • 2020-11-21 05:53

    If you want to put the files from a branch in repo B in a subtree of repo A and also preserve the history, keep reading. (In the example below, I am assuming that we want repo B's master branch merged into repo A's master branch.)

    In repo A, first do the following to make repo B available:

    git remote add B ../B # Add repo B as a new remote.
    git fetch B
    

    Now we create a brand new branch (with only one commit) in repo A that we call new_b_root. The resulting commit will have the files that were committed in the first commit of repo B's master branch but put in a subdirectory called path/to/b-files/.

    git checkout --orphan new_b_root master
    git rm -rf . # Remove all files.
    git cherry-pick -n `git rev-list --max-parents=0 B/master`
    mkdir -p path/to/b-files
    git mv README path/to/b-files/
    git commit --date="$(git log --format='%ai' $(git rev-list --max-parents=0 B/master))"
    

    Explanation: The --orphan option to the checkout command checks out the files from A's master branch but doesn't create any commit. We could have selected any commit because next we clear out all the files anyway. Then, without committing yet (-n), we cherry-pick the first commit from B's master branch. (The cherry-pick preserves the original commit message which a straight checkout doesn't seem to do.) Then we create the subtree where we want to put all files from repo B. We then have to move all files that were introduced in the cherry-pick to the subtree. In the example above, there's only a README file to move. Then we commit our B-repo root commit, and, at the same time, we also preserve the timestamp of the original commit.

    Now, we'll create a new B/master branch on top of the newly created new_b_root. We call the new branch b:

    git checkout -b b B/master
    git rebase -s recursive -Xsubtree=path/to/b-files/ new_b_root
    

    Now, we merge our b branch into A/master:

    git checkout master
    git merge --allow-unrelated-histories --no-commit b
    git commit -m 'Merge repo B into repo A.'
    

    Finally, you can remove the B remote and temporary branches:

    git remote remove B
    git branch -D new_b_root b
    

    The final graph will have a structure like this:

    0 讨论(0)
  • 2020-11-21 05:55

    I've been trying to do the same thing for days, I am using git 2.7.2. Subtree does not preserve the history.

    You can use this method if you will not be using the old project again.

    I would suggest that you branch B first and work in the branch.

    Here are the steps without branching:

    cd B
    
    # You are going to merge A into B, so first move all of B's files into a sub dir
    mkdir B
    
    # Move all files to B, till there is nothing in the dir but .git and B
    git mv <files> B
    
    git add .
    
    git commit -m "Moving content of project B in preparation for merge from A"
    
    
    # Now merge A into B
    git remote add -f A <A repo url>
    
    git merge A/<branch>
    
    mkdir A
    
    # move all the files into subdir A, excluding .git
    git mv <files> A
    
    git commit -m "Moved A into subdir"
    
    
    # Move B's files back to root    
    git mv B/* ./
    
    rm -rf B
    
    git commit -m "Reset B to original state"
    
    git push
    

    If you now log any of the files in subdir A you will get the full history

    git log --follow A/<file>
    

    This was the post that help me do this:

    http://saintgimp.org/2013/01/22/merging-two-git-repositories-into-one-repository-without-losing-file-history/

    0 讨论(0)
  • 2020-11-21 05:55

    Similar to @Smar but uses file system paths, set in PRIMARY and SECONDARY:

    PRIMARY=~/Code/project1
    SECONDARY=~/Code/project2
    cd $PRIMARY
    git remote add test $SECONDARY && git fetch test
    git merge test/master
    

    Then you manually merge.

    (adapted from post by Anar Manafov)

    0 讨论(0)
  • 2020-11-21 05:59

    git-subtree is nice, but it is probably not the one you want.

    For example, if projectA is the directory created in B, after git subtree,

    git log projectA
    

    lists only one commit: the merge. The commits from the merged project are for different paths, so they don't show up.

    Greg Hewgill's answer comes closest, although it doesn't actually say how to rewrite the paths.


    The solution is surprisingly simple.

    (1) In A,

    PREFIX=projectA #adjust this
    
    git filter-branch --index-filter '
        git ls-files -s |
        sed "s,\t,&'"$PREFIX"'/," |
        GIT_INDEX_FILE=$GIT_INDEX_FILE.new git update-index --index-info &&
        mv $GIT_INDEX_FILE.new $GIT_INDEX_FILE
    ' HEAD
    

    Note: This rewrites history, so if you intend to continue using this repo A, you may want to clone (copy) a throwaway copy of it first.

    Note Bene: You have to modify the substitute script inside the sed command in the case that you use non-ascii characters (or white characters) in file names or path. In that case the file location inside a record produced by "ls-files -s" begins with quotation mark.

    (2) Then in B, run

    git pull path/to/A
    

    Voila! You have a projectA directory in B. If you run git log projectA, you will see all commits from A.


    In my case, I wanted two subdirectories, projectA and projectB. In that case, I did step (1) to B as well.

    0 讨论(0)
  • 2020-11-21 06:00

    A single branch of another repository can be easily placed under a subdirectory retaining its history. For example:

    git subtree add --prefix=rails git://github.com/rails/rails.git master
    

    This will appear as a single commit where all files of Rails master branch are added into "rails" directory. However the commit's title contains a reference to the old history tree:

    Add 'rails/' from commit <rev>

    Where <rev> is a SHA-1 commit hash. You can still see the history, blame some changes.

    git log <rev>
    git blame <rev> -- README.md
    

    Note that you can't see the directory prefix from here since this is an actual old branch left intact. You should treat this like a usual file move commit: you will need an extra jump when reaching it.

    # finishes with all files added at once commit
    git log rails/README.md
    
    # then continue from original tree
    git log <rev> -- README.md
    

    There are more complex solutions like doing this manually or rewriting the history as described in other answers.

    The git-subtree command is a part of official git-contrib, some packet managers install it by default (OS X Homebrew). But you might have to install it by yourself in addition to git.

    0 讨论(0)
提交回复
热议问题