引言
首先為什麼要寫這篇文章?因為最近總是遇到有人在問一些關於程式碼管理的問題,比如怎麼從 倉庫拉程式碼,上傳程式碼,也有完全沒用過程式碼管理系統的。因此這篇文章是結合這幾年來使用 git 團隊協作或個人的程式碼管理的使用心得。
那 git 是什麼呢?git 是一個分散式程式碼管理系統,可能很多人之前用過 svn 這樣的程式碼管理系統。不過 git 的特點是分散式,以及更加便捷的協作方式。git 的使用也是十分廣泛,比如程式設計師交友圈 github, gitlab, gogs 等等程式碼託管平臺都是使用 git 作為程式碼管理工具。
必須瞭解的知識
SSH加密認證原理: SSH(Secure Shell)是一種非對稱加密與對稱加密演算法相結合的安全網路協議,用於計算機通訊加密。一個SSH會話的建立過程分為兩個階段:第一階段,雙方溝通並同意建立一個加密連線通道以供後續資訊傳輸用;第二階段,對請求接入的使用者進行身份驗證以確定伺服器端是否要給該使用者開放訪問許可權。詳細的文章請參考理解SSH的加密與連線過程 。
使用方式
首先,如果主機本地沒有建立祕鑰對,就需要生成。生成方式:
當然你可以使用預設的方式,一路回車下去即可。或者也可以指定名稱。例如 test,一路回車即可。
在當前目錄下會看到生成的祕鑰對如下,其中帶字尾.pub 為公鑰,沒有字尾的為私鑰。
這樣就生成好了祕鑰對,切記不要將私鑰隨意上傳到能被網路訪問、下載。個人的做法是用一套金鑰對來管理所有的終端,比如雲主機的 ssh 登陸方式。
這裡以 github 舉例,你將你的公鑰拷貝後,登入 github, 然後找到 Settings -> SSH and GPG keys。 如圖的所示的地方新建並貼上。
點選進入,如圖所示:
當搞定這個了就可以愉快的在 github 程式碼託管倉庫建立你的專案了,這裡我就一個測試的專案講解我的使用過程。第一步新建專案:
點選進入,填寫專案名稱以及專案的訪問方式, 這裡我以公開訪問方式,也就是所有人都可以訪問這個專案。
新建專案完成後第一個要做的就是將這個專案 clone(克隆) 到本地主機。
然後在終端中輸入, 這是第一個命令。
git clone git@github.com:huangxiaojingCN/LearningGit.git
複製程式碼
在終端命令列中輸入: ls -al 檢視, 可以看到有一個隱藏的目錄檔案 .git, 在這個檔案下就是記錄這個程式碼倉庫的所有資訊,其中也有很多功能可以用以我們對程式碼的自動化,程式碼檢查等等。
接下來是第二個命令:git add 檔案
// 建立一個新的空白檔案,你可以使用文字編輯器開啟並編輯內容,也可以使用 echo "This is a a.txt." >> a.txt
touch a.txt
// 將字串 "hello world" 重定向到 a.txt
echo "This is a a.txt" >> a.txt
// 通過 git add a.txt 檔案加入到原生程式碼管理
git add a.txt
複製程式碼
當然你也可以使用 git add -p 命令來提交檔案中的新增記錄, 前提是這個檔案以及在原生程式碼管理庫中存在。稍後再說這個用法,非常實用。
第三個命令: git status, 檢視當前程式碼庫的狀態
git status
複製程式碼
不難看到,我們發現剛才執行了 git add a.txt 的命令被加入到了快取區,即將要被提交 commit,當然你也可以刪除它,執行 git rm --cached a.txt。
第四個命令: git commit -m "提交訊息",只有在執行了 git add 將其加入到本地快取區後才能使用 commit, 將其加入到原生程式碼倉庫。
git commit -m "新增了 a.txt 檔案,並加入了 hello world 文字內容."
複製程式碼
從上面的結果顯示中可以看到,a.txt 被正確建立並加入到了原生程式碼管理庫中。接著通過前面的命令 git status 檢視下當前的程式碼庫狀態, 可以發現已經沒有檔案要被提交。
經過上面的兩個步驟:git add、git commit 就已經程式碼加入到了原生程式碼倉庫中,通過 git status 輔助檢視原生程式碼倉庫的狀態。 這三個命令構成另一次原生程式碼的的提交,比較基礎但是非常常用。
第五個命令: git log, 從名字不難看出就是要看看我們原生程式碼倉庫的日誌。
git log
複製程式碼
看到上面的截圖是不是就發現,我們剛才提交的資訊也被展現出來啦!沒錯,這就是告訴你檔案被正確提交併加入到了原生程式碼倉庫中。可能大家發現了有一個 HEAD-> master,你只需要記住,當前 HEAD 所在的地方就是你的程式碼的版本。如果你發現你的截圖跟我不一致,沒有 Author 那是因為還沒配置,這個後面再說,先說完整個 git 使用流程。
現在再來修改下 a.txt 的內容: 比如我們加入其他文字內容如下, 筆者使用的 vim 編輯方式,當然你可以使用你順手的,比如編輯器等等。
編輯完後引入新的 git add 帶引數的方式:
git add -p
複製程式碼
請你仔細看, 從第一行開始: diff --git a/a.txt b/a.txt 就是比較原來的 a.txt 和現在更改後的 a.txt 有啥區別。 綠色的 "+" 號表示新新增的,紅色的 "-" 號代表刪除的內容。最後一行方框中有選擇[y, n,q,a,d, /, e, ?],然後讓你填寫一種,一般我們使用最多的就是: y(yes)、n(no)、q(quit)。 這裡我們寫入 y 後回車。
不知道還記不記得前面提到的 git status, 用來檢視當前原生程式碼倉庫的狀態。你看出了什麼變化嗎??? 前面新增的檔案如果是新的, 那麼看到的就是 new file, 如果是修改的是 modified。這也很好理解嘛!
那下一步我們是不是又要提交啦!繼續 git commit -m "提交資訊"
然後檢視一下 git log 日誌吧!嗯嗯,是不是又看到了新修改的 a.txt 檔案啦!
為了鞏固前面的學到的知識,我們將繼續新建一個檔案, 鞏固 add、commit、status、log 的用法, 來來來
touch b.txt
echo "This is a b.txt" >> b.txt
git add b.txt
複製程式碼
按照管理,看看原生程式碼庫的狀態: git status
ok! 翻譯一下吧!說的就是告訴你這個檔案未被追蹤,也就是未被加入原生程式碼倉庫中。你可以使用 git add file 的方式去新增。那就執行 git add b.txt, 然後通過 git status 看看。 是不是很熟悉啦!但是我寫到想吐?
到了這人我們是不是就要提交了,從它的結果中也是可以看出的。繼續執行:
git commit -m "新增了 a.txt 檔案,加入了 This is a b.txt 文字內容."
複製程式碼
這個結果也是前面見過的,你應該有感覺了吧!這裡告訴我們的是 b.txt 也被加入到原生程式碼倉庫中。
快快使用 git status 和 git log 來瞅瞅吧!大佬!
又一個檔案被新增到原生程式碼庫管理了。但是這裡你看看 log 的方式是不不夠清晰,我們能不能看看它的生長過程。這個時候我們就要為 log 加上一些引數啦!
// --graph 圖形化展示
// --decorate 檢視當前提交的 commit 資訊和其他的描述資訊
// --all 檢視所有的分支,分支的概率後面再說。
git log --graph --decorate --all
複製程式碼
看到區別了吧! 是不是旁邊多了一條線,這個就好比我們物流時間軸。後面的肯定是包含前面的過程,在這裡就是後面會包含前面提交的檔案。比如當前 HEAD 所在的地方,執行 ls,可以看到即包含了 a.txt 又包含了 b.txt。
梳理一下前面的過程: 其無非核心的就是: add -> commit, 輔助 git status, git log 來完成提交到原生程式碼庫的操作。
接下來就開始往遠端倉庫推程式碼和拉取程式碼啦!首先先來解釋下前面總是提到的原生程式碼倉庫,這個含義就是你的服務端程式碼倉庫可能不包含你本地新建的檔案,很簡單嘛!你沒有把資訊同步給別人,別人是不知情你的本地的變化的。在這裡我就引入遠端程式碼倉庫。接下來就是和遠端倉庫的互動。
再回顧下前面 log 日誌列印內容, 你會發現有一串很長的數字。它代表了當前的提交的唯一標示,即我們可以通過它找到任意一次提交的程式碼。
第七個命令: git push。 push 的單詞意思就是推送,即客戶端向遠端倉庫推送程式碼。那 origin 又是什麼? 這個其實是遠端倉庫的名字而已,它等效於前面克隆的地址。你可以通過 git remote show origin 來檢視。
git push origin HEAD:分支
複製程式碼
從列印可以看到,Push URL: 即我們要推送的地址,而 Fetch URL 就是我們要拉取程式碼的地址。
好吧! 那我們執行提交程式碼的命令看看效果。
// HEAD 即當前所在的分支
// : 冒號左邊是本地分支, 右邊是遠端分支, 那這裡的 huangsanyang/add_a_and_b 即為遠端的分支名稱
// 完整的理解就是: 將本地 HEAD 的分支推向遠端倉庫名為 huangsanyang/add_a_and_b 的分支
HEAD:huangsanyang/add_a_and_b
複製程式碼
使用 git log --graph --decorate --all 檢視日誌,這下多了點區別,可以看到當前 HEAD 所在地方,右側還有一個紅色的 origin/huangsanyang/add_a_and_b, 這就是告訴我們這是一個遠端分支, 從前面的 origin 就可以看出。
再次回顧: 一次完整的推送程式碼到遠端倉庫的流程為: add -> commit -> push。 再次 看看 github 程式碼倉庫,是不是已經包含了我們的提交啦!說明已經正確將本地的程式碼倉庫提交到遠端程式碼倉庫。相當於做了一次遠端備份。
到了這兒,我們就將從零新建專案到推送程式碼到遠端伺服器倉庫的流程啦!
拉取程式碼和多人協作一起講解,現在假定你是專案組長,你擁有這個程式碼倉庫的所有權,你可以刪除、指定誰能協助專案開發、稽核程式碼、合併程式碼。
這裡我掏出了我自己使用的電腦,作為一個專案管理者的方式參與到協作開發中。因為我也是第一次克隆,各位請注意這是新的人員。所以你不是一個人在戰鬥....
老生常談:檢視一下 log 日誌吧! 是不是和前面的 push 後一樣。
接著我開始開發我的功能,比如我新建了 123.txt
touch 123.txt
echo "123456" >> 123.txt
git add 123.txt
複製程式碼
現在你還會忘記要幹嘛嗎?如果忘記就該撞牆去。 git commit -m "新增了 123.txt "
再來看看 log, 是不是當前的 HEAD 指向了 huangsanyang/add_a_and_b, 這個也就是當前的本地分支啦!你還會看到後面一個分支有一個 origin/HEAD, 這個告訴我們遠端伺服器的 HEAD 分支比我們本地剛才提交分支少了一個 123.txt 檔案。也就是說前面的開發者是沒法拿到 123.txt, 那如果要拿到該怎麼辦呢?
繼續提交吧!這一次提交是專案管理員,也是另一個協作的開發者提交的程式碼。
別慌,苟住。繼續來看 log 日誌。這裡我提交另一個新的檔案 123.txt, 推向了遠端伺服器分支 huangxiaojing/add_123。
OK, 讓我們回到前面的開發者,看他怎麼獲取最新的程式碼。這就很自然的過多到了 git fetch
git fetch --all
複製程式碼
sorry,可能仔細的你發現,我是用的是 git fetchall 而不是 git fetch --all, 那是因為我使用別名的方式簡化了拉取指令。別名在後面的 git 補充篇講解。
來看 log 日誌,是不是拿到最新的程式碼了呢? 也就是管理員作者提交的程式碼 123.txt。
可以看到分支確實能看到了吧!不過你通過 ls 檢視並沒有發現我們想要的 123.txt。 那是因為你沒有留意的你本地的 HEAD 指向的是哪裡。可以看到當前的 HEAD 是要早於 123.txt 之前提交的。所以肯定沒有的。那怎麼辦?
啊哈!我們就來看看怎麼把分支切換到最新的版本上去。引入了新的命令:
// checkout 切換分支
// 分支名: huangsanyang/fetch_123_txt,當然也可以不指定,最好還是帶上。這樣更好,更清晰
// 指定分支的雜湊值: 這個前面說過,用來確定唯一的提交記錄。
git checkout -b 分支名 指定分支的雜湊值
複製程式碼
來看 log 日誌, OK! 切換過來了吧!通過 ls 看看
經過上面的過程,你已經可以和同事進行程式碼交換啦!你可以拿到同事的程式碼,通過 fetch -> checkout。但是這樣其實也不是很好,我們的程式碼倉庫應該要有一個好的規範,在所有知名的 git 程式碼管理的專案中,都會通過維護一個 master 分支來保證穩定的版本。 master 分支應該遵循的原則是,一定要保證能編譯通過,能正常部署執行。根據實際情況也可以增加 developer 分支來表示開發的過程的分支。那這裡是不是就出現了兩條分支了,一個是開發分支,一個是釋出分支。 這個時候專案管理員的作用就來了,他務必保證 master 分支的程式碼一定是所有開發者的程式碼合體、保證程式碼穩定執行、程式碼質量審查。
接下來就要引入稍微高階的內容:程式碼合併、審查。
現在回到專案管理者,他首要的就是要保證程式碼的 master 主幹分支是最新的。
很顯然不是,因為我們並沒有看到 origin/master 是否在最後一次提交的分支。 這裡先執行 git push origin HEAD:master 建立出遠端 master 分支。
由於沒有新的內容需要條,所以可以看到 Total 為 0。接著看看 log 日誌,各位再次強調一下,善於使用 log 能讓我們清楚知道當前程式碼庫的狀態。
現在看到 origin/master, 我們已經建立出這樣一個用於專案釋出的分支。因此作為管理員就要維護這個分支。接下來演示一次合併請求。也就是其他的開發者,編寫了程式碼提交到遠端非 master 的分支。開發者可以設定遠端 master 分支為保護分支,那麼別的開發者就不能往 master 推送程式碼。
現在切換到普通開發者,編寫一個檔案 utils.txt
touch utils.txt
echo "這是一個描述專案中使用工具的檔案" >> utils.txt
git add utils.txt
複製程式碼
這裡就不再提供 git status 列印, 讀者自行使用檢視。通過 commit -> push 如下:
可以看到,開發者已經提交了新程式碼並且提交了指定分支 origin/huangsanyang/push_utils。 可以看出這樣的提交程式碼風格非常好。因為讓專案管理者,可以快速清晰的知道他幹了什麼。
讓我們切回到專案管理者,來看看他是如何合併程式碼的。請務必記住,一定要先執行 git fetch --all
git fetch -all
複製程式碼
再看 log 日誌
到了這一步其實也是前面都用到的知識,知識不斷的軟磨硬泡,旨在讓小白變成一個真正的高手。當然如果你看完不練習,當我沒說。
在合併的時候,開發者應該要將分支切換到遠端 master 所在的地方,然後再合併別的開發的程式碼。為什麼要這樣,因為是將別的開發者程式碼往 master 合併,而不是把 master 往開發者合併。要分清那個分支才是最重要的分支。因為開發者寫的功能也許有問題。所以一定要注意。
合併的命令:
// merge 合併分支
// --no-ff 不要強制解決衝突,由合併者自行處理
git merge --no-ff 指定合併的分支
複製程式碼
執行如下的合併指令:
git merge --no-ff c17c8f7e46b666ab84f3ba5b99efc57c1711baed
複製程式碼
最終的 log 截圖, 請你仔細看看,分支走向發生了變化。可以看到有一個分支合併到了 5df2ffb37f131f93fc1db775e1a7dd36674814c9 分支。
如果確認無誤,經過嚴格測試以後符合功能需求,再將其推送到遠端 master 分支。
再檢視 log
好了,正常合併流程已經講完。但是你想想,既然要成為一個高手,如果不去解決點衝突,這能符合高手的常規操作嗎?
營造一個衝突環境,比如管理員開發的某個檔案的一些程式碼被另一個開發者改掉,那麼開發者最終提交給管理員的程式碼和原來管理員的程式碼肯定不同的。這樣是不是就要處理衝突了。
回到開發者,記得 fetch, 記得 checkout 切換分支。 然後修改一下 123.txt 檔案, 可以看到他將新增了一些檔案,並且修改了原來 12345 改為 1234 sdf6。然後提交。
1234 sdf6
故人西辭富士康
為學技術到藍翔
藍翔畢業包分配
尼瑪還是富士康
複製程式碼
來看看 log 日誌,可以看到已經正確提交啦!
這個時候專案管理員就來合併程式碼啦!記得 fetch, 記得切換到 master 分支。
接著再來執行
git merge --no-ff 348a5e9987981d34ceea031c9cf442bd0aaa7c3c
複製程式碼
??,你會發現並沒有衝突,哈哈這是不是很尷尬呀。 那是因為我們是在 master 上將開發提交的分支往 master 合併。 而開發者其實是包含了 master 的所有檔案和記錄。因此這次合併是正常的。其實我就是為了再一次鞏固一下合併程式碼的流程。
其實衝突是多個開發者之間提交的程式碼中可能都修改同一個檔案,且各自的程式碼都不一致。這個時候專案管理者就要做權衡,選擇最好的結果。舉個例子,現在程式碼管理者對 123.txt 進行修改後提交,普通開發者也修改了 123.txt 並提交。
先來看看普通開發者的修改,請注意一定要拉取程式碼 fetch, 然後 checkout。再次鞏固這個兩個命令的使用:
// 拉取遠端程式碼倉庫的所有分支
git fetch --all
// checkout 切換, branc_name 有意義的分支名, hash: 當前分支的唯一標示
git checkout -b branch_name hash
複製程式碼
然後使用文字編輯修改內容為:
111111111111111
asdfasdfsa;dlf
故人西辭富士康
為學技術到藍翔
藍翔畢業包分配
尼瑪還是富士康
複製程式碼
然後提交和推送程式碼到遠端倉庫。再次回顧命令的使用:
// 將其加入到本地快取
git add 123.txt
// 提交程式碼到本地倉庫
git commit -m "修改了 123.txt 內容"
// 將程式碼推送到遠端倉庫
git push origin HEAD
複製程式碼
使用 log 檢視,命令回顧:
git log --graph --decorate --all
複製程式碼
不難看出,這一次提交要晚於遠端程式碼倉庫的 master 分支的。
接下來看專案管理者的修改, 請注意,這裡並沒有將分支切換到開發者提交的分支上,而是在原來的老的分支上進行直接的修改。
看到下面的 log, 我沒有騙你,我確實是在老的分支上,而開發者新提交的程式碼並沒有合併到 master
來看專案管理者修改內容變成了什麼,是不是和之前的完全不一樣,並且將之前的全部改掉啦!
接著又是熟悉的步驟, add、commit、push
OK, 提交成功後,嘗試合併。並將最終分支合併到 master。
合併的命令回顧:
git merge --no-ff 被合併的分支
複製程式碼
看到執行 merge 後的列印了嗎?
// 告訴我們嘗試自動合併失敗,需要手動合併解決衝突
Automatic merge failed; fix conflicts and then commit the result.
複製程式碼
OK, 這裡你應該選擇你最使用方便的編輯器來進行程式碼的合併操作,比如你可以使用 AS 自帶的 git 外掛。筆者這裡使用的是 Emacs 的 git 外掛。下面是本次合併資訊,可以看出來是分段的,第1、2 行表示當前的分支,3、4兩行表示要被合併的分支; 剩下的幾行表示有哪些檔案修改需要被合併。
我們進入到未合併的檔案 123.txt, 可以發現當前 HEAD 就是管理員提交的程式碼,而下面的就是要被合併的開發者提交的程式碼。
這裡我就直接選擇管理員的程式碼作為最終的程式碼,在實際開發中,稽核著就是要承擔這樣的責任,需要合理的選擇誰提交的程式碼可用,或者抽取各自好的程式碼進行合併。
最終選擇的結果,可以看到我刪除了開發者的程式碼,並將輔助的資訊全部刪除。比如有:
>> 開頭的
<< 開頭的
==== 開頭的
複製程式碼
合併完成後又進入到熟悉的步驟, commit、push。看到下面的圖,可以看到分支又被何在一起,你可以自己好好觀察下分支走向。那些分支合到那個分支上,只要看懂了,遇到再複雜的分支也不會暈頭轉向。
好啦, 寫到這兒就將整個 git 的常規操作全部講完了。讓我們來再一次回顧一下,因為內容太長,為了讓讀者能熟悉命令,看完後能記住大部分。我給你再總結一次。
寫在前面: 如果你的倉庫已經存在提交,你第一步就需要 fetch, 然後切換到最新的版本上去,一般是 master 所在的分支
git fetch --all
2. 切換到指定 hash 的分支
git checkout -b branch_name hash
3. 新建一個檔案後,需要將其加入到緩衝區,稱為追蹤檔案
git add xxx
4. 將快取去的檔案加入到原生程式碼倉庫
git commit -m "提交訊息內容"
5. 提交到遠端倉庫
git push origin HEAD:branch_name
輔助命令:
1. 檢視當前提交的狀態
git status
2. 檢視日誌,分支樹
git log --graph --decorate --all
管理者使用:
1. merge 合併程式碼,將指定分支合併到某一個分支,一般是 master
git merge --no-ff hash
複製程式碼
其實上面的是最常見的幾個命令啦!基本上熟練使用這幾個指令就能完成大部分工作。筆者用的最多也是這幾個命令。下一篇作為補充,再增加幾個命令,雖然不常用,但是也要知道。