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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/41811986/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-19 12:34:37  来源:igfitidea点击:

git move directory to another repository while keeping the history

git

提问by Shane Gannon

First - apologies for asking this question. There are a lot of topics about it already. But I'm not having much luck with them. My lack of familiarity with git is not much help.

首先 - 很抱歉问这个问题。已经有很多关于它的话题了。但我对他们的运气并不好。我对 git 的不熟悉没有多大帮助。

I'm moving a folder from one git repo to another (which already exists). e.g.

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

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

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

In the end I want the repos to look like

最后我希望回购看起来像

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

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

i.e. For a time dir-to-movewill exist in both repos. But eventually I'll migrate the latest changes to repo-2and remove dir-to-movefrom repo-1.

即一段时间内,两个存储库中都将存在dir-to-move。但最终我会将最新的更改迁移到repo-2并从repo-1 中删除dir-to-move

My initial research made me believe that I needed to use filter-branch. e.g.

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

How to move files from one git repo to another preserving history using `git format-patch`and `git am`

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

I've since learnt that subtreesuperseded that approach. However it's not doing what I expected. I thought I'd be able to do something like

从那以后,我了解到子树取代了这种方法。但是,它没有按照我的预期进行。我以为我可以做类似的事情

In repo-1workspace

repo-1工作区中

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

to filter the splitbranch down to only dir-to-moveand it's history. Then in repo-2workspace

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

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

This does move the code across. It also, sort of, includes the history

这确实移动了代码。它还包括历史

e.g.

例如

cd repo-2
git log

Shows commits from repo-1

显示来自repo-1 的提交

but

cd repo-2
git log dir-to-move

Shows only an 'Add dir-to-move from commit ....'

仅显示“从提交添加目录移动....”

i.e. The history is included but does not show up when checked for the specific files/directories.

即历史被包括在内,但在检查特定文件/目录时不显示。

How can I do this properly?

我怎样才能正确地做到这一点?

采纳答案by basin

I can't help you with git subtree, but with filter-branchit's possible.

我不能帮你git subtree,但有filter-branch可能。

First you need to create a common repository that will contain both source and destination branches. This can be done by adding a new "remote" beside "origin" and fetching from the new remote.

首先,您需要创建一个包含源分支和目标分支的公共存储库。这可以通过在“来源”旁边添加一个新的“远程”并从新的远程获取来完成。

Use filter-branchon the source branch to rm -rfall directories except dir-to-move. After that you'll have a commit history that can be cleanly rebased or merged into the destination branch. I think the easiest way is to cherry-pickall non-empty commits from the source branch. The list of these commits can be obtained by running git rev-list --reverse source-branch -- dir-to-move

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

Of course, if the history of dir-to-moveis non-linear (already contains merge commits), then it won't be preserved by cherry-pick, so git mergecan be used instead.

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

Example create common repo:

示例创建通用存储库:

cd repo-2
git remote add source ../repo-1
git fetch source

Example filter branch

示例过滤器分支

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

Example cherry-pick into destination master

示例cherry-pick 到目标主机

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

回答by donnie

FWIW, the following worked for me after a number of iterations.

FWIW,经过多次迭代后,以下内容对我有用。

Clone both the repos to a temporary work area.

将两个 repos 克隆到临时工作区。

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

Now switch to target repo and fetch everything from source.

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

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

Above steps assume that master branches are used for both source and targets. However, tags and branches were ignored.

以上步骤假设主分支用于源和目标。然而,标签和分支被忽略了。

回答by GabiM

My approach to move dir-to-moveto repo 2. Start by cloning the repo 1in a new location and cd to the repo 1folder. In my case I am moving the folder from repo 1branch developto repo 2where developis not existent yet.

我的方法移动dir-to-moverepo 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

Explanation for each line:

每行说明:

  1. Cleans all commits not related to dir-to-move, removes all files outside that folder, moves all contents in the root folder repo 1. From git docs:
  1. 清除与 无关的所有提交dir-to-move,删除该文件夹外的所有文件,移动根文件夹中的所有内容repo 1。从git 文档

Only look at the history which touches the given subdirectory. The result will contain that directory (and only that) as its project root

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

  1. You are still pointing to your origin repo 1url
  2. Replace the origin url for your current folder to point to repo 2
  3. Check the URLs have been updated correctly
  4. Push to repo 2the (newly created, in this case) developbranch
  1. 您仍然指向您的原始repo 1网址
  2. 替换当前文件夹的原始 url 指向 repo 2
  3. 检查 URL 是否已正确更新
  4. 推送到repo 2(在本例中是新创建的)develop分支

You can now clone or pull or fetch your repo 2repository. You will have under repo 2folder the contents of dir-to-movealong with relevant history as expected.

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

回答by rashok

You can even make a commit on repo-1by deleting all directory other than dir-to-movelocally (by this way no need to use fitler-branch) and then pull to repo-2.

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

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

Note: Make sure gitversion is 2.9or more for using --allow-unrelated-historiesoption

注意:确保git版本是2.9或更多用于使用--allow-unrelated-histories选项