使git格式的补丁遵循git log之类的重命名--遵循

使git格式的补丁遵循git log之类的重命名--遵循,git,Git,我试图将文件从一个本地git存储库移动到另一个本地git存储库,用于不同的项目,同时保留原始存储库中的历史记录。到目前为止,如果在源repo中从未移动或重命名该文件,则该文件运行良好: # Executed from a directory in the target repository ( cd $SOURCE_REPOSITORY_DIRECTORY && git format-patch -B -M --stdout --root $SOURCE_FILENAME) |

我试图将文件从一个本地git存储库移动到另一个本地git存储库,用于不同的项目,同时保留原始存储库中的历史记录。到目前为止,如果在源repo中从未移动或重命名该文件,则该文件运行良好:

# Executed from a directory in the target repository
( cd $SOURCE_REPOSITORY_DIRECTORY && git format-patch -B -M --stdout --root $SOURCE_FILENAME) | git am --committer-date-is-author-date
这是因为两个存储库的目录结构是相同的。如果它们不一样,我就必须创建补丁文件,并使用
sed
或其他方法修复目录名

不管怎么说,在我找到一个被重命名的文件之前,这一切都太棒了。即使我指定了
-B-m
(并获得了与
-B-m-C相同的结果——更难找到副本
),我也不会在移动之前获得补丁,即使文件被干净地移动(相似性指数100%)

这尤其奇怪,因为
git log--follow
显示所有提交,而
git log--follow-p
提供所有差异。除了它以相反的顺序提供它们,所以我不能将它们输入到
gitam

还请注意,
git log--follow-p filename
将显示以下“补丁”以显示重命名:

diff --git a/old_dir_name/dir1/dir2/filename b/new_dir_name/dir0/dir1/dir2/filename
similarity index 100%
rename from old_dir_name/dir1/dir2/filename
rename to new_dir_name/dir0/dir1/dir2/filename
现在,如果
git log
将以正确的格式和顺序显示补丁,以便
git am
应用它们,我可以使用它,但事实并非如此。使用
git log--reverse--follow-p filename
只输出名称更改补丁,而不输出其他内容

那么,如何让
git格式补丁
真正按照帮助文件/手册页所说的方式重命名,同时只输出单个文件的补丁呢?
或者,如何让
git log-p
生成补丁,并将它们输入
git am
以重新创建具有历史记录的文件


我使用的是git版本1.8.4.3。

真恶心。我认为问题出在逻辑上。特别是,当您将
--reverse
--follow
组合在一起时,必须指定旧文件名:

[rename foo to bar]
$ git log --follow bar  # works
$ git log --follow --reverse -- foo # requires "--" because foo is not in HEAD
这个。。。这类作品。除此之外,当它点击重命名按钮时,它会将文件视为已删除,所有操作都会停止

tree-diff.c
包含以下功能:

static void try_to_follow_renames(...)
{
        ...
        /* Remove the file creation entry from the diff queue, and remember it */
        choice = q->queue[0];
        q->nr = 0;
如果
diff\u maybe\u\u rename
返回true,则调用该函数:

static inline int diff_might_be_rename(void)
{
        return diff_queued_diff.nr == 1 &&
                !DIFF_FILE_VALID(diff_queued_diff.queue[0]->one);
}

...
int diff_tree_sha1(...)
{
        ...
        if (!*base && DIFF_OPT_TST(opt, FOLLOW_RENAMES) && diff_might_be_rename()) {
我在这里做了一些大的假设,但是当你按照另一个顺序去做时,我们来看看是否能找到一个被重命名的
foo
,而不是刚刚创建的“file
bar
”,如果日志被反转,你需要删除“file
foo
,看看我们是否能找到一个被重命名的
bar
”,那只是。。。如果评论准确,则缺少

如果你有很多事情要做,我建议尝试在这里添加一些东西来记住差异是否被反转(就像
格式补丁
日志-反转
)一样,并根据需要更改
差异可能是重命名()
尝试遵循重命名()
代码


如果您只有一个,那么,手动整理一些差异可能更容易。:-)

我已经取得了一些进展,但现在更需要手动操作了

  • 对于每个文件,使用带和不带
    log
    ——follow
    查看哪些文件已被重命名/移动/复制(为了简单起见,将它们全部称为“重命名”)
  • 对于已重命名的文件,从
    日志
    输出中提取以前完整的路径和文件名
  • 然后使用
    格式化补丁
    ,但在命令行上给出所有旧名称和当前名称
现在我有了这样的东西:

 git format-patch -B -M -o /tmp/patches --root -- old_dir_name/dir1/dir2/filename new_dir_name/dir0/dir1/dir2/filename
这将创建修补程序以创建旧文件,将其重命名为新名称,然后继续修补该文件。当然,对我来说,问题是旧目录在新的repo中不存在,并且目录级别已经改变,因此在让目录名正常工作方面仍然存在一些麻烦


这应该更容易一些。

我最近遇到了与此问题相同的用例,我使用Bash实现了一个解决方案,因此我想与大家分享,因为此代码可能对其他人有用

它包括一个脚本
git格式的补丁程序follow
,可在 ,可用于OP的问题,如下所示:

( cd "$SOURCE_REPOSITORY_DIRECTORY" && git format-patch-follow -B -M --stdout --root --follow -- "$SOURCE_FILENAME" ) | git am --committer-date-is-author-date
更一般地说,语法是:

git format-patch-follow <options/revisions...> --follow -- <paths...>
git格式修补程序跟随--跟随--
因此,可以将此Bash脚本视为自动运行@OldPro概述的算法的方法,我特别注意处理一些特殊情况,例如带有空格的文件名、在CLI上传递的多个文件,或者从Git源repo的子目录运行脚本

最后,正如所指出的,将这样一个脚本放在一个人的
路径中就足够了,Git可以像Git子命令
Git格式补丁遵循那样集成脚本


免责声明:
git格式补丁
,从而
git格式补丁遵循
,进入非线性历史(涉及合并提交)。

恶心是对的。我在使用git时已经够辛苦的了,我不可能尝试修补源代码。不过,感谢您深入研究,我希望您能激发一些人来清理这个问题。(旁白)注意:
git log--follow
对git 2.9做了一些改进:@VonC:看起来这主要是为了修复git 2.0中引入的一些破损(当父目录和最终文件名都发生更改时)。@torek是的,考虑到git 2.0是从2014年5月开始的,这可能涉及相当多的用户。这是一个非常好的方法。我通常运行命令
grep“rename from”/tmp/patches/*
,以验证在格式化修补大量文件时没有忽略任何重命名。如果某些文件是从grepping输出的,而我的命令中没有这些文件,我知道我需要添加它们并重新运行format-patch.interest