简体   繁体   English

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

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

Problem 问题

I'd like to move a folder (and subfolder contained files) from one repository to another, preserving the history. 我想将一个文件夹(和子文件夹包含文件)从一个存储库移动到另一个存储库,保留历史记录。

I found one approach on SE: How to move files from one git repo to another (not a clone), preserving history . 我在SE上找到了一种方法: 如何将文件从一个git repo移动到另一个(不是克隆),保留历史记录 And a different idea on blog.neutrino.es . 还有一个关于blog.neutrino.es的不同想法。 It is the last one that I'd like to discuss here. 这是我想在这里讨论的最后一个。

Attempted Solution 试图解决方案

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

If I understand correctly, the idea is to pretend we're going to submit the commits by e-mail, and re-import them in another repository. 如果我理解正确,我的想法是假装我们将通过电子邮件提交提交,并在另一个存储库中重新导入它们。

Error 错误

I get this error message when doing the 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".

In order to understand better the process, I tried a single file first (instead of the whole directory). 为了更好地理解这个过程,我首先尝试了一个文件(而不是整个目录)。 However "nothing" happened after git am (ie no new file, git status does not report any change). 然而,在git am之后“没有”发生(即没有新文件, git status不报告任何更改)。 Why is that? 这是为什么?

I then tried: 然后我尝试了:

INITCOMMIT=$(git rev-list --parents HEAD | egrep "^[a-f0-9]{40}$")
git format-patch -1 -o /tmp/mergepatchs ${INITCOMMIT}

But then got the same error message as before. 但后来得到了和以前一样的错误信息。

Why are the patches failing? 为什么补丁失败了?


Edit 1 编辑1

I tried something related, inspired from How to create and apply a patch with Git . 我尝试了一些相关的东西,灵感来自如何使用Git创建和应用补丁

In ~/repo/org : ~/repo/org

$ git format-patch --root HEAD --stdout myfile.c > /tmp/mergepaths/01.patch

In ~/depo/dest : ~/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 

Both stat and check tell me that "nothing" will be done. statcheck告诉我“什么都不会”。 The patch object is far from empty. 补丁对象远非空。 By the way, I don't know if this is relevant but both the creation and application of patches are done in branches. 顺便说一句,我不知道这是否相关,但补丁的创建和应用都是在分支机构中完成的。

There's a tool for surgery like this, git filter-branch . 有一个像这样的手术工具, git filter-branch

For something this simple, you don't need much of a safety net. 对于这么简单的事情,你不需要太多的安全网。 For reference, though, this is how I do pretty much anything that might dirty up history or namespace or worktree 但是,作为参考,这就是我如何做任何可能弄脏历史或命名空间或工作树的东西

# 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

filter-branch docs filter-branch docs

and you can push to any repo you have a url or a path for, just use the url directly instead of making a remote name for onesy-twosie work. 并且你可以推送到你有网址或路径的任何仓库,只需直接使用网址,而不是为一对二的工作制作一个远程名称。 To push and rename: git push u://r/l sliced:branchnameinthatrepo 推送和重命名: git push u://r/l sliced:branchnameinthatrepo


