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

/project/xyz

to

/components/xyz

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


当前回答

可以重命名文件并保持历史记录不变,尽管这会导致文件在存储库的整个历史记录中被重命名。这可能只适用于痴迷于git日志的爱好者,并有一些严重的影响,包括:

您可以重写共享历史,这是使用Git时最重要的“不要”。如果其他人克隆了存储库,您将通过此操作将其破坏。他们将不得不重新克隆以避免头痛。如果重命名足够重要,这可能是可以的,但您需要仔细考虑这一点——您可能会扰乱整个开源社区!如果您在存储库历史记录的早期使用文件的旧名称引用了该文件,那么实际上就是在破坏早期版本。为了解决这个问题,你必须多跳一点环跳。这不是不可能的,只是乏味而且可能不值得。

现在,既然你还和我在一起,你可能是一个单独的开发人员,正在重命名一个完全独立的文件。让我们使用过滤器树移动文件!

假设您要将一个旧文件移动到文件夹目录中,并将其命名为new

这可以用git-mv-old-dir/new&&git-add-u-dir/new来完成,但这打破了历史。

相反:

git filter-branch --tree-filter 'if [ -f old ]; then mkdir dir && mv old dir/new; fi' HEAD

将重做分支中的每个提交,在每个迭代的滴答声中执行命令。当你这样做的时候,很多事情都会出错。我通常会测试文件是否存在(否则它还没有移动),然后执行必要的步骤,按照我的喜好强行插入树。在这里,您可能会浏览文件以更改对文件的引用等等

完成后,文件将被移动,日志将完好无损。你觉得自己像忍者海盗。

而且当然,只有当您将文件移动到新文件夹时,mkdir目录才是必需的。if将避免在文件存在之前创建此文件夹。

其他回答

git log --follow [file]

将通过重命名向您展示历史。

可以重命名文件并保持历史记录不变,尽管这会导致文件在存储库的整个历史记录中被重命名。这可能只适用于痴迷于git日志的爱好者,并有一些严重的影响,包括:

您可以重写共享历史,这是使用Git时最重要的“不要”。如果其他人克隆了存储库,您将通过此操作将其破坏。他们将不得不重新克隆以避免头痛。如果重命名足够重要,这可能是可以的,但您需要仔细考虑这一点——您可能会扰乱整个开源社区!如果您在存储库历史记录的早期使用文件的旧名称引用了该文件,那么实际上就是在破坏早期版本。为了解决这个问题,你必须多跳一点环跳。这不是不可能的,只是乏味而且可能不值得。

现在,既然你还和我在一起,你可能是一个单独的开发人员,正在重命名一个完全独立的文件。让我们使用过滤器树移动文件!

假设您要将一个旧文件移动到文件夹目录中,并将其命名为new

这可以用git-mv-old-dir/new&&git-add-u-dir/new来完成,但这打破了历史。

相反:

git filter-branch --tree-filter 'if [ -f old ]; then mkdir dir && mv old dir/new; fi' HEAD

将重做分支中的每个提交,在每个迭代的滴答声中执行命令。当你这样做的时候,很多事情都会出错。我通常会测试文件是否存在(否则它还没有移动),然后执行必要的步骤,按照我的喜好强行插入树。在这里,您可能会浏览文件以更改对文件的引用等等

完成后,文件将被移动,日志将完好无损。你觉得自己像忍者海盗。

而且当然,只有当您将文件移动到新文件夹时,mkdir目录才是必需的。if将避免在文件存在之前创建此文件夹。

我愿意:

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

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

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

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

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

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

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。