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

/project/xyz

to

/components/xyz

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


当前回答

我愿意:

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

其他回答

虽然作为Git的核心,Git管道不会跟踪重命名,但如果你愿意,你用Git日志“瓷器”显示的历史可以检测它们。

对于给定的git日志,请使用-M选项:

git-log-p-M

使用当前版本的Git。

这也适用于其他命令,如gitdiff。

有一些选项可以使比较更严格或更不严格。如果您在重命名文件的同时不对文件进行重大更改,则Git日志和好友可以更容易地检测到重命名。出于这个原因,有些人在一次提交中重命名文件,在另一次提交时更改文件。

每当你要求Git查找文件被重命名的位置时,CPU的使用都是有成本的,所以你是否使用它,何时使用,都取决于你。

如果您希望始终在特定存储库中报告具有重命名检测的历史记录,可以使用:

git-config diff.renames 1

检测到文件从一个目录移动到另一个目录。下面是一个示例:

commit c3ee8dfb01e357eba1ab18003be1490a46325992
Author: John S. Gruber <JohnSGruber@gmail.com>
Date:   Wed Feb 22 22:20:19 2017 -0500

    test rename again

diff --git a/yyy/power.py b/zzz/power.py
similarity index 100%
rename from yyy/power.py
rename to zzz/power.py

commit ae181377154eca800832087500c258a20c95d1c3
Author: John S. Gruber <JohnSGruber@gmail.com>
Date:   Wed Feb 22 22:19:17 2017 -0500

    rename test

diff --git a/power.py b/yyy/power.py
similarity index 100%
rename from power.py
rename to yyy/power.py

请注意,无论何时使用diff,这都是有效的,而不仅仅是使用gitlog。例如:

$ git diff HEAD c3ee8df
diff --git a/power.py b/zzz/power.py
similarity index 100%
rename from power.py
rename to zzz/power.py

作为一次尝试,我在功能分支中的一个文件中做了一个小修改,并提交了它,然后在主分支中我重命名了该文件,提交了,然后在文件的另一部分中做了小修改并提交了该文件。当我转到功能分支并从master合并时,合并重命名了文件并合并了更改。以下是合并的输出:

 $ git merge -v master
 Auto-merging single
 Merge made by the 'recursive' strategy.
  one => single | 4 ++++
  1 file changed, 4 insertions(+)
  rename one => single (67%)

结果是一个工作目录,重命名了文件,并对两个文本进行了更改。因此,Git有可能做正确的事情,尽管它没有明确跟踪重命名。

这是对一个老问题的迟来回答,因此其他答案可能在当时的Git版本中是正确的。

我想重命名/移动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) 仅在另一侧对目录应用隐式目录重命名历史是一个做更名的人。

首先,只需重命名即可创建独立提交。

然后,对文件内容的任何最终更改都将放在单独的提交中。

我遵循这个多步骤过程将代码移动到父目录并保留历史记录。

步骤0:从“master”创建了分支“history”以进行安全保管

步骤1:使用gitfilter repo工具重写历史。下面的命令将文件夹“FolderwithContentOfInterest”移动到一个级别,并修改了相关的提交历史记录

git filter-repo --path-rename ParentFolder/FolderwithContentOfInterest/:FolderwithContentOfInterest/ --force

步骤2:此时GitHub存储库丢失了远程存储库路径。添加远程引用

git remote add origin git@github.com:MyCompany/MyRepo.git

步骤3:从存储库中提取信息

git pull

步骤4:将本地丢失分支连接到源分支

git branch --set-upstream-to=origin/history history

步骤5:如果提示,解决文件夹结构的合并冲突

第6步:推!!

git push

注意:修改的历史记录和移动的文件夹似乎已提交。在此处输入代码

完成。代码移动到父目录/所需目录,保持历史记录完整!

可以重命名文件并保持历史记录不变,尽管这会导致文件在存储库的整个历史记录中被重命名。这可能只适用于痴迷于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将避免在文件存在之前创建此文件夹。