From comments you want to both select and relocate the subdirectory. 从您想要的注释中选择并重新定位子目录。 That's some fairly straightforward git read-tree work. 这是一些相当简单的git read-tree工作。 Read-tree operates on the index, so you want an index filter for it. 读取树对索引进行操作,因此您需要一个索引过滤器。 To wit, this one: 即,这一个:

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`
'

which despite its unfamiliarity is quite simple if you just recall how git works. 如果你只记得git是如何工作的,那么尽管它不熟悉是很简单的。

As reminder or recap or intro as the case may be: 作为提醒或回顾或介绍的情况可能是:

  1. The repository proper is an object store: ask for anything by type and unique name (aka SHA1), the repo obligingly regurgitates it; 存储库本身就是一个对象存储:按类型和唯一名称(也就是SHA1)要求任何东西,回购服务器强制要求它回流; ask the repo to remember any content, you feed it type and bytes and it (a) stores that and (b) feeds you the unique name for it. 要求repo记住任何内容,你输入它的类型和字节,它(a)存储它们和(b)为它提供唯一的名称。

  2. The index is just a list, mapping pathnames to the repository content that goes there. 索引只是一个列表,将路径名映射到存储库内容。

  3. git read-tree is the underlying operation for checkout and merge and reset -- it doesn't actually operate on the repo, all the objects it works with are already there. git read-tree是checkout和merge and reset的基础操作 - 它实际上并不在repo上运行,它所使用的所有对象都已存在。 You feed it existing trees, it combines them with what's in the index (and optionally updates the worktree, though that's irrelevant here) to produce the index you want, or at least get you one step closer to it. 你给现有的树提供它,它将它们与索引中的内容相结合(并且可选地更新工作树,尽管这里不相关)来生成你想要的索引,或者至少让你更接近它。

The first read-tree above is 上面的第一个读树是

    git read-tree --prefix=destination/subdir/ $GIT_COMMIT:source/subdir

and you can identify the fundamental nature of what git read-tree is going to do by counting how many trees you gave it. 你可以通过计算你给它的树数来确定git read-tree要做的基本性质。 This one is a one-tree read, used here to add to the index ( edit: btw, with no options, 1-tree git read-tree replaces the index ). 这是一个单树读取,这里用来添加到索引( 编辑:顺便说一句,没有选项, 1树git read-tree替换索引 )。 The tree getting added here is at source/subdir in the commit being filtered, and read-tree adds it all to the index with destination/subdir/ added to the front of the pathnames. 这里添加的树是在被过滤的提交中的source/subdir ,并且read-tree将它全部添加到索引,其中destination/subdir/添加到路径名的前面。

The next read-tree is a two-tree read, it does the index (and worktree, if there was any you wanted to do here) work for git checkout -- it applies the differences between the original tree and the target tree to the index. 下一个读取树是一个双树读取,它执行索引(和worktree,如果你有任何想做的事情)工作git checkout - 它将原始树和目标树之间的差异应用于指数。 Here, the original tree is $GIT_COMMIT and the target tree, git mktree </dev/null , is the empty tree. 这里,原始树是$GIT_COMMIT ,目标树git mktree </dev/null是空树。 So the operation is "find everything in the original tree in the index and make all those entries look exactly like the target tree" aka here "make them all go away". 因此,操作是“在索引中查找原始树中的所有内容,并使所有条目看起来与目标树完全相同”,这里也称为“让它们全部消失”。 The added destination subdir above is not involved, it wasn't in the original tree so read-tree doesn't make that go away. 上面添加的目标子目录不涉及,它不在原始树中,因此read-tree不会使其消失。

The filter's done, filter-branch commits what's in the new index (of content already in the repo, remember) and it's time for the next commit. 过滤器完成后,filter-branch提交新索引中的内容(已经在repo中的内容,请记住),并且是下次提交的时间。

read-tree docs . 读树文档 This link skips the description, that's intentional. 此链接会跳过描述,这是有意的。 Don't read it. 不读它。

It looks like the problem you are having is that certain files exist in both repositories you are trying to then create them from the patch. 看起来您遇到的问题是,您尝试从补丁中创建它们的两个存储库中都存在某些文件。 In your example these are README.md and .gitignore 在您的示例中,这些是README.md和.gitignore

When applying the patch with git-apply, you can ignore these files with --exclude=<path-pattern> . 使用git-apply应用补丁时,可以使用--exclude=<path-pattern>忽略这些文件。 See http://git-scm.com/docs/git-apply 请参阅http://git-scm.com/docs/git-apply

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 How to git format-patch and git am/apply to move all patches from very first to the latest on a branch in a git repo to brand new git repo - How to git format-patch and git am/apply to move all patches from very first to the latest on a branch in a git repo to brand new git repo 如何将一些文件从一个 git 存储库移动到另一个(不是克隆),保留历史记录 - How to move some files from one git repo to another (not a clone), preserving history 在`git format-patch`,`git am`,`git pull`之后,在git历史中提交了两次提交 - Double commits in git history after `git format-patch`,`git am`, `git pull` 如何将特定文件从一个git存储库复制到另一个,从而保留历史记录 - How to copy specific files from one git repo to another, preserving history 将文件夹从一个存储库复制/移动到另一个存储库,同时保留 Git 中的历史记录 - Copy/Move a folder from one repo to another repo while preserving history in Git 将子文件夹从一个 git repo 移动到另一个保留历史记录 - Moving subfolder from one git repo to another preserving history 如何将一个 repo 移动到另一个 repo 文件夹并保留 git 历史记录? - How to move one repo to another repo folder and keep git history? Git格式补丁查看器? - Git format-patch viewer? git format-patch查询 - git format-patch query 如何防止 git 格式补丁截断文件名 - How to prevent git format-patch from truncating filenames
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM