分割文件时如何保留Git历史记录

58

我想将一个文件中的函数提取出来并放入另一个文件,但要保留其历史记录。

cp a.php b.php

vim b.php
# delete everything but 1 function

vim a.php
# delete the 1 function

git add a.php b.php
git commit

但是,如果我运行 git blame b.php,我只看到它责备到这个新提交。


6个回答

43

维护责任历史的通用规则是在任何编辑之前先进行一个独立的移动提交。我的经验是,这样可以使git blame工作而无需使用-C选项。因此,在将文件拆分为新文件的情况下,可以通过两个提交来完成:

  1. 将原始文件复制到新目标中,确保删除原始文件
  2. 从复制文件中删除额外的部分

在提供的示例中,操作步骤如下:

cp a.php b.php
mv a.php c.php
git add a.php b.php c.php
git commit
vim b.php  # delete everything but 1 function
vim c.php  # delete the 1 function
git add b.php c.php
git commit

git add 命令在 Git 1.9 版本之后对不存在的文件的行为发生了改变。如果要反映已删除的文件,您需要使用 git rmgit add -A 命令。 - Damian Yerrick
感谢您的评论@DamianYerrick。 我认为,如果您指定要暂存的确切文件,用哪种方式都无关紧要。(根据发布说明,从 git 2.0 开始,“git add <path>git add -A <path>相同”,因为它包括删除操作)。 - vine77

13

我稍微修改了Peter在这里回答另一个问题的答案,创建了一个可重复使用、非交互式的shell脚本,名为git-split.sh

#!/bin/sh

if [[ $# -ne 2 ]] ; then
  echo "Usage: git-split.sh original copy"
  exit 0
fi

git mv $1 $2
git commit -n -m "Split history $1 to $2"
REV=`git rev-parse HEAD`
git reset --hard HEAD^
git mv $1 temp
git commit -n -m "Split history $1 to $2"
git merge $REV
git commit -a -n -m "Split history $1 to $2"
git mv temp $1
git commit -n -m "Split history $1 to $2"

它只是将源文件复制到一个新文件中,两个文件具有相同的历史记录。为什么这样做可以工作的解释可以在那篇其他答案中看到。


谢谢——非常顺利地完成了任务,包括重复三次将一个大文件分割成四个(最后一次迭代只需使用 git mv $1 $2 重命名原始文件就可以了)。 - jessexknight

6
也许这个之前的SO问题会很有启发:Git如何跟踪文件间移动的源代码? 简单概括一下被接受的答案:实际上,Git并没有“存储”移动的代码;在生成像移动代码的责备这样的东西时,通过从提交到提交检查整个仓库的状态来完成。

2
这个答案听起来像是“不行”,但实际上是“有时候可以”。删除似乎会触发Git查看文件的历史记录,超越其他文件的出生日期。像OP那样分离一个函数但保留文件的其余部分可能行不通。但我刚刚把一个文件分成两半,删除原始文件并给它们两个新名称,责任正确地分配到了两个新文件中。 - Potatoswatter
糟糕,那是在编辑之后。提交后,显然有一个新文件的责任丢失了。仍然可能是个或许? - Potatoswatter
@Potatoswatter 尝试将每个文件拆分到单独的分支中,然后再合并这些分支。我认为这样应该可以让 Git 识别“多个副本”。 - DylanYoung

6
尝试运行命令 git blame -C -C b.php

我想要历史记录实际上归咎于早期的提交。我们使用 git-svn,因此其他人将在 svn 上工作。 - Paul Tarjan
太糟糕了。SVN根本不支持这个! - Arafangion
非常有帮助,谢谢。我还没有找到“git log”的解决方案。 - ptim

0

提醒一下,我发布了一个 NPM 包(可以直接通过 npx 命令调用),它可以为您拆分(复制)文件。它比 shell 脚本慢一些,但如果您正在使用 npm,则可以轻松执行,无需创建文件或将其检入 git 存储库中,以与团队共享。

https://www.npmjs.com/package/swgh

你会做

npx swgh myFile.txt myDuplicatedFileWithHistory.someOtherExtensionIfYouWantTo

0

这个版本是我对Lukas' answer的改进。

通过按名称添加特定文件,并使用git reset --soft,它得到了改进,所以工作目录中可以有文件,但它们不会受到 git-split.sh 的影响。

#!/bin/sh

if [[ $# -ne 2 ]] ; then
  echo "Usage: git-split.sh original copy"
  exit 0
fi

git mv $1 $2
git add $2
git commit -m "renamed: $1 -> $2"
git branch temp-git-split
git reset HEAD~1 --soft
git mv $2 temp-git-split-file
git commit -m "renamed: $1 -> temp-git-split-file"
git merge temp-git-split
git add temp-git-split-file
git add $2
git rm $1
git commit -m "merging history"
git branch -d temp-git-split
git mv temp-git-split-file $1
git commit -m "renamed: temp-git-split-file -> $1"

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接