1. 程式人生 > >實用:Git 中的一些常見錯誤

實用:Git 中的一些常見錯誤

無論是資料科學家、演算法工程師還是普通開發人員,在每個團隊協作開發任務中,Git 都是必不可少的版本控制工具,因此掌握它的基本操作十分有必要。但即便是教程滿天飛的今天,開發人員在使用 Git 時也還是會犯一些不應該犯的錯誤。本文總結了其中的幾種常見錯誤,希望能對新手有所幫助。

force push

 

有時,我們會需要用 force push 把 commit 推送到遠端倉庫。

  1. 假設有 2 名開發人員正在合作開發一個分支

  2. 之前開發人員1已經完成更改,把程式碼 push 到了遠端倉庫

  3. 現在,開發人員 2 也完成了更改,正當他準備提交時,他卻發現自己無法將程式碼推送到遠端倉庫

  4. 由於開發人員 2 是個初學者,他 Google 了一下,發現了一個神奇的命令 git push -f,於是進行了強制 push

  5. 之後開發人員 1 在檢查遠端倉庫時,發現自己編寫的程式碼全消失了

出現這個問題的原因是 force push 會覆蓋遠端倉庫中的程式碼,使現有程式碼全部丟失。

如果開發人員 2 想避免這個問題,一種理想方法是他先把開發人員 1 的更新從遠端倉庫 pull 到本地,然後把自己的程式碼 rebase 一下,再進行 push。這裡我們討論的是在同一分支中從遠端到本地倉庫的 rebase。

git push -f 這個命令非常不安全,除非有絕對的必要,大家最好還是不要用它。它會把本地分支的提交覆蓋遠端推送分支的提交,給協作的同伴帶去不少麻煩,即便是上面的解決方案,它也可能存在一個時間差的問題,因為你不可能時刻掌握同伴的工作進展。

所以如果大家都用正確的 git 工作流,讓每個開發人員都擁有自己的功能分支,這種情況根本不會發生。

Rebase

 

如果你想把一個分支的修改合併到當前分支,你可以用 git rebase。它和 git merge 的區別是 merge 有一個合併 commit 的步驟,而 rebase 是把所有 commit 都串聯在一起,讓你本地的分支歷史看起來像沒有經過任何合併一樣。

  1. 假設有 2 名開發人員正在合作開發一個功能分支

  2. 開發人員 1 率先完成了一系列 commit,並將其推送到遠端功能分支

  3. 之後,開發人員 2 把遠端功能分支的最新更改 pull 到本地

  4. 開發人員 2 向本地功能分支添加了一堆 commit

  5. 這時,他想把本地倉庫的更新重新 rebase 到遠端倉庫中,於是他把整個預發分支(release branch)在本地功能分支上 rebase 了一下。這裡我們討論的是在不同分支中從遠端到本地倉庫的 rebase

  6. 現在,開發人員 2 試著把程式碼 push 到遠端功能分支上,由於提交歷史記錄已更改,這個操作不被允許,他只能又開始用 git push -f

  7. 最後,當開發人員 1 想從遠端倉庫提取最新程式碼時,由於提交記錄已更改,他被迫需要處理大量程式碼衝突問題

 

 

如上圖所示,rebase遠端倉庫會改變提交歷史記錄,並在其他開發人員嘗試從遠端倉庫中提取最新程式碼時產生問題。處理這種情況的理想方法是始終只rebase本地倉庫,本地倉庫中的任何commit都不應該被push到遠端倉庫。

如果別人事先已經把commit推送到遠端功能分支,那麼你最好先用pull命令把更新拉到本地,用merge和你的修改合併,因為merge不會改變提交歷史,而rebase會。

此外,和上個問題一樣,如果使用正確的git工作流,每個開發人員都會有自己的功能分支,這時,開發者在自己的功能分支上進行更新並且在遠端功能分支上做rebase是不會報錯的,因為沒有其他開發人員從同一個遠端功能分支中提取程式碼。無論如何,儘量避免重新定義遠端倉庫。

