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

41zrol4v  于 2022-10-23  发布在  Git
关注(0)|答案(9)|浏览(265)

首先,很抱歉问这个问题。关于它的主题已经很多了。但我和他们在一起运气不好。我对git不熟悉并没有多大帮助。
我正在将一个文件夹从一个git repo移动到另一个(已经存在)。例如

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

repo-2
---- dir1
---- dir2
---- dir3

最后,我希望回购看起来像

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

repo-2
---- dir1
---- dir2
---- dir3
---- dir-to-move

i、 e.在一段时间内,dir to move将存在于两个回购中。但最终我会将最新的更改迁移到repo-2,并删除dir以从repo-1移动
我最初的研究让我相信我需要使用filter分支。例如
How to move files from one git repo to another preserving history using git format-patchand git am
从那以后,我了解到“子树”取代了这种方法。然而,它并没有达到我的预期。我想我能做点什么
repo-1工作区中

git subtree split -P dir-to-move -b split

将split分支向下过滤到只有dir才能移动,这就是历史。然后在repo-2工作区中

git remote add repo-1 repo-1-url.git
git subtree add --prefix dir-to-move split

这确实会移动代码。它还包括历史
例如

cd repo-2
git log

显示来自repo-1的提交
但是

cd repo-2
git log dir-to-move

仅显示“Add dir to move from commit…”
i、 e.包含历史记录,但在检查特定文件/目录时不显示。
我怎样才能正确地做到这一点?

xxls0lw8

xxls0lw81#

我帮不了你git subtree,但有了filter-branch这是可能的。
首先,您需要创建一个包含源分支和目标分支的公共存储库。这可以通过在“源”旁边添加一个新的“远程”并从新的远程获取来完成。
在源分支上使用filter-branchrm -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

过滤器分支示例

cd repo-2
git checkout -b source-master source/master
CMD="rm -rf dir1 dir2 dir3 dir5"
git filter-branch --tree-filter "$CMD"

目标主控器中的樱桃拾取示例

cd repo-2
git checkout master
git cherry-pick `git rev-list --reverse source-master -- dir-to-move`
elcex8rz

elcex8rz2#

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

现在切换到目标存储库并从源获取所有内容。

git clone repo-2
git remote add master ../repo-1/
git pull master master --allow-unrelated-histories
git push  # push everything to remote

以上步骤假设主分支同时用于源和目标。但是,标记和分支被忽略。

ugmeyewa

ugmeyewa3#

使用git subtree确实可以做到这一点。
在repo-1中创建子树:

git subtree split -P dir-to-move -b <split>

split分支现在只包含dir-to-move目录。现在需要将分支从repo-1拉到repo-2中的分支。
如果repo-2碰巧是一个新的存储库(例如,就在git init之后),只需 checkout 一个没有历史记录的分支并从repo-1中提取即可。

cd repo-2
git checkout <branch>
git pull <path-to-repo-1.git> <split>

但是,如果repo-2是一个已经提交的现有repo(如本问题中的情况),则需要从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
9cbw7uwe

9cbw7uwe4#

我将dir-to-move移动到repo 2的方法。首先在新位置克隆repo 1,然后cd到m1n 3o1p文件夹。在我的例子中,我将文件夹从repo 1分支develop移动到repo 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

每行的说明:
1.清理所有与dir-to-move无关的提交,删除该文件夹之外的所有文件,移动根文件夹repo 1中的所有内容。从git docs
只查看与给定子目录相关的历史记录。结果将包含该目录(并且仅包含该目录)作为其项目根目录
1.您仍然指向源repo 1 url
1.将当前文件夹的源url替换为指向repo 2
1.检查URL是否已正确更新
1.将(本例中新创建的)develop分支推送到repo 2
现在,您可以克隆或提取repo 2存储库。您将在repo 2文件夹下拥有dir-to-move的内容以及预期的相关历史记录。

gudnpqoy

gudnpqoy5#

另一种解决方案是使用git filter-branch官方推荐的git-filter-repo
在我关于如何push commits affecting a given path to a new origin?的回答中找到一个完整的示例,包括windows用户的安装提示

2fjabf4q

2fjabf4q6#

我最喜欢的解决方案是http://blog.neutrino.es/2012/git-copy-a-file-or-directory-from-another-repository-preserving-history/,我已经使用多年了。然而,它似乎正在从谷歌搜索中消失,所以我担心它有一天会消失,因此我在这里复制它:

mkdir /tmp/mergepatchs
cd ~/repo/org
export reposrc=myfile.c #or mydir
git format-patch -o /tmp/mergepatchs $(git log $reposrc|grep ^commit|tail -1|awk '{print $2}')^..HEAD $reposrc
cd ~/repo/dest
git am /tmp/mergepatchs/*.patch
lxkprmvk

lxkprmvk7#

您甚至可以通过在本地删除除dir-to-move之外的所有目录(通过这种方式,无需使用fitler-branch),然后拉到repo-2来对repo-1进行提交。

git clone https://path/to/repo-1
cd repo-1
rm dir1 dir2 dir3 dir5 -rf
git commit -m "Removed unwanted directory for repo-2
cd ../
git clone https://path/to/repo-2
cd repo-2
git remote add repo-1-src ../repo-1
git pull repo-1-src master --allow-unrelated-histories

:确保git版本为2.9或更高,以便使用--allow-unrelated-histories选项

o2g1uqev

o2g1uqev8#

git:在保留历史记录的同时将文件夹从一个存储库移动到另一个存储

cd target-repo
git remote add source ../source/repo
git fetch source
git checkout -b source source/master

# filter out history of a single directory only

git filter-branch --subdirectory-filter gen/golang -- --all

# put it on top of your target repo history

git rebase master
qzwqbdag

qzwqbdag9#

哑解决方案手动解决方案(如果所有其他解决方案都失败):
1.从第一个存储库下载所有文件
1.创建新的回购
1.将第二个存储库克隆到磁盘。
1.打开要保留的第一个回购文件(新结构)
1.将更改推至第二个回购。
1.确认你有你想要的
1.从第一个存储库中删除您不想要的文件/文件夹。
你当然失去了可追溯性。。。

相关问题