如何将文件从一个git repo移到另一个(不是克隆),保留历史记录


483

我们的Git储存库是作为单个SVN怪物储存库的一部分开始的,其中每个项目都有自己的树,如下所示:

project1/branches
        /tags
        /trunk
project2/branches
        /tags
        /trunk

显然,使用可以很容易地将文件从一个移动到另一个svn mv。但是在Git中,每个项目都在其自己的存储库中,今天,我被要求将子目录从project2移至project1。我做了这样的事情:

$ git clone project2 
$ cd project2
$ git filter-branch --subdirectory-filter deeply/buried/java/source/directory/A -- --all
$ git remote rm origin  # so I don't accidentally overwrite the repo ;-)
$ mkdir -p deeply/buried/different/java/source/directory/B
$ for f in *.java; do 
>  git mv $f deeply/buried/different/java/source/directory/B
>  done
$ git commit -m "moved files to new subdirectory"
$ cd ..
$
$ git clone project1
$ cd project1
$ git remote add p2 ../project2
$ git fetch p2
$ git branch p2 remotes/p2/master
$ git merge p2 # --allow-unrelated-histories for git 2.9+
$ git remote rm p2
$ git push

但这似乎令人费解。总的来说,有没有更好的方法来做这种事情?还是我采用了正确的方法?

请注意,这涉及将历史记录合并到现有存储库中,而不是简单地从另一个存储库中的一部分创建新的独立存储库(如前面的问题所示)。


1
对我来说,这听起来像是一种合理的方法。我想不出任何明显的方法来显着改善您的方法。很好,Git实际上确实使这变得容易(例如,我不想尝试在Subversion中的不同存储库之间移动文件目录)。
Greg Hewgill

1
@ebneter-我已经使用外壳程序脚本手动完成了此操作(将历史记录从一个svn存储库移动到了另一个)。基本上,我将特定文件/目录的历史记录(差异,提交日志消息)重播到另一个存储库中。
亚当·蒙森,

1
我不知道为什么你不这样做git fetch p2 && git merge p2,而不是git fetch p2 && git branch .. && git merge p2?编辑:好的,看起来您想在名为p2的新分支(而不是当前分支)中进行更改。
Lekensteyn

1
有没有办法防止--filter-branch破坏目录结构?该“ git mv”步骤导致大量提交,其中包括文件删除和文件创建。
Edward Falk 2014年

1
请注意,自git 2.9起,默认情况下不允许合并无关的历史记录。要使其工作,请添加--allow-unrelated-histories到最后一个git merge使其工作。
Scott Berrevoets

Answers:


55

是的,打的--subdirectory-filterfilter-branch是关键。使用它的事实实质上证明,没有别的简单方法-您只能重写历史记录,因为您只想保留文件的一个(重命名的)子集,并且根据定义,这会更改哈希值。由于没有标准命令(例如pull)重写历史记录,因此您无法使用它们来完成此操作。

当然,您可以细化细节-并非必须进行某些克隆和分支操作-但总体方法是好的!太复杂了,真是可惜,但是,当然git的目的并不是要使重写历史记录变得容易。


1
如果您的文件已在多个目录中移动并且现在位于一个目录中,该怎么办?子目录过滤器仍可以工作吗?(即,我假设如果我只想移动一个文件,可以将其移动到其自己的子目录中,这可以工作吗?)
rogerdpack 2012年

1
@rogerdpack:不,这不会通过重命名来跟踪文件。我相信它似乎是在将它移到所选子目录中时创建的。如果您想选择只有一个文件,看看--index-filterfilter-branch手册页。
卡斯卡贝尔2012年

8
关于如何遵循重命名有什么秘诀吗?
Night Warrier'Apr

我认为维护和整理历史记录是git的重点之一。
artburkart

287

如果您的历史记录是合理的,则可以将提交作为补丁取出,并将其应用到新的存储库中:

cd repository
git log --pretty=email --patch-with-stat --reverse --full-index --binary -- path/to/file_or_folder > patch
cd ../another_repository
git am --committer-date-is-author-date < ../repository/patch 

或一行

git log --pretty=email --patch-with-stat --reverse -- path/to/file_or_folder | (cd /path/to/new_repository && git am --committer-date-is-author-date)

