Git Bash 提交程式碼的正確姿勢
專案經驗,如需轉載,請註明作者: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}]
表示可選,不加預設彈出棧頂元素,也可以指定彈出哪一個暫存記錄。彈出結果如下:
提示有衝突。莫要驚慌,有衝突解決就是了,畢竟咱們乾的都是“小專案”,除非檔案換行符變了,否則不會衝突太多。像 AOSP
、Mokee
那種大型專案,發生衝突才是坑爹。比如國內的手機廠商,每次大版本升級時(比如從 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>...
命令,將其從暫存區撤出:
<file>...
表示可選,不加即撤出所有,加了即撤出指定的檔案。Linux 幫助手冊中很多使用 <arg>
或者 [arg]
表示引數可選,<>
和 []
是不需要輸入的,這個已經成為開發人員的習慣用法。
4. 新建本地分支
很多人這個時候,就直接在本地 master
分支上瘋狂輸出需求程式碼了。NO!我們應該針對不同的開發內容,新建不同的本地分支。比如 feature_shopping
,bugfix_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
來同步程式碼。正確的操作為:
git checkout master
:切到本地主分支git pull --rebase
:同步程式碼git checkout feature_shopping
:切換到本地需求分支git rebase master
:將本地主分支程式碼,合入到本地需求分支(可能有衝突,按照 Git 的提示修復即可)git push origin HEAD:refs/for/master
:將本地需求分支的提交推送到遠端master
分支
結語
Git Bash 每一個命令的操作結果,成功或者出錯的描述都很詳細。遇到問題的時候,只要按照提示,一步步操作,一般都能解決。