如何确定git pull是否从shell脚本中执行了某些操作

时间:2014-01-08 11:59:25

标签: git shell

我在登录时运行一个脚本,打开一个终端并在其中运行一个读取

的shell脚本
git pull --rebase && git log

但是,即使上游没有变化,这个git日志也会运行。如果有更改,我该如何修改此脚本才能运行git log

2 个答案:

答案 0 :(得分:5)

你可以这样做:

prev=$(git rev-list HEAD -n 1)
git pull --rebase
test $prev = $(git rev-list HEAD -n 1) || git log

答案 1 :(得分:2)

“常规”(无--rebasegit pull确实运行git fetch,后跟git mergegit pull --rebase真正运行git fetch后跟git rebase(如果您正在重新定位的“跟踪分支”本身已经重新设置,则会有一些聪明。)

在这两种情况下,实际上是fetch步骤带来了上游变化。第二部分(合并或重组)只是将这些上游更改(如果有)与您的更改(如果有)组合在一起。所以你想知道的是:从上游跟踪分支获取的是什么?

janos' answer可以正常使用。但是,要了解为什么,请考虑提交图在git pull --rebase之前和之后的显示方式。我们将从一个简单的图表开始,只需提交ABY,其中AB位于上游分支的“上方” Y是您自己的提交,将被重新定位:

A - B          <-- origin/master
      \
        Y      <-- HEAD=master

(这假设您在分支master上并且您的遥控器名为origin,但由于我们只查看HEAD它适用于任何分支和任何远程。)< / p>

如果fetch步骤不执行任何操作,则此图表在(无操作/跳过)合并或重组步骤后保持不变。因此HEAD仍为master仍然引用提交YY代表43be6d8...之类的长SHA-1字符串之一;这些是“真实的名字“提交,他们永远不会改变;你不能改变提交,你只能停止看它,我们将在下面看到。”

如果fetch步骤引入了新的提交C,则中间图如下所示:

A - B - C      <-- origin/master
      \
        Y      <-- HEAD=master

现在git pull --rebase必须运行git rebase才能将您的Y提交到新提交。现有的Y提交仍然存在,但标签master被移动以指向新提交。让我们调用新提交Y',因为它的内容与Y的内容基本相同,只是父ID现在是C而不是B。所以现在你有了:

A - B - C      <-- origin/master
     .    \
      .     Y' <-- HEAD=master
       .
        Y          [no label - abandoned]

所以,让我们采用那个简短的脚本并稍微简化一下(我将反转测试,即使这会使它更长一些,并添加双引号):

prev=$(git rev-parse HEAD)
git pull --rebase
test "$prev" != "$(git rev-parse HEAD)" && git log

第一个rev-parse找到HEAD提交(Y)的SHA-1。然后我们执行git pull --rebase,它会引入或不引入新的提交。最后,我们看到HEAD是否指向新的不同提交(Y')。如果是这样,旧的SHA-1和新的​​SHA-1会有所不同,!=测试会成功,我们将运行git log

值得考虑一些边缘和错误情况:

  • 如果没有提交Y,即你的分支和上游分支同步怎么办?

    没问题!这里HEAD会在B之前找到提交pull,并再次找到提交B(无更改)或提交C(某些更改)。测试仍然会做正确的事。

  • 如果pull --rebase因为无法到达上游而失败怎么办?

    这导致了与上游没有变化相同的行为。

  • 如果pull --rebase能够获取但是rebase失败怎么办?

    在这种情况下,您的提交(Y)仍然是您的HEAD提交。这里的脚本认为没有任何改变并跳过git log ...但是git pull --rebase步骤打印了一些关于失败的rebase的错误,所以这可能是正确的。

  • 如果没有HEAD版本怎么办? (空仓库;不在git仓库中;其他奇怪的错误。)

    在这种情况下,git rev-parse将退出并显示错误,prev将设置为空字符串。 git pull --rebase步骤也将失败,第二个git rev-parse将再次失败。这就是我添加双引号的原因:现在我们将运行test "" != ""。这两个空字符串相等,脚本将跳过git log步骤。所以你会得到一些额外的错误信息,但它仍然会有效。

    (为了使这个更加健壮,如果第一个git rev-parse HEAD失败,我们可能应该停止整个过程,但这相对较小。)

这里更复杂(更有趣)的方法是找到实际的上游分支,看看在fetch步骤中发生了什么。这将允许您记录上游分支上执行的操作(如果有的话)。事实证明,使用@{u} gitrevisions语法很容易:

old=$(git rev-parse @{u}) || exit # find current upstream, bail on error
git pull --rebase || exit         # update, bail on error
new=$(git rev-parse @{u}) || exit # find new upstream, bail on error
[ $old = $new ] && exit           # nothing to do if unchanged
ours=$(git rev-parse HEAD) || exit
if [ ! -z "$(git rev-list $new..$old)" ]; then
    # this is pretty rare; pull --rebase knows what to do though
    # could add "we rebased onto new head" if $ours != $new, perhaps
    echo "upstream removed old commits:"
    git log --oneline $new..$old
fi
if [ $ours != $new ]; then
    echo "upstream added new commits, which we rebased onto:"
else
    echo "upstream added new commits:"
fi
git log $old..$new

(注意:上面的脚本完全未经测试)。