(摘自Exherbo的文档


21
对于需要移动的三个或四个文件,这是一个比公认的答案简单得多的解决方案。我最终使用find-replace修剪了补丁文件中的路径,以使其适合我的新仓库的目录结构。
瑞安·桑德森

8
我添加了一些选项,以便二进制文件(如图像)也可以正确迁移:git log --pretty=email --patch-with-stat --full-index --binary --reverse -- client > patch。可以正常工作AFAICT。
Emmanuel Touzery 2013年

35
在应用步骤中,我使用了--committer-date-is-author-date选项来保留原始提交日期,而不是文件移动的日期。
darrenmc 2014年

6
历史记录中的合并提交打破了“ am”命令。您可以在上面的git log命令中添加“ -m --first-parent”,然后对我有用。
伽柏Lipták

6
@Daniel金我已经成功地修复与已移动文件的问题(这是一个错误的结果git log,所以它不工作,都--follow--reverse正确的)。我用了这个答案,这是我现在用来移动文件
tsayen

75

尝试了各种方法将文件或文件夹从一个Git存储库移动到另一个Git存储库后,下面概述了唯一似乎可靠地工作的文件或文件夹。

它涉及到克隆您要从中移动文件或文件夹的存储库,将该文件或文件夹移动到根目录,重写Git历史记录,克隆目标存储库以及将具有历史记录的文件或文件夹直接拉到该目标存储库中。

第一阶段

  1. 制作存储库A的副本,因为以下步骤对此副本进行了重大更改,您不应该推送!

    git clone --branch <branch> --origin origin --progress \
      -v <git repository A url>
    # eg. git clone --branch master --origin origin --progress \
    #   -v https://username@giturl/scm/projects/myprojects.git
    # (assuming myprojects is the repository you want to copy from)
    
  2. 光盘进入

    cd <git repository A directory>
    #  eg. cd /c/Working/GIT/myprojects
    
  3. 删除指向原始存储库的链接,以避免意外进行任何远程更改(例如,通过推送)

    git remote rm origin
    
  4. 浏览您的历史记录和文件,删除目录1中没有的所有内容。结果是目录1的内容被喷入到存储库A的库中。

    git filter-branch --subdirectory-filter <directory> -- --all
    # eg. git filter-branch --subdirectory-filter subfolder1/subfolder2/FOLDER_TO_KEEP -- --all
    
  5. 仅适用于单个文件移动:浏览剩余内容并删除所需文件以外的所有内容。(您可能需要删除不想要的具有相同名称的文件并提交。)

    git filter-branch -f --index-filter \
    'git ls-files -s | grep $'\t'FILE_TO_KEEP$ |
    GIT_INDEX_FILE=$GIT_INDEX_FILE.new \
    git update-index --index-info && \
    mv $GIT_INDEX_FILE.new $GIT_INDEX_FILE || echo "Nothing to do"' --prune-empty -- --all
    # eg. FILE_TO_KEEP = pom.xml to keep only the pom.xml file from FOLDER_TO_KEEP
    

第二阶段

  1. 清理步骤

    git reset --hard
    
  2. 清理步骤

    git gc --aggressive
    
  3. 清理步骤

    git prune
    

您可能希望将这些文件导入到目录B中而不是根目录下的存储库B中:

  1. 建立该目录

    mkdir <base directory>             eg. mkdir FOLDER_TO_KEEP
    
  2. 将文件移到该目录

    git mv * <base directory>          eg. git mv * FOLDER_TO_KEEP
    
  3. 将文件添加到该目录

    git add .
    
  4. 提交更改,我们准备将这些文件合并到新的存储库中

    git commit
    

第三阶段

  1. 如果您还没有存储库B,请复制它

    git clone <git repository B url>
    # eg. git clone https://username@giturl/scm/projects/FOLDER_TO_KEEP.git
    

    (假设FOLDER_TO_KEEP是您要复制到的新存储库的名称)

  2. 光盘进入

    cd <git repository B directory>
    #  eg. cd /c/Working/GIT/FOLDER_TO_KEEP
    
  3. 创建到存储库A的远程连接作为存储库B中的分支

    git remote add repo-A-branch <git repository A directory>
    # (repo-A-branch can be anything - it's just an arbitrary name)
    
    # eg. git remote add repo-A-branch /c/Working/GIT/myprojects
    
  4. 从该分支(仅包含要移动的目录)中拉入存储库B。

    git pull repo-A-branch master --allow-unrelated-histories
    

    拉式复制文件和历史记录。注意:您可以使用合并而不是拉取,但是拉取效果更好。

  5. 最后,您可能希望通过删除与存储库A的远程连接来进行一些清理

    git remote rm repo-A-branch
    
  6. 按下即可,一切就绪。

    git push
    

我已经完成了此处概述的大多数步骤,但是,它似乎仅复制了主文件(而不是其他任何分支)的文件或目录的提交历史记录。那正确吗?
阮宝龙(Nuyen-Trong),2015年

我认为是正确的,对于要从中移动文件或文件夹的任何分支,您都必须执行类似的步骤。切换到分支 MyBranch在存储库中,筛选分支等你会那么“混帐拉回购-A-分支MyBranch”在仓库B.
mcarans

谢谢回复。您是否知道分支上的标签也将被迁移?
阮宝龙

恐怕我不知道,但会猜想他们会的。
mcarans'2

1
@mcarans不幸的是,这似乎不是可靠的方法。与所有其他解决方案一样,它也遇到相同的问题-它不保留重命名后的历史记录。就我而言,第一个提交是当我重命名目录/文件时。除此之外的一切都丢失了。
xZero

20

我发现非常有用。这是一种非常简单的方法,您可以创建应用于新存储库的补丁。有关更多详细信息,请参见链接页面。

它仅包含三个步骤(从博客复制):

# Setup a directory to hold the patches
mkdir <patch-directory>

# Create the patches
git format-patch -o <patch-directory> --root /path/to/copy

# Apply the patches in the new repo using a 3 way merge in case of conflicts
# (merges from the other repo are not turned into patches). 
# The 3way can be omitted.
git am --3way <patch-directory>/*.patch

我唯一的问题是我无法使用一次应用所有补丁

git am --3way <patch-directory>/*.patch

在Windows下,我收到一个InvalidArgument错误。所以我不得不一次又一次地应用所有补丁。


不适用于我,因为有时缺少散列。这对我有帮助:stackoverflow.com/questions/17371150/…–
dr0i

与“ git log”方法不同,此选项对我来说非常有效!谢谢!
AlejandroVD

1
尝试了将项目转移到新仓库的不同方法。这是唯一为我工作的人。不能相信如此普通的任务一定那么复杂。
Chris_D_Turk '17

感谢您分享Ross Hendrickson的博客。这种方法对我有用。
考希克·阿查里亚

1
这是一个非常优雅的解决方案,但是,它又遇到了与所有其他解决方案相同的问题-它不会保留重命名后的历史记录。
xZero

6

保留目录名称

子目录过滤器(或更短的命令git子树)效果很好,但对我却不起作用,因为它们从提交信息中删除了目录名称。在我的场景中,我只想将一个存储库的一部分合并到另一个存储库中,并保留具有完整路径名的历史记录。

我的解决方案是使用树过滤器,并从源存储库的临时克隆中简单删除不需要的文件和目录,然后通过5个简单步骤将其从克隆中提取到目标存储库中。

# 1. clone the source
git clone ssh://<user>@<source-repo url>
cd <source-repo>
# 2. remove the stuff we want to exclude
git filter-branch --tree-filter "rm -rf <files to exclude>" --prune-empty HEAD
# 3. move to target repo and create a merge branch (for safety)
cd <path to target-repo>
git checkout -b <merge branch>
# 4. Add the source-repo as remote 
git remote add source-repo <path to source-repo>
# 5. fetch it
git pull source-repo master
# 6. check that you got it right (better safe than sorry, right?)
gitk

脚本不会对您的原始存储库进行任何修改。如果映射文件中指定的dest repo不存在,则此脚本将尝试创建它。
Chetabahana

1
我还认为保持目录名称的完整性非常重要。否则,您将获得额外的重命名提交到目标存储库。
ipuustin 2015年


5

该答案提供了一些有趣的命令,这些命令基于git am示例并逐步给出。

目的

  • 您想要将部分或全部文件从一个存储库移动到另一个存储库。
  • 您想保留他们的历史。
  • 但是您并不关心保留标签和分支。
  • 您接受重命名文件(和重命名目录中的文件)的有限历史记录。

程序

  1. 使用以下格式提取电子邮件格式的历史记录
    git log --pretty=email -p --reverse --full-index --binary
  2. 重组文件树并更新历史记录中的文件名更改[可选]
  3. 应用新的历史记录,使用 git am

1.以电子邮件格式提取历史记录

例如:提取物的历史file3file4file5

my_repo
├── dirA
│   ├── file1
│   └── file2
├── dirB            ^
│   ├── subdir      | To be moved
│   │   ├── file3   | with history
│   │   └── file4   | 
│   └── file5       v
└── dirC
    ├── file6
    └── file7

清理临时目录目标

export historydir=/tmp/mail/dir  # Absolute path
rm -rf "$historydir"             # Caution when cleaning

清理您的回购

git commit ...           # Commit your working files
rm .gitignore            # Disable gitignore
git clean -n             # Simulate removal
git clean -f             # Remove untracked file
git checkout .gitignore  # Restore gitignore

提取电子邮件格式的每个文件的历史记录

cd my_repo/dirB
find -name .git -prune -o -type d -o -exec bash -c 'mkdir -p "$historydir/${0%/*}" && git log --pretty=email -p --stat --reverse --full-index --binary -- "$0" > "$historydir/$0"' {} ';'

不幸的是选项--follow--find-copies-harder不能与组合使用--reverse。这就是为什么在重命名文件(或重命名父目录)时剪切历史记录的原因。

之后:电子邮件格式的临时历史记录

/tmp/mail/dir
    ├── subdir
    │   ├── file3
    │   └── file4
    └── file5

2.重新组织文件树并更新历史记录中的文件名更改[可选]

假设您要将这三个文件移到另一个仓库中(可以是相同的仓库)。

my_other_repo
├── dirF
│   ├── file55
│   └── file56
├── dirB              # New tree
│   ├── dirB1         # was subdir
│   │   ├── file33    # was file3
│   │   └── file44    # was file4
│   └── dirB2         # new dir
│        └── file5    # = file5
└── dirH
    └── file77

因此,重新组织您的文件:

cd /tmp/mail/dir
mkdir     dirB
mv subdir dirB/dirB1
mv dirB/dirB1/file3 dirB/dirB1/file33
mv dirB/dirB1/file4 dirB/dirB1/file44
mkdir    dirB/dirB2
mv file5 dirB/dirB2

您的临时历史记录现在为:

/tmp/mail/dir
    └── dirB
        ├── dirB1
        │   ├── file33
        │   └── file44
        └── dirB2
             └── file5

还要更改历史记录中的文件名:

cd "$historydir"
find * -type f -exec bash -c 'sed "/^diff --git a\|^--- a\|^+++ b/s:\( [ab]\)/[^ ]*:\1/$0:g" -i "$0"' {} ';'

注意:这将重写历史记录以反映路径和文件名的更改。
      (即,在新仓库中更改新位置/名称)


3.应用新的历史记录

您的其他回购是:

my_other_repo
├── dirF
│   ├── file55
│   └── file56
└── dirH
    └── file77

应用来自临时历史记录文件的提交:

cd my_other_repo
find "$historydir" -type f -exec cat {} + | git am 

您的其他仓库现在是:

my_other_repo
├── dirF
│   ├── file55
│   └── file56
├── dirB            ^
│   ├── dirB1       | New files
│   │   ├── file33  | with
│   │   └── file44  | history
│   └── dirB2       | kept
│        └── file5  v
└── dirH
    └── file77

采用 git status看被推提交准备的金额:-)

注意:由于历史记录已被重写以反映路径和文件名的更改:(
      即与上一个仓库中的位置/名称进行比较)

  • 无需git mv更改位置/文件名。
  • 无需git log --follow访问完整的历史记录。

额外的技巧:在您的仓库中检测重命名/移动的文件

列出已重命名的文件:

find -name .git -prune -o -exec git log --pretty=tformat:'' --numstat --follow {} ';' | grep '=>'

更多自定义:您可以git log使用选项--find-copies-harder或完成命令--reverse。您也可以使用cut -f3-并grepping完整模式'{。* =>。*}' 删除前两列。

find -name .git -prune -o -exec git log --pretty=tformat:'' --numstat --follow --find-copies-harder --reverse {} ';' | cut -f3- | grep '{.* => .*}'

3

经历了类似的尝试(虽然仅适用于给定存储库的某些文件),该脚本确实非常有用:git-import

简短的版本是$object从现有存储库中创建给定文件或目录()的补丁文件:

cd old_repo
git format-patch --thread -o "$temp" --root -- "$object"

然后将其应用于新的存储库:

cd new_repo
git am "$temp"/*.patch 

有关详细信息,请查询:


2

尝试这个

cd repo1

这将删除除提到的目录以外的所有目录,仅保留这些目录的历史记录

git filter-branch --index-filter 'git rm --ignore-unmatch --cached -qr -- . && git reset -q $GIT_COMMIT -- dir1/ dir2/ dir3/ ' --prune-empty -- --all

现在您可以在git remote中添加新的仓库,并将其推送到

git remote remove origin <old-repo>
git remote add origin <new-repo>
git push origin <current-branch>

添加-f覆盖


警告:git-filter-branch有太多的陷阱,会生成损坏的历史记录重写。在继续中止操作之前,先按 Ctrl-C,然后使用其他过滤工具,例如'git filter-repo'(github.com/newren/git-filter-repo)。有关更多详细信息,请参见过滤器分支手册页。要消除此警告,请设置FILTER_BRANCH_SQUELCH_WARNING = 1。
科林

1

借鉴http://blog.neutrino.es/2012/git-copy-a-file-or-directory-from-another-repository-preserving-history/的灵感,我创建了此Powershell函数来执行此操作,到目前为止对我来说非常有效:

# Migrates the git history of a file or directory from one Git repo to another.
# Start in the root directory of the source repo.
# Also, before running this, I recommended that $destRepoDir be on a new branch that the history will be migrated to.
# Inspired by: http://blog.neutrino.es/2012/git-copy-a-file-or-directory-from-another-repository-preserving-history/
function Migrate-GitHistory
{
    # The file or directory within the current Git repo to migrate.
    param([string] $fileOrDir)
    # Path to the destination repo
    param([string] $destRepoDir)
    # A temp directory to use for storing the patch file (optional)
    param([string] $tempDir = "\temp\migrateGit")

    mkdir $tempDir

    # git log $fileOrDir -- to list commits that will be migrated
    Write-Host "Generating patch files for the history of $fileOrDir ..." -ForegroundColor Cyan
    git format-patch -o $tempDir --root -- $fileOrDir

    cd $destRepoDir
    Write-Host "Applying patch files to restore the history of $fileOrDir ..." -ForegroundColor Cyan
    ls $tempDir -Filter *.patch  `
        | foreach { git am $_.FullName }
}

此示例的用法:

git clone project2
git clone project1
cd project1
# Create a new branch to migrate to
git checkout -b migrate-from-project2
cd ..\project2
Migrate-GitHistory "deeply\buried\java\source\directory\A" "..\project1"

完成此操作后,可以migrate-from-project2在合并分支之前重新组织分支上的文件。


1

我想要一些健壮且可重用的功能(一个命令执行+撤消功能),所以我编写了以下bash脚本。为我工作过几次,所以我想在这里分享。

它是可以移动的任意文件夹/path/to/foo,从repo1进入/some/other/folder/barrepo2(文件夹路径可以相同或不同,与根文件夹的距离可以不同)。

由于它只遍历接触输入文件夹中文件的提交(而不遍及源存储库的所有提交),因此即使在大型源存储库中,即使您提取了一个深层嵌套的子文件夹,而每个子文件夹都未涉及到该子文件夹,它也应该相当快承诺。

由于这样做是用所有旧存储库的历史记录创建一个孤立的分支,然后将其合并到HEAD,因此即使文件名冲突也可以使用(然后您必须在最后解决一个合并问题) 。

如果没有文件名冲突,则只需 git commit最后完成合并即可。

不利之处在于,它可能不会遵循文件重命名( REWRITE_FROM源存储库中的文件夹)-GitHub上的pull request来适应这种情况。

GitHub链接:git-move-folder-between-repos-keep-history

#!/bin/bash

# Copy a folder from one git repo to another git repo,
# preserving full history of the folder.

SRC_GIT_REPO='/d/git-experimental/your-old-webapp'
DST_GIT_REPO='/d/git-experimental/your-new-webapp'
SRC_BRANCH_NAME='master'
DST_BRANCH_NAME='import-stuff-from-old-webapp'
# Most likely you want the REWRITE_FROM and REWRITE_TO to have a trailing slash!
REWRITE_FROM='app/src/main/static/'
REWRITE_TO='app/src/main/static/'

verifyPreconditions() {
    #echo 'Checking if SRC_GIT_REPO is a git repo...' &&
      { test -d "${SRC_GIT_REPO}/.git" || { echo "Fatal: SRC_GIT_REPO is not a git repo"; exit; } } &&
    #echo 'Checking if DST_GIT_REPO is a git repo...' &&
      { test -d "${DST_GIT_REPO}/.git" || { echo "Fatal: DST_GIT_REPO is not a git repo"; exit; } } &&
    #echo 'Checking if REWRITE_FROM is not empty...' &&
      { test -n "${REWRITE_FROM}" || { echo "Fatal: REWRITE_FROM is empty"; exit; } } &&
    #echo 'Checking if REWRITE_TO is not empty...' &&
      { test -n "${REWRITE_TO}" || { echo "Fatal: REWRITE_TO is empty"; exit; } } &&
    #echo 'Checking if REWRITE_FROM folder exists in SRC_GIT_REPO' &&
      { test -d "${SRC_GIT_REPO}/${REWRITE_FROM}" || { echo "Fatal: REWRITE_FROM does not exist inside SRC_GIT_REPO"; exit; } } &&
    #echo 'Checking if SRC_GIT_REPO has a branch SRC_BRANCH_NAME' &&
      { cd "${SRC_GIT_REPO}"; git rev-parse --verify "${SRC_BRANCH_NAME}" || { echo "Fatal: SRC_BRANCH_NAME does not exist inside SRC_GIT_REPO"; exit; } } &&
    #echo 'Checking if DST_GIT_REPO has a branch DST_BRANCH_NAME' &&
      { cd "${DST_GIT_REPO}"; git rev-parse --verify "${DST_BRANCH_NAME}" || { echo "Fatal: DST_BRANCH_NAME does not exist inside DST_GIT_REPO"; exit; } } &&
    echo '[OK] All preconditions met'
}

# Import folder from one git repo to another git repo, including full history.
#
# Internally, it rewrites the history of the src repo (by creating
# a temporary orphaned branch; isolating all the files from REWRITE_FROM path
# to the root of the repo, commit by commit; and rewriting them again
# to the original path).
#
# Then it creates another temporary branch in the dest repo,
# fetches the commits from the rewritten src repo, and does a merge.
#
# Before any work is done, all the preconditions are verified: all folders
# and branches must exist (except REWRITE_TO folder in dest repo, which
# can exist, but does not have to).
#
# The code should work reasonably on repos with reasonable git history.
# I did not test pathological cases, like folder being created, deleted,
# created again etc. but probably it will work fine in that case too.
#
# In case you realize something went wrong, you should be able to reverse
# the changes by calling `undoImportFolderFromAnotherGitRepo` function.
# However, to be safe, please back up your repos just in case, before running
# the script. `git filter-branch` is a powerful but dangerous command.
importFolderFromAnotherGitRepo(){
    SED_COMMAND='s-\t\"*-\t'${REWRITE_TO}'-'

    verifyPreconditions &&
    cd "${SRC_GIT_REPO}" &&
      echo "Current working directory: ${SRC_GIT_REPO}" &&
      git checkout "${SRC_BRANCH_NAME}" &&
      echo 'Backing up current branch as FILTER_BRANCH_BACKUP' &&
      git branch -f FILTER_BRANCH_BACKUP &&
      SRC_BRANCH_NAME_EXPORTED="${SRC_BRANCH_NAME}-exported" &&
      echo "Creating temporary branch '${SRC_BRANCH_NAME_EXPORTED}'..." &&
      git checkout -b "${SRC_BRANCH_NAME_EXPORTED}" &&
      echo 'Rewriting history, step 1/2...' &&
      git filter-branch -f --prune-empty --subdirectory-filter ${REWRITE_FROM} &&
      echo 'Rewriting history, step 2/2...' &&
      git filter-branch -f --index-filter \
       "git ls-files -s | sed \"$SED_COMMAND\" |
        GIT_INDEX_FILE=\$GIT_INDEX_FILE.new git update-index --index-info &&
        mv \$GIT_INDEX_FILE.new \$GIT_INDEX_FILE" HEAD &&
    cd - &&
    cd "${DST_GIT_REPO}" &&
      echo "Current working directory: ${DST_GIT_REPO}" &&
      echo "Adding git remote pointing to SRC_GIT_REPO..." &&
      git remote add old-repo ${SRC_GIT_REPO} &&
      echo "Fetching from SRC_GIT_REPO..." &&
      git fetch old-repo "${SRC_BRANCH_NAME_EXPORTED}" &&
      echo "Checking out DST_BRANCH_NAME..." &&
      git checkout "${DST_BRANCH_NAME}" &&
      echo "Merging SRC_GIT_REPO/" &&
      git merge "old-repo/${SRC_BRANCH_NAME}-exported" --no-commit &&
    cd -
}

# If something didn't work as you'd expect, you can undo, tune the params, and try again
undoImportFolderFromAnotherGitRepo(){
  cd "${SRC_GIT_REPO}" &&
    SRC_BRANCH_NAME_EXPORTED="${SRC_BRANCH_NAME}-exported" &&
    git checkout "${SRC_BRANCH_NAME}" &&
    git branch -D "${SRC_BRANCH_NAME_EXPORTED}" &&
  cd - &&
  cd "${DST_GIT_REPO}" &&
    git remote rm old-repo &&
    git merge --abort
  cd -
}

importFolderFromAnotherGitRepo
#undoImportFolderFromAnotherGitRepo

0

就我而言,我不需要保留要从其迁移的存储库或保留任何以前的历史记录。我有一个来自不同远程站点的同一分支的补丁

#Source directory
git remote rm origin
#Target directory
git remote add branch-name-from-old-repo ../source_directory

在这两个步骤中,我能够使另一个存储库的分支出现在同一存储库中。

最后,我将这个分支(从另一个仓库导入)设置为遵循目标仓库的主线(这样我就可以准确地区分它们)

git br --set-upstream-to=origin/mainline

现在,它的行为就像是我推过该回购协议的另一个分支一样。


0

如果有问题的文件的路径在两个存储库中相同,并且您只想移交一个文件或一小套相关文件,则使用的一种简单方法是git cherry-pick

第一步是使用将其他仓库中的提交带入您自己的本地仓库中git fetch <remote-url>。这将FETCH_HEAD指向另一个仓库的头提交。如果您想在完成其他提取操作后保留对该提交的引用,则可能需要使用对其进行标记git tag other-head FETCH_HEAD

然后,您需要为该文件创建一个初始提交(如果不存在),或者进行一次提交,以使文件进入可以与您要引入的其他存储库中的第一次提交打补丁的状态。能够使用所需的文件(git cherry-pick <commit-0>如果已commit-0引入)执行此操作,或者您可能需要“手动”构造提交。-n如果需要将初始提交修改为(例如,从不想引入的那个提交中删除文件),请添加到cherry-pick选项。

之后,您可以继续进行git cherry-pick后续的提交,-n必要时再次使用。在最简单的情况下(所有提交正是您想要的并干净地应用),您可以在cherry-pick命令行上提供完整的提交列表:git cherry-pick <commit-1> <commit-2> <commit-3> ...


0

通过使用git-filter-repo,这变得更加简单。

为了移动project2/sub/dirproject1/sub/dir

# Create a new repo containing only the subdirectory:
git clone project2 project2_subdir
cd project2_subdir
git filter-repo --force --path sub/dir

# Merge the new repo:
cd ../project1
git remote add project2_subdir ../project2_subdir/
git merge remotes/project2_subdir/master --allow-unrelated-histories
git remote remove project2_subdir

要简单地安装该工具,请执行以下操作pip3 install git-filter-repo :(自述文件中的更多详细信息和选项

# Before: (root)
.
|-- project1
|   `-- 3
`-- project2
    |-- 1
    `-- sub
        `-- dir
            `-- 2

# After: (project1)
.
├── 3
└── sub
    └── dir
        └── 2

-2

通过以下方法通过维护所有分支和保留历史记录将我的GIT Stash迁移到GitLab。

将旧存储库克隆到本地。

git clone --bare <STASH-URL>

在GitLab中创建一个空的存储库。

git push --mirror <GitLab-URL>

当我们将代码从存储库迁移到GitLab时,我执行了以上操作,并且效果很好。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.