将一个带有所有历史记录的Git仓库导入到现有的Git仓库中。

24

我有两个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 的文件。

有任何想法吗?

5个回答

13

谢谢,Phil。我已经看过那些了。之后我找到了一个答案,希望能很快在这里发布。那些解决方案对我来说不起作用,因为它们将提交历史整合成了一个合并评论。 - Curious Attempt Bunny
14
@CuriousAttemptBunny发布了那个解决方案吗? - Michael Paulukonis
1
非常有帮助...仍在等待解决方案! - weirdalsuperfan

8

在最简单的情况下,如果您想要合并后来自两个存储库的所有文件,则应该可以简单地使用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

1
谢谢,Jesse。我已经找到一个答案,希望很快能在这里发布。这种解决方案不是我想要的,因为合并将把提交历史记录合并为一个提交。 - Curious Attempt Bunny
3
@CuriousAttemptBunny Merge 操作永远不会将提交历史记录合并为单个提交。您可能将其与其他方法混淆了。 - Robin Green

8

这篇权威文章首先讲解了简单情况,但其主要内容是更可能出现的情况:如何使用子树合并策略

两个仓库的提交历史都被保留。


以下是基于上述参考文章的版本...

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

1
现在您需要将 --allow-unrelated-histories 添加到 git merge 命令中:https://dev59.com/ploU5IYBdhLWcg3wAjYs#37938036 - Fernando Correia

2
我知道现在可能有些晚了,但是如果还有人在寻找一种方法来做这件事,我已经在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

脚本本身有很多注释,可以解释它所做的事情。
编辑以替换为更优秀的脚本。

0

以下是对我有效的方法,包括提交历史记录:

  1. 克隆您想要导入的存储库并进入该存储库。
  2. 获取您的远程:

    git remote show origin

  3. 删除不相关的远程:

    git remote set-url --delete origin git@github.com:XXXX/YYYYYY.git

  4. 添加新的远程(新目标):

    git remote set-url --add origin git@github.com:YOUR/NEW.git

  5. 验证您的远程是否已更改:

    git remote show origin

  6. 推送到远程:

    git push origin main


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接