Rebase是一個非常強大的功能,使用時也需謹慎。

amend

 

git amend 命令的作用是修復最近一次 commit,讓你合併你快取區的修改和上一次 commit,而不是提交一個新的快照。這裡需要注意一點,它不是修改最近一次 commit,而是整個替換掉原 commit,所以對 Git 來說這是一個新的 commit。此外,它還可以用來編輯上一次的 commit 描述。

  1. 假設有 2 名開發人員正在合作開發一個功能分支

  2. 開發人員 1 率先完成了 commit,並將其推送到遠端功能分支,我們把它稱為“old commit”

  3. 之後,開發人員 2 把最新程式碼從遠端功能分支 pull 到本地功能分支

  4. 然後他開始處理本地倉庫中的程式碼,在這個過程中,他沒有向遠端倉庫 push 任何 commit

  5. 這時開發人員 1 突然發現之前的 commit 中存在 bug,他用 amend 命令修復了本地倉庫裡的最近一次 commit,我們把它稱為“new commit”

  6. 開發人員 1 嘗試把這個新 commit 重新 push 到遠端功能分支,由於提交歷史記錄發生了變化,這個操作報錯了,於是他呼叫了 git push -f

  7. 現在,當開發人員 2 想從遠端功能分支中提取最新程式碼時,git 會注意到提交歷史記錄的變化並建立合併的 commit。因此當他 pull 到本地後,他會在本地倉庫裡發現“commit old”和“commit new”,這就破壞了 amend 這個操作的意義。

  8. 最後,即便開發人員 2 從遠端分支到本地分支執行 rebase,這個“commit old”還是會出現在本地倉庫中,它仍然會作為歷史提交的一部分。

amend commit 會更改提交歷史記錄,所以當其他開發人員嘗試從遠端倉庫提取最新程式碼時,修改遠端倉庫中的 commit 會產生混淆。

為了避免這個錯誤,最好的方法是隻在本地倉庫裡修改 commit,不要對遠端庫裡的 commit 做任何修改。當然,一人一個分支也不會出現這個問題。

Hard reset

 

git reset 命令是用來將當前 branch 重置到另外一個 commit 的。它不會產生 commit,而是隻更新一個 branch(branch 本身就是一個指向一個 commit 的指標)指向另外一個 commit。

  1. Hard reset 的命令是 git reset --hard

  2. 此外,git reset 還有 --soft 和 --mixed,只不過它們都沒有 Hard reset 那麼不安全

  3. 假設開發人員 1 正在開發一個功能分支,並在本地倉庫中完成了 5 次 commit

  4. 與此同時,他還正在處理尚未提交的兩個檔案

  5. 這時,如果他運行了 git reset --hard <commit4hash>

  6. 那麼功能分支中的最新 commit 會變成是 commit4,commit5 丟失

  7. 同時他正在處理的那兩個未提交檔案也會丟失

這時 commit5 還在 git 內部,只不過對它的引用丟失了,我們可以用 git reflog 把它恢復,但總體來說,hard reset 還是很不安全。在 git 中使用 reset 命令時要非常小心,如果必須得用,確保你已經完全評估所有情況。

小結

 

 

 

綜上所述,為了避免使用 git 時出錯,我們可以牢記這幾條教訓:

  1. 避免多人在同一分支上協作。上述四個例子中有三個都是在說明這個問題,在日常工作中,遵守正確的工作流非常重要,要確保只有一個人在一個功能分支上工作,這是技術主管、高階開發人員尤其需要注意的。

  2. 不要到處實用 force push。

  3. 如果萬不得已必須使用 force push,先評估其他方案,把它作為最後的手段。

  4. 不要試圖修改遠端倉庫裡的 commit,要只在本地倉庫中更改 commit 歷史記錄。但即便是在本地倉庫裡,用 rebase 還是要謹慎。

原文地址:adityasridhar.com/posts/how-you-can-go-wrong-with-git