2014-01-08 47 views
2

我在登錄時運行一個腳本,打開一個終端,並在它運行一個shell腳本讀取如何確定一個git拉是否從一個shell腳本

git pull --rebase && git log 

但是做了,這個混帳日誌仍將運行如果上游沒有變化。如何修改此腳本以僅在運行git log時發生變化?

+2

你一定非常信任在登錄時盲目運行git pull。 –

回答

5

你可以做這樣的事情:

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

「常規」(無--rebasegit pull真正運行之後git mergegit fetch。 A git pull --rebase確實運行git fetch,然後是git rebase(如果您正在重新投入使用的「跟蹤分支」本身已重新發布,那麼可以使用一些巧妙的方法)。

在這兩種情況下,它實際上是fetch一步,在上游的變化帶來的。第二部分(合併或rebase)簡單地將這些上游更改(如果有)與您的更改(如果有)進行組合。所以你想知道的是:是否從上游跟蹤分支獲取任何東西?

janos' answer將工作得很好。爲了理解爲什麼,雖然考慮提交圖表在git pull --rebase之前和之後的樣子。我們將用一個簡單的圖形開始只是犯ABY,其中AB是「上」上游分支和Y是你自己的承諾將要重建基礎:

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

(假設你在分支master和你的遠程被命名爲origin,但因爲我們只看HEAD它適用於任何分支和任何遠程)。

如果fetch步驟不執行任何操作,該圖保持相同(無操作/跳過)合併或 - 底墊步驟之後。因此HEAD仍然是master,它仍然指代提交YY代表那些長的SHA-1字符串之一,如43be6d8...;這些是提交的「真實姓名」,並且它們永不改變;您不能更改提交,您只能停止看它,我們將在下面看到)。

如果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找到了SHA-1爲您HEAD提交(Y)。然後,我們執行git pull --rebase,它確實或不帶來新的提交。最後,我們看到HEAD是否指向新的不同提交(Y')。如果是這樣,舊的SHA-1和新的​​SHA-1將會不同,!=測試會成功,我們將運行git log

這是值得考慮的幾個邊緣和錯誤的情況下,以及:

  • 如果沒有提交Y,即你的分支和上游分支保持同步?

    沒問題!這裏HEAD會發現pull之前提交B,並要麼找到再犯B(無變化)或提交C(一些變化)。測試仍然會做正確的事情。

  • 如果在pull --rebase失敗,因爲上游不能達到?

    這導致了與上游沒有變化相同的行爲。

  • 如果該pull --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 

(注:上面的腳本是完全未經測試)。

相關問題