git将目录移动到另一个存储库,同时保留历史记录

Sha*_*non 11 git

首先 - 向这个问题道歉.关于它的话题已经很多了.但我对他们没有太多运气.我对git缺乏熟悉并没有多大帮助.

我正在将一个文件夹从一个git repo移动到另一个(已经存在).例如

repo-1
---- dir1
---- dir2
---- dir3
---- dir-to-move
---- dir5

repo-2
---- dir1
---- dir2
---- dir3
Run Code Online (Sandbox Code Playgroud)

最后我希望回购看起来像

repo-1
---- dir1
---- dir2
---- dir3
---- dir-to-move
---- dir5

repo-2
---- dir1
---- dir2
---- dir3
---- dir-to-move
Run Code Online (Sandbox Code Playgroud)

也就是说,在两个回购中都存在dir-to-move.但最终我会将最新的更改迁移到repo-2并从repo-1中移除dir-to-move.

我最初的研究让我相信我需要使用filter-branch.例如

如何使用`git format-patch`和`git am`将文件从一个git repo移动到另一个保存历史记录

我已经知道子树取代了这种方法.然而,它并没有达到我的预期.我以为我能够做类似的事情

repo-1工作区中

git subtree split -P dir-to-move -b split
Run Code Online (Sandbox Code Playgroud)

拆分分支过滤到仅dir-to-move及其历史记录.然后在repo-2工作区

git remote add repo-1 repo-1-url.git
git subtree add --prefix dir-to-move split
Run Code Online (Sandbox Code Playgroud)

这确实会移动代码.它也有,包括历史

例如

cd repo-2
git log
Run Code Online (Sandbox Code Playgroud)

显示repo-1的提交

cd repo-2
git log dir-to-move
Run Code Online (Sandbox Code Playgroud)

仅显示"从提交添加目标移动..."

即包含历史记录但在检查特定文件/目录时不显示.

我该怎么做呢?

Thy*_*ymo 73

这确实可以使用git subtree.

在 repo-1 中创建一个子树:

git subtree split -P dir-to-move -b <split>
Run Code Online (Sandbox Code Playgroud)

split分支现在仅包含该dir-to-move目录。您现在需要将分支从 repo-1 拉取到 repo-2 中的分支。

如果 repo-2 恰好是一个新存储库(例如,就在 之后git init),事情就像检查没有历史记录的分支并从 repo-1 中提取一样简单。

cd repo-2
git checkout <branch>
git pull <path-to-repo-1.git> <split>
Run Code Online (Sandbox Code Playgroud)

但是,如果 repo-2 是已进行提交的现有存储库(如本问题中的情况),则需要从 repo-2 中的孤立分支进行合并:

cd repo-2
git checkout --orphan <temp>                    # Create a branch with no history
git pull <path-to-repo-1.git> <split>           # Pull the commits from the subtree
git checkout <branch>                           # Go back to the original branch
git merge --allow-unrelated-histories <temp>    # Merge the unrelated commits back
git branch -d <temp>                            # Delete the temporary branch
Run Code Online (Sandbox Code Playgroud)

  • `git pull &lt;path-to-repo-1.git&gt; &lt;split&gt;` 生成错误“致命:使用添加到索引的更改更新未出生的分支。” 有关该错误的文档似乎不可用。 (4认同)
  • 这是对我最有帮助的答案。我想补充一点, --allow-unrelated-histories 也可以直接在 git pull 命令上使用,如果您的 repo-2 中已经有相同的文件,这可能会导致一些合并冲突,但如果它是很容易解决这些冲突,那么这是最快的解决方案,我认为。 (3认同)
  • 这对我来说效果很好,但丢失了父文件夹。找到了解决方案[此处](/sf/ask/2305847561/) (3认同)

bas*_*sin 9

我无法帮助你git subtree,但有filter-branch可能.

首先,您需要创建一个包含源和目标分支的公共存储库.这可以通过在"origin"旁边添加一个新的"remote"并从新的遥控器中获取来完成.

filter-branch在源分支上使用rm -rf除以外的所有目录dir-to-move.之后,您将拥有一个可以干净地重新定位或合并到目标分支的提交历史记录.我认为最简单的方法是cherry-pick从源分支进行所有非空提交.可以通过运行获得这些提交的列表git rev-list --reverse source-branch -- dir-to-move

当然,如果历史dir-to-move是非线性的(已经包含合并提交),那么它将不会被cherry-pick保留,因此git merge可以使用.

示例创建公共仓库:

cd repo-2
git remote add source ../repo-1
git fetch source
Run Code Online (Sandbox Code Playgroud)

示例过滤器分支

