我想重命名/移动Git中的项目子树

/project/xyz

to

/components/xyz

如果我使用普通的gitmv项目组件,那么xyz项目的所有提交历史都会丢失。有没有一种方法可以移动它,以保持历史?


当前回答

Git检测重命名,而不是通过提交持久化操作,因此使用gitmv还是mv并不重要。

log命令采用--follow参数,该参数在重命名操作之前继续历史,即,它使用启发式搜索类似的内容。

要查找完整的历史记录,请使用以下命令:

git log --follow ./path/to/file

其他回答

No.

简短的答案是否定的。在Git中重命名文件并记住历史是不可能的。这是一种痛苦。

有传言说,git log--follow--查找副本会更加困难,但这对我来说不起作用,即使文件内容没有任何更改,而且这些移动都是用git mv完成的。

(最初,我使用Eclipse在一次操作中重命名和更新包,这可能会混淆Git。但这是一件非常常见的事情。--如果只执行mv,然后提交mv,并且mv不太远,那么follow似乎确实有效。)

Linus表示,您应该全面了解软件项目的全部内容,而不需要跟踪单个文件。不幸的是,我的小脑袋无法做到这一点。

这么多人无意识地重复了Git自动跟踪移动的说法,这真的很烦人。他们浪费了我的时间。Git没有这样做。根据设计(!)Git根本不跟踪移动。

我的解决方案是将文件重命名回其原始位置。更改软件以适合源代码管理。使用Git,您似乎只需要第一次“获取”它。

不幸的是,这打破了Eclipse,它似乎使用了--follow。gitlog--follow有时不会显示具有复杂重命名历史的文件的完整历史记录,尽管gitlog会显示。(我不知道为什么。)

(有一些过于聪明的黑客会重新开始旧的工作,但它们相当可怕。请参阅GitHub Gist:emiller/git mv with history。)

简而言之:如果Subversion这样做是错误的,那么Git这样做也是错误的——这样做不是什么(错误!)功能,这是错误的。

我愿意:

git mv {old} {new}
git add -u {new}

要重命名目录或文件(我不太了解复杂的情况,因此可能需要注意):

git filter-repo --path-rename OLD_NAME:NEW_NAME

要在提到目录的文件中重命名目录(可以使用回调,但我不知道如何):

git filter-repo --replace-text expressions.txt

expressions.txt是一个充满了文字:OLD_NAME==>NEW_NAME等行的文件(可以将Python的RE与regex一起使用,也可以将glob与glob一起使用)。

要重命名提交消息中的目录,请执行以下操作:

git-filter-repo --message-callback 'return message.replace(b"OLD_NAME", b"NEW_NAME")'

Python的正则表达式也受支持,但必须用Python手动编写。

如果存储库是原始的,没有远程,则必须添加--force以强制重写。(在执行此操作之前,您可能需要创建存储库的备份。)

如果不想保留引用(它们将显示在GitGUI的分支历史记录中),则必须添加--replace-refs delete no add。

Git检测重命名,而不是通过提交持久化操作,因此使用gitmv还是mv并不重要。

log命令采用--follow参数,该参数在重命名操作之前继续历史,即,它使用启发式搜索类似的内容。

要查找完整的历史记录,请使用以下命令:

git log --follow ./path/to/file

我想重命名/移动Git中的项目子树/项目/xyz到/组件/xyz如果我使用普通的gitmv项目组件,那么xyz项目的所有提交历史都会丢失。

没有(8年后,Git 2.19,2018年第3季度),因为Git会检测到目录重命名,现在已经有了更好的记录。

参见Elijah Newren(Newren)提交的提交b00bf1c、提交1634688、提交0661e49、提交4d34dff、提交983f464、提交c840e1a、提交9929430(2018年6月27日)和提交d4e8062、提交5dacd4a(2018年7月25日)。(于2018年7月24日由Junio C Hamano(吉斯特)在提交时合并)

现在在Documentation/technical/directory-rename-detection.txt中对此进行了解释:

例子:

当所有x/a、x/b和x/c都移动到z/a、z/b和z/c时,很可能同时添加的x/d也希望通过以下方式移动到z/d提示整个目录“x”移动到“z”。

但还有很多其他情况,比如:

历史的一方重命名为x->z,另一方将某个文件重命名为x/e,导致合并需要进行传递重命名。

为了简化目录重命名检测,这些规则由Git强制执行:

当应用目录重命名检测:

如果合并的两边仍然存在给定的目录,我们不认为它已被重命名。如果要重命名的文件的子集有一个文件或目录在其中(或将相互妨碍),请“关闭”这些特定子路径的目录重命名,并向用户报告冲突。如果历史的另一端将目录重命名为您的历史的另一侧重命名的路径,那么对于任何隐式目录重命名,忽略历史另一端的特定重命名(但警告用户)。

您可以在t/t6043-merge-rename-directories.sh中看到许多测试,其中还指出:

a) 如果重命名将一个目录拆分为两个或多个其他目录,则重命名最多的目录“获胜”。b) 避免对路径进行目录重命名检测,如果该路径是合并两侧重命名的源。c) 仅在另一侧对目录应用隐式目录重命名历史是一个做更名的人。