我们的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
但这似乎相当复杂。有没有更好的方法来做这类事情呢?还是我采取了正确的方法?
注意,这涉及到将历史合并到现有的存储库中,而不是简单地从另一个存储库的一部分创建一个新的独立存储库(如前面的问题中所述)。
试试这个
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远程中添加新的repo并将其推到该位置
git remote remove origin <old-repo>
git remote add origin <new-repo>
git push origin <current-branch>
添加-f来覆盖
在尝试了将文件或文件夹从一个Git存储库移动到另一个存储库的各种方法后,下面概述了唯一可靠的方法。
它包括克隆要从中移动文件或文件夹的存储库,将该文件或文件夹移动到根目录,重写Git历史记录,克隆目标存储库,并将具有历史记录的文件或文件夹直接拉到目标存储库中。
阶段一
Make a copy of repository A as the following steps make major
changes to this copy which you should not push!
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)
cd into it
cd <git repository A directory>
# eg. cd /c/Working/GIT/myprojects
Delete the link to the original repository to avoid accidentally
making any remote changes (eg. by pushing)
git remote rm origin
Go through your history and files, removing anything that is not in
directory 1. The result is the contents of directory 1 spewed out
into to the base of repository A.
git filter-branch --subdirectory-filter <directory> -- --all
# eg. git filter-branch --subdirectory-filter subfolder1/subfolder2/FOLDER_TO_KEEP -- --all
For single file move only: go through what's left and remove
everything except the desired file. (You may need to delete files
you don't want with the same name and commit.)
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
第二阶段
清理步骤
Git重置——很难
清理步骤
Git gc -aggressive
清理步骤
git修剪
你可能想要将这些文件导入存储库B中的一个目录,而不是根目录:
创建那个目录
Mkdir <基本目录>;mkdir FOLDER_TO_KEEP
将文件移动到该目录
Git mv * <基本目录>git mv *文件夹to_keep
将文件添加到该目录
Git添加。
提交您的更改,我们准备将这些文件合并到
新的存储库
git提交
第三阶段
Make a copy of repository B if you don’t have one already
git clone <git repository B url>
# eg. git clone https://username@giturl/scm/projects/FOLDER_TO_KEEP.git
(assuming FOLDER_TO_KEEP is the name of the new repository you are copying to)
cd into it
cd <git repository B directory>
# eg. cd /c/Working/GIT/FOLDER_TO_KEEP
Create a remote connection to repository A as a branch in repository
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
Pull from this branch (containing only the directory you want to
move) into repository B.
git pull repo-A-branch master --allow-unrelated-histories
The pull copies both files and history. Note: You can use a merge instead of a pull, but pull works better.
Finally, you probably want to clean up a bit by removing the remote
connection to repository A
git remote rm repo-A-branch
Push and you’re all set.
git push
如果你的历史记录是正常的,你可以将提交作为补丁取出,并将它们应用到新的存储库中:
cd repository
git log --pretty=email --patch-with-stat --reverse --full-index --binary -m --first-parent -- 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 --full-index --binary -m --first-parent -- path/to/file_or_folder | (cd /path/to/new_repository && git am --committer-date-is-author-date)
提示:如果源项目子目录中的提交应该被提取到一个新的存储库根目录,git am可以被赋予-p2这样的参数来从补丁中删除额外的目录。
(摘自Exherbo的文件)