cd repo-2
git checkout -b source-master source/master
CMD="rm -rf dir1 dir2 dir3 dir5"
git filter-branch --tree-filter "$CMD"
Run Code Online (Sandbox Code Playgroud)

樱桃挑选目的地主人

cd repo-2
git checkout master
git cherry-pick `git rev-list --reverse source-master -- dir-to-move`
Run Code Online (Sandbox Code Playgroud)

  • 似乎是唯一的解决方案。我遵循这里的方法http://blog.mattsch.com/2015/06/19/move-directory-from-one-repository-to-another-preserving-history/ (3认同)
  • 值得注意的是,如果之前在 repo-1 中移动过 `dir-to-move` 的内容,则此解决方案会将这些文件的历史记录截断为仅当它们存在于 `dir-to-move` 位置时在 repo-1 中。 (3认同)
  • 对于 2021 年或以后来到这里的任何人,请注意,在过去几年中,git 约定已更改为使用“main”而不是“master”,因此您可能需要将出现的“master”替换为“main”这个答案的代码。 (2认同)

don*_*nie 9

FWIW,经过多次迭代后,以下工作对我有用.

将两个仓库克隆到临时工作区.

git clone <repourl>/repo-1.git 
git clone <repourl>/repo-2.git
cd repo-1
git remote rm origin # delete link to original repository to avoid any accidental remote changes
git filter-branch --subdirectory-filter dir-to-move -- --all  # dir-to-move is being moved to another repo.  This command goes through history and files, removing anything that is not in the folder.  The content of this folder will be moved to root of the repo as a result. 
# This folder has to be moved to another folder in the target repo.  So, move everything to another folder.
git filter-branch -f --index-filter \
'git ls-files -s | /usr/local/bin/sed -e "s/\t\"*/&dir-to-move\//" |
    GIT_INDEX_FILE=$GIT_INDEX_FILE.new \
        git update-index --index-info &&
 mv "$GIT_INDEX_FILE.new" "$GIT_INDEX_FILE"' HEAD
# Above command will go through history and rewrite the history by adding dir-to-move/ to each files.  As a result, all files will be moved to a subfolder /dir-to-move.  Make sure to use gnu-sed as OSX sed doesn't handle some extensions correctly.  For eg. \t
Run Code Online (Sandbox Code Playgroud)

现在切换到目标repo并从源获取所有内容.

git clone repo-2
git remote add master ../repo-1/
git pull master master --allow-unrelated-histories
git push  # push everything to remote 
Run Code Online (Sandbox Code Playgroud)

上述步骤假设主分支用于源和目标.但是,标签和分支被忽略了.

  • 我在 windows + git bash 上遇到了这种方法的问题。我用实际的 `sed` 命令替换了 `usr\bin` 路径的路径,但是我得到了 `Rewrite ... mv: cannot stat `my-repo-local-path/.git-rewrite/t/../index .new': 没有这样的文件或目录` (2认同)

Gab*_*biM 5

我搬到的dir-to-move方式repo 2。首先将克隆到repo 1一个新位置,然后将cd repo 1复制到该文件夹。就我而言,我的文件夹移动repo 1分支developrepo 2这里develop是不是存在呢。

git filter-branch --subdirectory-filter dir-to-move -- --all     #line 1
git remote -v                                                    #line 2
git remote set-url origin repo_2_remote_url                      #line 3
git remote -v                                                    #line 4
git push origin develop                                          #line 5
Run Code Online (Sandbox Code Playgroud)

每行的说明:

  1. 清除所有不相关的提交dir-to-move,删除该文件夹之外的所有文件,将所有内容移动到根文件夹中repo 1。从git docs

只查看涉及给定子目录的历史记录。结果将包含该目录(仅该目录)作为其项目根目录

  1. 您仍指向您的原始repo 1网址
  2. 替换当前文件夹的原始网址以指向 repo 2
  3. 检查网址是否已正确更新
  4. 推送到repo 2(在这种情况下,是新创建的)develop分支

现在,您可以克隆或提取或获取您的repo 2存储库。您将在repo 2文件夹下dir-to-move拥有预期内容以及相关历史记录。


Hug*_*ins 5

您可以使用git format-patch, 后接git am:

mkdir /tmp/mergepatchs
cd ~/repo/org
git format-patch -o /tmp/mergepatchs --root dir_to_move1 dir_to_move2 dir_to_move3
cd ~/repo/dest
git am /tmp/mergepatchs/*.patch
Run Code Online (Sandbox Code Playgroud)

感谢http://blog.neutrino.es/2012/git-copy-a-file-or-directory-from-another-repository-preserving-history/建议采用这种方法,并感谢@sean 在下面的评论,为了改进它:)

  • 更简单:`git format-patch -o /tmp/mergepatchs --root $reposrc` (3认同)