问题
我想将文件夹(和子文件夹包含文件)从一个存储库移动到另一个存储库,保留历史记录。
我在SE上发现了一种方法:How to move files from one git repo to another (not a clone), preserving history。 关于blog.neutrino.es的另一个想法。这是我想在这里讨论的最后一个。
尝试解决方案
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
如果我理解正确,我的想法是假装我们通过电子邮件提交提交,并在另一个存储库中重新导入它们。
错误
执行git am /tmp/mergepatchs/*.patch
:
Applying: Initial commit
error: .gitignore: already exists in index
error: README.md: already exists in index
Patch failed at 0001 Initial commit
The copy of the patch that failed is found in:
/Users/myuser/repo/org/.git/rebase-apply/patch
When you have resolved this problem, run "git am --continue".
If you prefer to skip this patch, run "git am --skip" instead.
To restore the original branch and stop patching, run "git am --abort".
为了更好地理解这个过程,我首先尝试了一个文件(而不是整个目录)。然而"没什么"发生在git am
之后(即没有新文件,git status
没有报告任何更改)。那是为什么?
然后我尝试了:
INITCOMMIT=$(git rev-list --parents HEAD | egrep "^[a-f0-9]{40}$")
git format-patch -1 -o /tmp/mergepatchs ${INITCOMMIT}
但后来得到了和以前一样的错误信息。
为什么补丁失败?
修改1
我尝试了一些相关内容,灵感来自How to create and apply a patch with Git。
在~/repo/org
:
$ git format-patch --root HEAD --stdout myfile.c > /tmp/mergepaths/01.patch
在~/depo/dest
:
$ git apply --stat /tmp/mergepaths/01.patch
0 files changed
$ git apply --check /tmp/mergepaths/01.patch
$ git am < /tmp/mergepaths/01.patch
stat
和check
都告诉我&#34;没有&#34;将会完成。补丁对象远非空。
顺便说一句,我不知道这是否相关,但补丁的创建和应用都是在分支机构中完成的。
答案 0 :(得分:10)
这样的手术工具git filter-branch
。
对于这么简单的事情,你不需要太多的安全网。但是,作为参考,这就是我几乎可以做任何可能弄脏历史或命名空间或工作树的事情
# make a throwaway sandbox to play in:
git clone -s . /tmp/deleteme
cd !$
git checkout -b sliced
# do it
git filter-branch --subdirectory-filter your/subdir
# and if the result looks good:
git push origin sliced
你可以推送到你有网址或路径的任何仓库,只需直接使用网址,而不是为偶数工作制作一个远程名称。推送和重命名:git push u://r/l sliced:branchnameinthatrepo
从您要评论的评论中选择并重新定位子目录。这是一个非常简单的git read-tree
工作。读取树对索引进行操作,因此您需要一个索引过滤器。也就是说,这一个:
git filter-branch --index-filter '
git read-tree --prefix=des/ti/nation/ $GIT_COMMIT:source/subdir
git read-tree -m $GIT_COMMIT `git mktree </dev/null`
'
如果你只记得git是如何运作的话,尽管它不熟悉是很简单的。
作为提醒或回顾或介绍的情况可能是:
正确的存储库是一个对象存储:按类型和唯一名称(也就是SHA1)要求任何东西,回购集合强制要求它回流;要求repo记住任何内容,你输入它的类型和字节,然后它(a)存储它们,(b)为它提供唯一的名称。
索引只是一个列表,将路径名映射到存储库内容。
git read-tree
是结帐和合并和重置的基础操作 - 它实际上并不对repo进行操作,它所使用的所有对象都已存在。你给现有的树提供它,它将它们与索引中的内容结合起来(并且可选地更新工作树,虽然这里不相关)以产生你想要的索引,或者至少让你更接近一步它
上面的第一个阅读树是
git read-tree --prefix=destination/subdir/ $GIT_COMMIT:source/subdir
你可以通过计算你给它的树数来确定git read-tree
将要做的事情的基本性质。这是一个单树阅读,用于添加到索引(编辑:顺便说一句,没有选项,1-tree git read-tree
replaces the index)。这里添加的树在被过滤的提交中为source/subdir
,而read-tree将它全部添加到索引中,destination/subdir/
添加到路径名的前面。
下一个读取树是一个双树读取,它执行索引(和工作树,如果有任何你想做的)工作git checkout
- 它应用原始树之间的差异和目标树到索引。这里,原始树是$GIT_COMMIT
,目标树git mktree </dev/null
是空树。因此,操作是&#34;在索引中找到原始树中的所有内容,并使所有条目看起来与目标树完全相同。又名在这里&#34;让它们全部消失&#34;。上面添加的目标子目录不涉及,它不在原始树中,因此read-tree不会使其消失。
过滤器的完成,filter-branch提交新索引中的内容(已经在repo中的内容,请记住)以及下次提交的时间。
read-tree docs。此链接会跳过描述,这是有意的。不要读它。
答案 1 :(得分:2)
看起来您遇到的问题是,您尝试从补丁创建它们的两个存储库中都存在某些文件。在您的示例中,这些是README.md和.gitignore
使用git-apply应用补丁时,可以使用--exclude=<path-pattern>
忽略这些文件。见http://git-scm.com/docs/git-apply