1. 程式人生 > >Git 看這一篇就夠了

Git 看這一篇就夠了

上一篇講 Git 的文章發出來沒想到效果特別好,很多讀者都要求繼續深入的寫。 那今天齊姐簡單講下 Git 的實現原理,知其所以然才能知其然;並且梳理了日常最常用的 12 個命令,分為三大類分享給你。 本文的結構如下: 1. **作者和開發原由** 2. **Git 的資料模型** 3. **常用命令** 4. **資源推薦** ## 作者和開發原由 > Talk is cheap. Show me the code. 這句話就出自 Linux 和 Git 的作者 `Linus Torvalds`。 原本 Linux 核心的版本控制系統是用的 BitKeeper,然而 2005 年,BitMover 公司不再讓 Linux 開發團隊免費使用了。。 Linus 一聽,不給用了?老子自己寫! 於是,大佬十天之內完成了 Git 的第一個版本。 所以 Git 是一個免費的、開源的版本控制系統。 ### 版本控制系統 版本控制其實每個人都用過,那些年修改過的簡歷: > 小齊簡歷 2012 版 > 小齊簡歷 2013 版 > 小齊簡歷 2014 版 > 小齊簡歷 2015 版 > 小齊簡歷 2016 版 > 小齊簡歷 2017 版 > 小齊簡歷 2018 版 > 小齊簡歷 2019 版 > ... 還有那些年打死都不再改的畢業論文: > 畢業論文最終版 > 畢業論文最最終版 > 畢業論文最最最終版 > 畢業論文最最最最終版 > 畢業論文最終不改版 > 畢業論文最終真不改版 > 畢業論文最終真真不改版 > 畢業論文最終打死不改版 > 畢業論文最終打死不改版 2 > ... 沒錯,這就是本地版本控制系統。 很明顯,好處是簡單,但是隻能一個人在這改,無法和他人完成合作。那麼以下兩種主流的版本控制系統應運而生。 #### 1. 集中化版本控制系統 Centralized Version Control Systems (CVCS) 比如:CVS, Subversion, Perforce, etc. 這種版本控制系統有一個單一的集中管理的伺服器,儲存所有檔案的最新版本,大家可以通過連線到這臺伺服器上來獲取或者提交檔案。 ![](https://img2020.cnblogs.com/other/2000306/202010/2000306-20201014095036662-1091864532.jpg) 這種模式相對本地版本控制系統是有所改進的,但是缺點也很明顯,如果伺服器宕機,那麼輕則耽誤工作、重則資料丟失。於是分散式版本控制系統應運而生。 #### 2. 分散式版本控制系統 Distributed Version Control Systems (DVCS) 比如:Git, Mercurial, Bazaar, etc. 分散式的版本控制系統會把**程式碼倉庫完整地映象**下來,這樣任何一個伺服器發生故障,都可以用其他的倉庫來修復。 更進一步,這種模式可以更方便的和不同公司的人進行同一專案的開發,因為兩個遠端程式碼倉庫可以互動,這在之前的集中式系統中是無法做到的。 #### 那麼什麼叫“把程式碼倉庫完整地映象下來”呢? CVCS 每個版本存放的是當前版本與前一個版本的差異,因此也被稱作基於差異的版本控制 (delta-based); Git 儲存的是所有檔案的一個快照 (snapshot),如果有的檔案沒有修改,那就只保留一個 reference 指向之前儲存的檔案。 不是很好理解?那接著看吧~ ## Git 的資料模型 #### 1. 什麼是快照 (snapshot) 呢? 首先我們來學兩個 Git 中的術語: - blob, 就是單個的檔案; - tree, 就是一個資料夾。 快照則是被追蹤的最頂層的樹。 比如我的“公眾號”資料夾的這麼一個結構: ![](https://img2020.cnblogs.com/other/2000306/202010/2000306-20201014095040248-2131764030.jpg) 那麼一個快照就是追蹤的“公眾號”這顆樹。 #### 2. 本地庫的資料模型 Git 記錄了每個快照的 parent,也就是當前這個資料夾的上一個版本。 那麼快照的迭代更新的過程就可以表示為一個**有向無環圖**,是不是很熟悉?我們在「拓撲」那篇文章裡講過,忘了的小夥伴快去公眾號內回覆「拓撲」獲取拓撲的入門文章吧~ ![](https://img2020.cnblogs.com/other/2000306/202010/2000306-20201014095042043-8793651.jpg) 每個快照其實都對應了一次 `commit`,我們用程式碼來表示一下: ``` class commit { array parents String author String message Tree snapshot } ``` 這就是 Git 的資料模型。 blob, tree, snapshot 其實都一樣,它們在 Git 中都是`物件`,都可以被引用或者被搜尋,會基於它們的 `SHA-1 hash` 進行定址。 > `git cat-file -t`: 檢視每個 SHA-1 的型別; > `git cat-file -p`: 檢視每個物件的內容和簡單的資料結構。 但是通過這個雜湊值來搜尋也太不方便了,畢竟這是一串 40 位的十六進位制字元,就是第二部分 `git log` 裡輸出的那個`編碼`。 因此,Git 還給了一個引用 `reference`。 比如,我們常見的 `HEAD` 就是一個特殊的引用。 本地庫就是由 `物件` 和 `引用` 構成的,或者叫 `Repositories`. 在硬碟上,Git 只儲存 `物件` 和 `引用`,所有的 Git 命令都對應提交一個快照。 那有哪些常用命令呢? ## 常用命令 本章分三大部分介紹日常常用命令: - 本地操作 - 和遠端庫的互動 - 團隊協作 - 分支 ### 本地操作 在學習常用命令之前,你首先需要知道的 Git 的「三個分割槽」和對應的檔案的「三種狀態」: ![](https://img2020.cnblogs.com/other/2000306/202010/2000306-20201014095044144-2015830893.jpg) - `工作區`:就是你本地實際寫程式碼的地方,無論你是用 vim 直接改也好,還是在 IDE 裡寫,都無所謂。 - 對應的檔案狀態是:`modified`,已修改,但還沒儲存到資料庫中。 - `暫存區`:就是臨時存放的地方。 - 對應的檔案狀態是:`staged`,Git 已經對該檔案做了標記,下次提交知道要包含它。 - `本地庫`:存放本地歷史版本資訊。 - 對應的檔案狀態是:`committed`,檔案已經安全的儲存在本地資料庫中。 #### 1. $ git add 工作區改完了程式碼,就用 `git add` 提交到暫存區。 這裡如果檔案改動的比較多,但又不是每個都需要提交,我會設定 `git ignore file`,就表示這些檔案不要提交,比如在 build project 的時候會自動生成的那些檔案等等。 #### 2. \$ git commit -m "comment" 從暫存區提交到本地庫,就需要用 commit。 一般後面都會跟個 `-m` 加句 `comment`,簡單說下改動的內容或者原因,我們公司大家預設也會把 `Jira`連結附上,這樣就知道這個改動對應哪個任務。 那如果想再改,再重新 `git add` 即可,但是 `commit` 這句需要改成 ``` $ git commit --amend ``` 這樣就還是一條 git log 資訊。 #### 3. \$ git log `git log` 可以檢視到提交過的資訊,從近到遠顯示每次 commit 的 comment 還有作者、日期等資訊,比如大概長這個樣子: ``` commit 5abcd17dggs9s0a7a91nfsagd8ay76875afs7d6 Author: Xiaoqi Date: xxx xxx xxx 改了 Test 檔案 ``` commit 後面的這個`編號`,是每次歷史記錄的一個`索引`。比如如果需要對版本進行前進或者後退的時候,就需要用到它。 這樣列印的 log 太多,更簡潔的列印方式是: ``` $ git log --oneline ``` 就一行打印出來了。 或者: ``` $ git reflog ``` 更常用一些。 #### 4. \$ git reset 那我們剛剛說過,如果需要前進或退回到某個版本,就用 ``` $ git reset --hard <編號> ``` 這樣就直接跳到了這個`編號`對應的那個版本。 那麼這個 `hard` 是什麼意思呢? 這裡有 3 個引數:`hard`, `soft`, `mixed`,我們一一來說一下。 回到我們最重要的這張圖上來: ![](https://img2020.cnblogs.com/other/2000306/202010/2000306-20201014095045573-647577219.jpg) 我們剛剛說的前進或後退到某一版本,是對`本地庫`進行的操作。 那有個問題: **本地庫的程式碼跳到那個版本之後,工作區和暫存區的程式碼就和本地庫的不同步了呀!** 那這些引數就是用來控制這些是否**同步**的。 #### \$ git reset --hard xxx 三個區都同步,都跳到這個 xxx 的版本上。 #### \$ git reset --soft xxx 前面兩個區不同步,就只有本地庫跳到這個版本。 #### \$ git reset --mixed xxx 暫存區同步,工作區不動。 所以呢,用的多的就是 hard. ![](https://img2020.cnblogs.com/other/2000306/202010/2000306-20201014095046666-1363885381.jpg) ### 遠端互動 ![](https://img2020.cnblogs.com/other/2000306/202010/2000306-20201014095048766-1420736537.jpg) 和遠端庫的互動主要是`推`、`拉`,也就是寫入和讀取。 #### 5. $ git push 小齊寫完了程式碼,要提交到公司的程式碼庫裡,這個過程要用 `git push`. 當然了,這麼用會被打的。。畢竟還要 cr 呢。 #### 5. $ git clone 新來的實習生首先要 clone 整個專案到本地來,然後才能增刪改查。 當然了實際工作中也沒人這麼用。。因為每家公司都會有自己包裝的工具。不過如果是做 Github 上的開源專案,就用得上了。 #### 6. $ git pull 小齊提交了新的程式碼之後,領導要審查呀,所以用 `git pull` 把最新的程式碼拉取下來瞅瞅。 實際上呢, `git pull = fetch + merge` #### 7. $ git fetch `git fetch` 這個操作是將遠端庫的資料下載到本地庫,但是工作區中的檔案沒有更新。 ![](https://img2020.cnblogs.com/other/2000306/202010/2000306-20201014095051715-560367485.jpg) 而要談 `get merge`,我們還需要先講下`分支`。 `merge` 是 `git pull` 預設的選項,合併其實還有另外一種方法:`rebase`,中文叫做**變基**。 #### 8. $ git rebase rebase 的作用更多的是來整合分叉的歷史,可以將某個分支上的所有修改都移到另一分支上,就像是變了基底。 ### 分支與合併 首先我們來看幾個關於分支的基本操作: #### 9. 檢視分支: `$ git branch` 類似於`ls`,能夠列出當前所有分支。 `git branch -v` 能夠顯示更多資訊。 #### 10. 建立分支: `$ git branch ` #### 11. 切換分支: `$ git checkout ` 有了分支之後必然會有合併: #### 12. 合併分支: `$ git merge ` 而合併時就可能會有衝突,什麼時候會有衝突呢?: **在同一個檔案的同一個位置修改時。** 因為 Git 會努力的把你們改動不同的地方合併在一起,但如果實在是在同一個地方改的,那它也沒辦法了,只能留給程式設計師去手動處理了。 當然了,每個命令延伸下去還有無限多個,本文不可能涵蓋全部,所以在此重磅推薦齊姐精心挑選的三大學習資源,大家可以自行享用~ ## 學習資源 ### git help 其實我個人使用最多的是`git help` 真心方便又好用啊! 比如 `git help pull`: ![](https://img2020.cnblogs.com/other/2000306/202010/2000306-20201014095057256-1878795116.jpg) 先介紹了有哪些引數,然後 description 詳細解釋了它的工作原理,下面還有圖解,有木有太香!! 不過這種方式更像是 `cheatsheet`,當你已經知道了這個命令、只是忘了它的用法的時候去查。 如果你想系統的學習,那麼下面