【问题标题】:Import a git repository with all its history into an existing git repository将 git 存储库及其所有历史记录导入现有 git 存储库
【发布时间】:2011-08-25 03:32:00
【问题描述】:

我有两个 git 存储库,我想将它们合并在一起而不丢失它们的提交历史。我试过这个:

cd firstRepo
git remote add other path/to/otherRepo
git fetch other
git checkout -b otherRepoBranch other/master
echo "`git rev-list otherRepoBranch | tail -n 1` `git rev-list master | head -n 1`" >> .git/info/grafts
git rebase otherRepoBranch master

现在,当我查看提交历史记录时,一切看起来都不错,但我的存储库中的唯一文件现在是 otherRepo 中的文件。

有什么想法吗?

【问题讨论】:

    标签: git


    【解决方案1】:

    我知道这已经很晚了,但是对于仍在寻找方法的人来说,我在这里收集了很多关于 StackOverFlow 等的信息,并设法将一个脚本放在一起,为我解决了这个问题。

    脚本实际上甚至处理功能分支和标签 - 在新项目中重命名它们,以便您知道它们来自哪里。

    我知道这已经很晚了,但是对于仍在寻找方法的人来说,我在这里收集了很多关于 StackOverFlow 等的信息,并设法将一个脚本放在一起,为我解决了这个问题。

    脚本实际上甚至处理功能分支和标签 - 在新项目中重命名它们,以便您知道它们来自哪里。

    #!/bin/bash
    #
    ################################################################################
    ## Script to merge multiple git repositories into a new repository
    ## - The new repository will contain a folder for every merged repository
    ## - The script adds remotes for every project and then merges in every branch
    ##   and tag. These are renamed to have the origin project name as a prefix
    ##
    ## Usage: mergeGitRepositories.sh <new_project> <my_repo_urls.lst>
    ## - where <new_project> is the name of the new project to create
    ## - and <my_repo_urls.lst> is a file contaning the URLs to the respositories
    ##   which are to be merged on separate lines.
    ##
    ## Author: Robert von Burg
    ##            eitch@eitchnet.ch
    ##
    ## Version: 0.2.0
    ## Created: 2015-06-17
    ##
    ################################################################################
    #
    
    # disallow using undefined variables
    shopt -s -o nounset
    
    # Script variables
    declare SCRIPT_NAME="${0##*/}"
    declare SCRIPT_DIR="$(cd ${0%/*} ; pwd)"
    declare ROOT_DIR="$PWD"
    
    
    # Detect proper usage
    if [ "$#" -ne "2" ] ; then
      echo -e "ERROR: Usage: $0 <new_project> <my_repo_urls.lst>"
      exit 1
    fi >&2
    
    
    # Script functions
    function failed() {
      echo -e "ERROR: Merging of projects failed:"
      echo -e "$1"
      exit 1
    } >&2
    
    function commit_merge() {
      current_branch="$(git symbolic-ref HEAD 2>/dev/null)"
      CHANGES=$(git status | grep "working directory clean")
      MERGING=$(git status | grep "merging")
      if [[ "$CHANGES" != "" ]] && [[ "$MERGING" == "" ]] ; then
        echo -e "INFO:   No commit required."
      else
        echo -e "INFO:   Committing ${sub_project}..."
        if ! git commit --quiet -m "[Project] Merged branch '$1' of ${sub_project}" ; then
          failed "Failed to commit merge of branch '$1' of ${sub_project} into ${current_branch}"
        fi
      fi
    }
    
    
    ## Script variables
    PROJECT_NAME="${1}"
    PROJECT_PATH="${ROOT_DIR}/${PROJECT_NAME}"
    REPO_FILE="${2}"
    REPO_URL_FILE="${ROOT_DIR}/${REPO_FILE}"
    
    
    # Make sure the REPO_URL_FILE exists
    if [ ! -e "${REPO_URL_FILE}" ] ; then
      echo -e "ERROR: Repo file ${REPO_URL_FILE} does not exist!"
      exit 1
    fi >&2
    
    
    # Make sure the required directories don't exist
    if [ -e "${PROJECT_PATH}" ] ; then
      echo -e "ERROR: Project ${PROJECT_NAME} already exists!"
      exit 1
    fi >&2
    
    
    # create the new project
    echo -e "INFO: Creating new git repository ${PROJECT_NAME}..."
    echo -e "===================================================="
    cd ${ROOT_DIR}
    mkdir ${PROJECT_NAME}
    cd ${PROJECT_NAME}
    git init
    echo "Initial Commit" > initial_commit
    # Since this is a new repository we need to have at least one commit
    # thus were we create temporary file, but we delete it again.
    # Deleting it guarantees we don't have conflicts later when merging
    git add initial_commit
    git commit --quiet -m "[Project] Initial Master Repo Commit"
    git rm --quiet initial_commit
    git commit --quiet -m "[Project] Initial Master Repo Commit"
    echo
    
    
    # Merge all projects into th branches of this project
    echo -e "INFO: Merging projects into new repository..."
    echo -e "===================================================="
    for url in $(cat ${REPO_URL_FILE}) ; do
    
      # extract the name of this project
      export sub_project=${url##*/}
      sub_project=${sub_project%*.git}
    
      echo -e "INFO: Project ${sub_project}"
      echo -e "----------------------------------------------------"
    
      # Fetch the project
      echo -e "INFO:   Fetching ${sub_project}..."
      git remote add "${sub_project}" "${url}"
      if ! git fetch --no-tags --quiet ${sub_project} 2>/dev/null ; then
        failed "Failed to fetch project ${sub_project}"
      fi
    
      # add remote branches
      echo -e "INFO:   Creating local branches for ${sub_project}..."
      while read branch ; do 
        branch_ref=$(echo $branch | tr " " "\t" | cut -f 1)
        branch_name=$(echo $branch | tr " " "\t" | cut -f 2 | cut -d / -f 3-)
    
        echo -e "INFO:   Creating branch ${branch_name}..."
    
        # create and checkout new merge branch off of master
        git checkout --quiet -b "${sub_project}/${branch_name}" master
        git reset --hard --quiet
        git clean -d --force --quiet
    
        # Merge the project
        echo -e "INFO:   Merging ${sub_project}..."
        if ! git merge --quiet --no-commit "remotes/${sub_project}/${branch_name}" 2>/dev/null ; then
          failed "Failed to merge branch 'remotes/${sub_project}/${branch_name}' from ${sub_project}"
        fi
    
        # And now see if we need to commit (maybe there was a merge)
        commit_merge "${sub_project}/${branch_name}"
    
        # relocate projects files into own directory
        if [ "$(ls)" == "${sub_project}" ] ; then
          echo -e "WARN:   Not moving files in branch ${branch_name} of ${sub_project} as already only one root level."
        else
          echo -e "INFO:   Moving files in branch ${branch_name} of ${sub_project} so we have a single directory..."
          mkdir ${sub_project}
          for f in $(ls -a) ; do
            if  [[ "$f" == "${sub_project}" ]] || 
                [[ "$f" == "." ]] || 
                [[ "$f" == ".." ]] ; then 
              continue
            fi
            git mv -k "$f" "${sub_project}/"
          done
    
          # commit the moving
          if ! git commit --quiet -m  "[Project] Move ${sub_project} files into sub directory" ; then
            failed "Failed to commit moving of ${sub_project} files into sub directory"
          fi
        fi
        echo
      done < <(git ls-remote --heads ${sub_project})
    
    
      # checkout master of sub probject
      if ! git checkout "${sub_project}/master" 2>/dev/null ; then
        failed "sub_project ${sub_project} is missing master branch!"
      fi
    
      # copy remote tags
      echo -e "INFO:   Copying tags for ${sub_project}..."
      while read tag ; do 
        tag_ref=$(echo $tag | tr " " "\t" | cut -f 1)
        tag_name=$(echo $tag | tr " " "\t" | cut -f 2 | cut -d / -f 3)
    
        # hack for broken tag names where they are like 1.2.0^{} instead of just 1.2.0
        tag_name="${tag_name%%^*}"
    
        tag_new_name="${sub_project}/${tag_name}"
        echo -e "INFO:     Copying tag ${tag_name} to ${tag_new_name} for ref ${tag_ref}..."
        if ! git tag "${tag_new_name}" "${tag_ref}" 2>/dev/null ; then
          echo -e "WARN:     Could not copy tag ${tag_name} to ${tag_new_name} for ref ${tag_ref}"
        fi
      done < <(git ls-remote --tags ${sub_project})
    
      # Remove the remote to the old project
      echo -e "INFO:   Removing remote ${sub_project}..."
      git remote rm ${sub_project}
    
      echo
    done
    
    
    # Now merge all project master branches into new master
    git checkout --quiet master
    echo -e "INFO: Merging projects master branches into new repository..."
    echo -e "===================================================="
    for url in $(cat ${REPO_URL_FILE}) ; do
    
      # extract the name of this project
      export sub_project=${url##*/}
      sub_project=${sub_project%*.git}
    
      echo -e "INFO:   Merging ${sub_project}..."
      if ! git merge --quiet --no-commit "${sub_project}/master" 2>/dev/null ; then
        failed "Failed to merge branch ${sub_project}/master into master"
      fi
    
      # And now see if we need to commit (maybe there was a merge)
      commit_merge "${sub_project}/master"
    
      echo
    done
    
    
    # Done
    cd ${ROOT_DIR}
    echo -e "INFO: Done."
    echo
    
    exit 0
    

    您也可以从http://paste.ubuntu.com/11732805/获取它

    首先创建一个包含每个存储库的 URL 的文件,例如:

      git@github.com:eitchnet/ch.eitchnet.parent.git
      git@github.com:eitchnet/ch.eitchnet.utils.git
      git@github.com:eitchnet/ch.eitchnet.privilege.git
    

    然后调用脚本,给出项目名称和脚本路径:

      ./mergeGitRepositories.sh eitchnet_test eitchnet.lst
    

    脚本本身有很多 cmets 应该解释它的作用。

    已编辑以将脚本替换为更高级的脚本。

    【讨论】:

      【解决方案2】:

      在最简单的情况下,您希望合并后来自两个存储库的所有文件,您应该能够简单地使用 git merge:

      cd firstRepo
      git remote add other path/to/otherRepo
      git fetch other
      git checkout -b merged
      git merge --allow-unrelated-histories other/master
      

      【讨论】:

      • 谢谢,杰西。从那以后,我找到了一个答案,希望很快能在这里发布。这个解决方案不是我想要的,因为合并会将提交历史汇总到单个提交中。
      • @CuriousAttemptBunny Merge 从不将提交历史汇总到单个提交中。你一定是把它和其他方法搞混了。
      【解决方案3】:

      这篇权威文章讨论了开头段落中的简单案例 - 并将更有可能的案例作为其主要主题:How to use the subtree merge strategy

      保留两个存储库的提交历史记录。


      这是我的版本——基于上面引用的文章...

      git remote add temp staging_path/(reponame)
      git fetch temp
      git fetch --tags temp ## optional -- may pull in additional history
      for remote in $(git branch -r | grep temp/ ) ; do git branch --no-track imported_$(basename $remote) $remote ; done ## create local branches prefixed with 'imported_'
      git remote rm temp ## optional -- assumes you no longer plan to use the source repo
      
      git merge -s ours --no-commit imported_master ## mysterious "merge strategy"
      git read-tree -u --prefix=(reponame)/ imported_master ## move to sub-folder
      git commit
      

      【讨论】:

      【解决方案4】:

      我认为 git 存储库不相关?就像它们是您想要合并在一起以形成组合存储库的单独项目的不同存储库一样?如果是这样,那么以下参考资料可能会有所帮助:

      Combining multiple git repositories.

      Merging two unrelated repositories.

      【讨论】:

      • 谢谢,菲尔。我已经看过那些了。从那以后,我找到了一个答案,希望很快能在这里发布。这些解决方案对我不起作用,因为它们将提交历史汇总到一个合并注释中。
      • @CuriousAttemptBunny 曾经发布过那个解决方案吗?
      • 真的很有帮助...仍在等待解决方案!
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2022-08-07
      • 1970-01-01
      • 2017-06-23
      • 1970-01-01
      • 1970-01-01
      • 2015-01-12
      相关资源
      最近更新 更多