專案經驗,如需轉載,請註明作者:Yuloran (t.cn/EGU6c76)

前言

本文介紹如何使用 Git Bash 命令列,提交程式碼、解決衝突,以及如何避免衝突。有助於理解 Android Studio 的 VCS 背後的原理。

檢出程式碼

檢出遠端倉庫

git clone https://github.com/Yuloran/GitTutorial.git
複製程式碼

可以檢出 origin/master 分支到本地,這是 GitHub 建立倉庫時預設的 主機名/分支名。使用 git branch -vv 檢視本地分支狀態:

可見,本地分支名為 master,關聯的遠端分支名為 origin/master(origin 是主機名,master 是分支名)。

檢出遠端分支

很多時候,配置管理員需要新建很多遠端分支,以進行同一專案不同版本的並行開發。比如,有的分支用於需求開發,有的分支用於 Bug 修復等。此時,我們需要檢出各自對應的分支,修改並提交程式碼。

同步遠端分支

管理員新建遠端分支後,我們需要先同步一下遠端分支,才能看到新建的分支:

如上圖所示,先使用 git branch -a 檢視本地和遠端所有分支,發現並沒有管理員新建的 bug_fix 分支,此時輸入 git fetch,提示有一個新分支 bug_fix。再次輸入 git branch -a 檢視所有分支:

嗯,確實多了一個 bug_fix 分支。

檢出遠端分支

git checkout -b bug_fix -t remotes/origin/bug_fix
複製程式碼

checkout -b 表示新建本地分支,bug_fix 為本地分支名,你也可以起別的名字。-t 表示追蹤遠端分支(track),remotes/origin/bug_fix 為遠端分支名,檢視檢出結果:

輸入 git branch 檢視當前所在的本地分支:

輸入 git status 檢視當前分支狀態:

提示你目前修改是最新的,沒有任何修改可以提交。

提交程式碼

不良習慣

很多開發人員,喜歡在一個本地分支上,連續提交程式碼。這是一個很不好的習慣,尤其是在多人協作的情況下。這會導致每筆提交之間存在依賴關係,即使每筆修改之間毫無瓜葛。進而可能導致 merge 衝突、cherry-pick 合入冗餘程式碼。而且,如果你突然發現,上上一筆提交有問題的時候,我覺得你可能有種想 shi 的感覺。

正確姿勢

保留一個本地分支,專門用於同步程式碼。

比如,我們現在需要在 master 分支上做一個需求,首先輸入 git status 檢視本地 master 分支的狀態:

提示本地有修改檔案,沒有提交。咋整呢?有兩種處理方法:

  • 啥也不管,直接輸入 git pull 進行同步,有衝突會自動合併,合併不了再手動解決。-> 不推薦,可能會在本地產生一條 merge 記錄
  • 先將本地修改 stash save,再使用 git pull --rebase 進行同步,最後將暫存的修改 stash pop,有衝突會自動合併,合併不了再手動解決。-> 推薦,自動變基,不會在本地產生 merge 記錄

1. 暫存程式碼

git stash save [-u] 'update readme.md'
複製程式碼

[-u] 表示引數可選,加 -u 會將本地新增檔案也暫存,不加則僅暫存本地修改部分。'update readme.md' 為描述,下面列出 git stash 支援的所有操作:

  • git stash list 顯示所有暫存記錄
  • git stash show [email protected]{0} 檢視指定的暫存記錄
  • git stash pop [email protected]{0} 彈出指定的暫存記錄
  • git stash drop [email protected]{0} 刪除指定的暫存記錄
  • git stash clear 清空暫存記錄

2. 同步程式碼

git pull --rebase
複製程式碼

同步結果:

提示已經是最新的。如果原生代碼不是最新的,應當類似於下圖:

3. 彈出暫存程式碼

git stash pop [[email protected]{0}]
複製程式碼

[[email protected]{0}] 表示可選,不加預設彈出棧頂元素,也可以指定彈出哪一個暫存記錄。彈出結果如下:

提示有衝突。莫要驚慌,有衝突解決就是了,畢竟咱們乾的都是“小專案”,除非檔案換行符變了,否則不會衝突太多。像 AOSPMokee 那種大型專案,發生衝突才是坑爹。比如國內的手機廠商,每次大版本升級時(比如從 Android 8.0 升到 Android 9.0),都需要花幾個月的時間才能使版本穩定,這也是為什麼國產手機安卓版本總是落後於 Google 的原因。扯遠了,還是先 git status 看一下工作區狀態:

原來是 README.md 檔案修改衝突了,而且 Git 還貼心地提示你:

  • 使用 git reset HEAD <file> 來丟棄本地修改
  • 使用 git add <file>... 標記衝突解決(省略號表示後面可接多個檔案,以空格分隔)

我們先使用 git diff <file> 看看哪裡衝突了:

git 使用:

<<<<<<< Updated upstream

=======
>>>>>>> Stashed changes
複製程式碼

標記衝突狀態,======= 上面的是遠端倉庫上別人的修改,下面的是我們的本地修改。嗯,這個衝突是我人為製作的,所以比較簡單。在 IDE 中手動解決該衝突後,使用 git add README.md 命令標記衝突已解決:

README.md 咋變原諒色了呢?因為我們剛才用了 git add 命令,將其新增到了暫存區,所以上面顯示的是 Changes to be committed,也就是待提交。提交啥啊,剛解決完衝突,需求還沒做呢!所以,我們使用 git reset <file>... 命令,將其從暫存區撤出:

image.png

<file>... 表示可選,不加即撤出所有,加了即撤出指定的檔案。Linux 幫助手冊中很多使用 <arg> 或者 [arg] 表示引數可選,<>[] 是不需要輸入的,這個已經成為開發人員的習慣用法。

4. 新建本地分支

很多人這個時候,就直接在本地 master 分支上瘋狂輸出需求程式碼了。NO!我們應該針對不同的開發內容,新建不同的本地分支。比如 feature_shoppingbugfix_tombstone 等等,假設我們現在需要實現一個購物功能,我們應該使用 git checkout -b feature_shopping 新建一個本地分支來實現這個需求:

5. 提交程式碼

連續通宵5天后,我們的需求終於做完了,可以提交程式碼了:

git commit -m "update README.md" 表示將修改提交到本地倉庫,此時還沒有推送到遠端倉庫。-m 後面的是修改描述,這是一種簡便寫法。而大公司都會對提交的描述有格式要求,所以需要先配置 commit 模板:

git config --global commit.template ~/.gitmsg
複製程式碼

編輯該模板:

輸入 git commit

模板已經生效了,輸入修改描述即可。我這裡配置的 Git 編輯器是 vim,你也可以配置成別的:

git config --global core.editor notepad
複製程式碼

這樣,就可以用記事本來編寫修改描述了。

6. 追加提交

commit 之後,本地又修改了一些檔案,此時需要使用 git commit --amend 追加提交:

7. 回退提交

commit 之後,發現提交多了,把不需要提交的也提交了,此時需要回退,有兩種方式:

  • git reset [--soft] commit_id,軟回退,不會丟棄檔案修改記錄,--soft 不加也可以。
  • git reset --hard commit_id,硬回退,丟棄所有修改。一般僅在需要回退到指定節點驗證問題時使用。

檢視 commit_id:

git log -1
複製程式碼

-1 表示只檢視提交記錄裡的最後一條:

輸入 git reset 306c8b26360adfbdb3992f62514e8d58626f2d20,即可回退提交。然後重新 git add <file>...git commit

8. 推送程式碼

commit 之後很多人就直接 git push 了,這是不對的,應當先同步程式碼。由於我們現在在新建的本地分支 feature_shopping 上,這個分支沒有關聯遠端分支,所以無法也不應該使用 git pull --rebase 來同步程式碼。正確的操作為:

  1. git checkout master:切到本地主分支
  2. git pull --rebase:同步程式碼
  3. git checkout feature_shopping:切換到本地需求分支
  4. git rebase master:將本地主分支程式碼,合入到本地需求分支(可能有衝突,按照 Git 的提示修復即可)
  5. git push origin HEAD:refs/for/master:將本地需求分支的提交推送到遠端 master 分支

結語

Git Bash 每一個命令的操作結果,成功或者出錯的描述都很詳細。遇到問題的時候,只要按照提示,一步步操作,一般都能解決。