如何将一组修改的提交(存储在reflog中)转换为实际提交

时间:2012-06-19 03:22:41

标签: git refactoring commit reflog

我正在进行重构,每次我取得任何体面的进展时都会进行修改。它已经非常好了,我意识到能够向人们展示我如何处理如此巨大的重构(很多小步骤,只需轻轻一点,每个点都有绿色测试套件)会很好。

提交都在我的reflog中。有没有办法将每个修改后的提交转换为自己的实际提交(抱歉,不知道术语),以便用户可以看到每个步骤而不仅仅是第81和57行的聚合提交?我不需要提交或任何内容的唯一消息,只是它们在历史记录中被捕获。

这仍然在我当地的回购中。

3 个答案:

答案 0 :(得分:2)

重建reflog可能不像第一次出现那么困难。您可以将reflog的提交重新生成到不同的分支中,或者您可以使用git read-tree生成包含部分reflog的单个newbranch

每个分支解决方案一次提交:

假设您希望每次提交都有不同的分支。首先,复制您的存储库。您将在此过程中更改您的reflog,因此您不妨使用一次性版本。其次,检查您的reflog并找到您要启动的提交。它看起来像HEAD@{n}。假设它是HEAD@{49}。然后,尝试使用此脚本,将head -50替换为head -<n + 1>,无论您的情况如何:

#!/bin/sh
reflog=$(git reflog | head -50 | awk '{ print $1 }')
i=0
for ref in $reflog; do
    git checkout -B "reflog_$i" $ref
    i=$(expr $i + 1)
done

这是抓取你的reflog的提交历史记录一次,然后迭代它,沿途生成reflog_$i个分支。您可以根据需要随意挑选,合并或操纵它们。如果这只是一个演示文稿,你可以编写一个简短的脚本,在分支上执行git checkout,运行测试套件,并在整个过程中显示绿色。请记住,reflog_1代表最新的历史记录; reflog_<n+1>最老的。

#!/bin/sh
for i in $(seq 50 1); do
    git checkout "reflog_$i"
    ./test-suite.sh
done

当你解释你的提交方法时,把它扔在投影仪上,这是一个很好的背景。

如果要组合所有分支,可以运行此ruby脚本以按顺序应用它们(或者以您熟悉的任何语言创建等效语句)。让我重申一下,你应该备份你的目录,因为这是相当具有破坏性的。

#!/usr/bin/env ruby
n = 50

n.downto 0 do |i|
  system "
    git read-tree reflog_#{i}
    git commit -m 'Refactoring #{n - i}'
    git checkout -- .
    git br -D refog_#{i}
  "
end

使用git read-tree

将所有提交放在同一分支上

首先,复制您的存储库。然后使用如下所示的脚本。根据第一个解决方案中的讨论,将两个<n + 1>更改为您在reflog中想要的任何深度,再加上一个。请注意sed的附加管道。您必须使用它或类似的东西,以免reflog按反向时间顺序应用于newbranch。肯定有一种方法可以在不同时使用awksed的情况下执行此操作,但这样做有效:

#!/bin/sh
reflog=$(git reflog | head -<n + 1> | awk '{ print $1 }' | sed -n '1!G;h;$p')
git checkout -B newbranch HEAD@{<n + 1>}
for ref in $reflog; do
    git read-tree "$ref"
    git commit --no-verify -m "Adding commit $ref"
    git checkout -- .
done

最终结果将为newbranch,其中应包含HEAD@{0}HEAD@{n}之间的所有提交,具体取决于提交HEAD@{n+1}

答案 1 :(得分:1)

这是可能的,但可能很难。

每次你执行一个--amend时,你基本上“扔掉”了之前的提交,并用新的修改提交替换它,其中包含先前的更改以及修改的内容。

所以:

A<--B (master)

现在提交--amend:

  ---B
 /
A<--C (master)

现在再次提交--amend:

 --B
/
A<--D (master)
\
 --C

提交B和C仍然存在。然而,它们并没有被任何东西指向,最终会被垃圾收集。

所以要获得你想要的图表:

git checkout master
git reset --hard A
git cherry-pick B
git cherry-pick C
git cherry-pick D

叶:

A<--B'<--C'<--D' (master)

(注意素数(撇号)符号 - 表示它们是新哈希的新提交)

现在,我不确定的一件事是你是否会有冲突。我相信你会这样做,因为后来的每个提交都包含了与之前提交相同的一些更改,因为它们已被修改。如果是这样,你将不得不解决这些问题。

请记住,考虑到git的工作方式,如果你尝试这个并且它不起作用,那么回到你现在的位置只是一个简单的问题:

git reset --hard D

答案 2 :(得分:0)

你可以从第一个reflog条目中创建一个新的分支,然后对于每个reflog条目,git checkout树到workarea然后提交它。

这样你就不会有任何冲突,因为你基本上只用新的reflog树重写workarea树。