协慌网

登录 贡献 社区

是否可以在 Git 中移动 / 重命名文件并维护其历史记录?

我想在 Git 中重命名 / 移动项目子树,将其从

/project/xyz

/components/xyz

如果我使用普通的git mv project components xyz project所有提交历史记录都将丢失。有什么办法可以移动历史记录吗?

答案

Git 会检测到重命名,而不是通过提交保留操作,因此无论您使用git mv还是mv都无关紧要。

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

http://git-scm.com/docs/git-log

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

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

不。

简短的答案是 “否” 。无法在 Git 中重命名文件并记住历史记录。这是一种痛苦。

有传言说git log --follow --find-copies-harder可以工作,但即使文件内容更改为零,并且对git mv进行了移动,它也对我不起作用。

(起初,我使用 Eclipse 在一个操作中重命名和更新程序包,这可能会使 Git 感到困惑。但这是很常见的事情--follow的确在仅mv然后执行commitmv不太远。)

Linus 说,您应该全面了解软件项目的全部内容,而无需跟踪单个文件。好吧,可悲的是,我的小脑无法做到这一点。

如此多的人无意识地重复了 Git 自动跟踪移动的说法,这真是令人讨厌。他们浪费了我的时间。 Git 没有这样的事情。 根据设计(!),Git 根本不会跟踪移动。

我的解决方案是将文件重命名为其原始位置。更改软件以适合源代码管理。使用 Git,您似乎只需要在第一时间正确进行 “git” 操作即可。

不幸的是,这破坏了 Eclipse,后者似乎使用了--followgit log --follow有时不显示具有复杂重命名历史记录的文件的完整历史记录,即使git log可以显示。 (我不知道为什么。)

(有一些非常聪明的 hacks 可以返回并重新提交旧工作,但是它们相当令人恐惧。请参阅 GitHub-Gist: emiller / git-mv-with-history 。)

简而言之:如果Subversion 这样做是错误的,那么 Git 这样做也是错误的 - 这样做不是某些功能(错误!),这是一个错误。

可以重命名文件并保持历史记录完整,尽管这会导致在存储库的整个历史记录中重命名文件。这可能仅适用于痴迷 git-log-lovers 的人,并且会产生一些严重的影响,包括:

  • 您可能正在重写共享历史记录,这是使用 Git 时最重要的。如果其他人已经克隆了存储库,则将其破坏。他们将不得不重新克隆以避免头痛。如果重命名足够重要,这可能没问题,但是您需要仔细考虑这一点 - 您可能最终使整个开源社区感到沮丧!
  • 如果您在存储库历史记录中较早地使用文件的旧名称引用了该文件,则实际上是在破坏较早的版本。为了解决这个问题,您将需要做更多的跳跃动作。这不是不可能,只是乏味,可能不值得。

现在,由于您仍在我身边,您可能是一个重命名完全隔离的文件的单独开发人员。让我们使用filter-tree移动文件!

假设您要将old文件移到文件夹dir并命名为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 dir。如果使用 if,将避免在历史记录中早于文件存在创建此文件夹。