制作git format-patch遵循重命名,如git log --follow

问题描述 投票:4回答:3

我正在尝试将文件从一个本地git存储库移动到另一个本地git存储库以用于另一个项目,同时保留原始存储库中的历史记录。到目前为止,我有这个,如果文件从未在源代码库中移动或重命名,它工作正常:

# 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 --find-copies-harder得到相同的结果),我也不会在移动之前获得补丁,即使文件被彻底移动(相似性指数100%)。

这是特别奇怪的,因为git log --follow显示所有提交,git log --follow -p提供所有差异。除了它以相反的顺序提供它们,所以我不能将它们送入git am

另请注意,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 format-patch真正按照帮助文件/手册页所说的方式重命名,同时只输出单个文件的补丁?或者,如何让git log -p以我可以将它们输入git am的方式生成补丁,以重新创建具有历史记录的文件?

我正在使用git版本1.8.4.3。

git
3个回答
2
投票

我已经取得了一些进展,但现在更加手动了。

  • 对于每个文件,使用带有和不带log--follow来查看哪些文件已被重命名/移动/复制(为简单起见,将它们全部称为“重命名”)。
  • 对于已重命名的文件,从log输出中提取以前的完整路径和文件名。
  • 然后使用format-patch,但在命令行中提供所有旧名称和当前名称。

所以现在我有这样的事情:

 git format-patch -B -M -o /tmp/patches --root -- old_dir_name/dir1/dir2/filename new_dir_name/dir0/dir1/dir2/filename

创建补丁以创建旧文件,将其重命名为新名称,然后继续修补该文件。当然,对我来说问题是旧目录没有退出新的仓库并且目录级别已经改变,所以仍然有一些与使目录名称起作用有关的问题。

这应该更容易....


2
投票

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

它由git-format-patch-follow上提供的脚本https://github.com/erikmd/git-scripts组成,可用于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...>

因此,可以将此Bash脚本视为运行@OldPro概述的算法的自动方式,并且我特别注意应对极端情况,例如具有空格的文件名,在CLI上传递的多个文件或从运行脚本的脚本Git源代码库的子目录。

最后,正如this blog post指出的那样,只需将这样的脚本放在一个用于Git的PATH中就可以像git子命令git format-patch-follow一样集成脚本。


1
投票

呸。我认为问题是一些破碎的逻辑。特别是,当您组合--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_might_be_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()) {

我在这里做了一些大的假设,但是当你进入另一个顺序时,而不是“文件bar刚创建,让我们看看我们是否可以找到一个重命名的foo”,如果日志被反转你需要有“文件foo被删除,让我们看看我们是否可以找到它被重命名的bar”,如果评论准确的话,那只是......遗失了。

如果你有很多这样的事情要做,我建议在这里添加一些东西来记住差异是否相反(如同format-patchlog --reverse)并根据需要更改diff_might_be_rename()try_to_follow_renames()代码。

如果你只有一个,那么,手动攻击一些差异可能更容易。 :-)

© www.soinside.com 2019 - 2024. All rights reserved.