教你打通 Git 的任督二脈

大家好,我是 bigsai,一個不愛打球的程序員

這篇主要讓我們來學習一下 Git,這個分佈式版本控制系統


在日常工作中,經常會用到 Git 操作。但是對於很多人來講,剛上來對 Git 很陌生,操作起來也很懵逼。本篇文章主要針對剛開始接觸 Git 的新人,理解 Git 的基本原理,掌握常用的一些命令。

關於版本控制

什麼是版本控制?我真的需要嗎?版本控制是一種記錄若干文件內容變化,以便將來查閱特定版本修訂情況的系統。

什麼是分佈式版本控制系統

分佈式版本控制系統( Distributed Version Control System,簡稱 DVCS )。

在這類系統中,像 Git,Mercurial,Bazaar 以及 Darcs 等,客戶端並不只提取最新版本的文件快照,而是把原始的代碼倉庫完整地鏡像下來。這麼一來,任何一處協同工作用的服務器發生故障,事後都可以用任何一個鏡 像出來的本地倉庫恢復。因爲每一次的提取操作,實際上都是一次對代碼倉庫的完整備份。

更進一步,許多這類系統都可以指定和若干不同的遠端代碼倉庫進行交互。藉此,你就可以在同一個項目中,分別和不同工作小組的人相互協作。你可以根據需要設定不同的協作流程,比如層次模型式的工作流,而這在以前的集中式系統中是無法實現的。

一、Git 工作流程

以上包括一些簡單而常用的命令,但是先不關心這些,先來了解下面這 4 個專有名詞。

  1. Workspace:工作區

  2. Index / Stage:暫存區

  3. Repository:倉庫區(或本地倉庫)

  4. Remote:遠程倉庫

工作區

指的是在 PC 中能看得到的創建的一個管理倉庫的目錄,比如我的 test 文件夾就是一個工作區:如下圖:

暫存區

.git 目錄下的 index 文件, 暫存區會記錄 git add 添加文件的相關信息 (文件名、大小、timestamp...),不保存文件實體, 通過 id 指向每個文件實體。可以使用 git status 查看暫存區的狀態。暫存區標記了你當前工作區中,哪些內容是被 git 管理的。

當你完成某個需求或功能後需要提交到遠程倉庫,那麼第一步就是通過 git add 先提交到暫存區,被 git 管理。

本地倉庫

保存了對象被提交 過的各個版本,比起工作區和暫存區的內容,它要更舊一些。

git commit 後同步 index 的目錄樹到本地倉庫,方便從下一步通過 git push 同步本地倉庫與遠程倉庫的同步。

遠程倉庫

遠程倉庫的內容可能被分佈在多個地點的處於協作關係的本地倉庫修改,因此它可能與本地倉庫同步,也可能不同步,但是它的內容是最舊的。

小結

  1. 任何對象都是在工作區中誕生和被修改;

  2. 任何修改都是從進入 index 區纔開始被版本控制;

  3. 只有把修改提交到本地倉庫,該修改才能在倉庫中留下痕跡;

  4. 與協作者分享本地的修改,可以把它們 push 到遠程倉庫來共享。

下面這幅圖更加直接闡述了四個區域之間的關係,可能有些命令不太清楚,沒關係,下部分會詳細介紹。

二、常用 Git 命令

==============

網上找了個圖,別人整理的一張圖,很全很好,借來用下。下面詳細解釋一些常用命令。

在掌握具體命令前,先理解下 HEAD。

這要從 git 的分支說起,git 中的分支,其實本質上僅僅是個指向 commit 對象的可變指針。git 是如何知道你當前在哪個分支上工作的呢?其實答案也很簡單,它保存着一個名爲 HEAD 的特別指針。在 git 中,它是一個指向你正在工作中的本地分支的指針,可以將 HEAD 想象爲當前分支的別名。

add

add 相關命令很簡單,主要實現將工作區修改的內容提交到暫存區,交由 git 管理。

  1. git add . 添加當前目錄的所有文件到暫存區

  2. git add dir  添加指定目錄到暫存區,包括子目錄

  3. git add file 添加指定文件到暫存區

git add -A 和 git add . 和 git add -u 的區別

git add -A : 把所有變化提交到索引庫,包含當前git倉庫的所有目錄
git add -u : 提交被修改(modified)和被刪除(deleted)文件,不包括新文件(new)
git add . : 該操作與git 的版本有關:
     -1.x 版本:提交新文件(new)和被修改(modified)文件,不包括被刪除(deleted)文件
     -2.x 版本:和git add -A一樣,提交所有變化

commit

git commit 主要是將暫存區裏的改動給提交到本地的版本庫。每次使用 git commit 命令我們都會在本地版本庫生成一個 40 位的哈希值,這個哈希值也叫 commit-id,commit-id 在版本回退的時候是非常有用的,它相當於一個快照, 可以在未來的任何時候通過與 git reset 的組合命令回到這裏.

  1. git commit -m message 提交暫存區到本地倉庫, message 代表說明信息

  2. git commit file -m message 提交暫存區的指定文件到本地倉庫

  3. git commit --amend -m message 使用一次新的 commit,替代上一次提交

branch

涉及到協作,自然會涉及到分支,關於分支,大概有展示分支,切換分支,創建分支,刪除分支這四種操作。

  1. git branch 列出所有本地分支

  2. git branch -r 列出所有遠程分支

  3. git branch -a 列出所有本地分支和遠程分支

  4. git branch branch-name 新建一個分支,但依然停留在當前分支

  5. git checkout -b branch-name 新建一個分支,並切換到該分支

  6. git branch --track branch remote-branch 新建一個分支,與指定的遠程分支建立追蹤關係

  7. git checkout branch-name 切換到指定分支,並更新工作區

  8. git branch -d branch-name 刪除分支

  9. git push origin --delete branch-name 刪除遠程分支

關於分支的操作雖然比較多,但都比較簡單好記。

merge

Git 的 git-merge 是在 Git 中頻繁使用的一個命令,很多人都覺得 git 合併是一個非常麻煩的事情,一不小心就會遇到丟失代碼的問題,從而對 git 望而卻步。

merge 命令把不同的分支合併起來。如上圖,在實際開放中,我們可能從 master 分支中切出一個分支,然後進行開發完成需求,中間經過 R3,R4,R5 的 commit 記錄,最後開發完成需要合入 master 中,這便用到了 merge。

  1. git fetch remote merge 之前先拉一下遠程倉庫最新代碼

  2. git merge branch 合併指定分支到當前分支 一般在 merge 之後,會出現 conflict,需要針對衝突情況,手動解除衝突。主要是因爲兩個用戶修改了同一文件的同一塊區域。

rebase

rebase 又稱爲衍合,是合併的另外一種選擇。

在開始階段,我們處於 new 分支上,執行 git rebase dev,那麼 new 分支上新的 commit 都在 master 分支上重演一遍,最後 checkout 切換回到 new 分支。這一點與 merge 是一樣的,合併前後所處的分支並沒有改變。git rebase dev,通俗的解釋就是 new 分支想站在 dev 的肩膀上繼續下去。rebase 也需要手動解決衝突。

rebase 與 merge 的區別

現在我們有這樣的兩個分支, test 和 master,提交如下:

      D---E test
     /
A---B---C---F master

在 master 執行 git merge test, 然後會得到如下結果:

      D--------E
     /          \
A---B---C---F----G   test, master

在 master 執行 git rebase test,然後得到如下結果:

A---B---D---E---C'---F'   test, master

可以看到,merge 操作會生成一個新的節點,之前的提交分開顯示。而 rebase 操作不會生成新的節點,是將兩個分支融合成一個線性的提交。

如果你想要一個乾淨的,沒有 merge commit 的線性歷史樹,那麼你應該選擇 git rebase 如果你想保留完整的歷史記錄,並且想要避免重寫 commit history 的風險,你應該選擇使用 git merge

reset

有時候,我們用 Git 的時候有可能 commit 提交代碼後,發現這一次 commit 的內容是有錯誤的,那麼有兩種處理方法:

  1. 修改錯誤內容,再次 commit 一次

  2. 使用 git reset 命令撤銷這一次錯誤的 commit

第一種方法比較直接,但會多次一次 commit 記錄。而我個人更傾向第二種方法,錯誤的 commit 沒必要保留下來。

reset 命令把當前分支指向另一個位置,並且相應的變動工作區和暫存區。

  1. git reset —soft commit 只改變提交點,暫存區和工作目錄的內容都不改變

  2. git reset —mixed commit 改變提交點,同時改變暫存區的內容

  3. git reset —hard commit 暫存區、工作區的內容都會被修改到與提交點完全一致的狀態

  4. git reset --hard HEAD 讓工作區回到上次提交時的狀態

revert

git revert 用一個新提交來消除一個歷史提交所做的任何修改。

revert 與 reset 的區別

  1. git revert 是用一次新的 commit 來回滾之前的 commit,git reset 是直接刪除指定的 commit。

  2. 在回滾這一操作上看,效果差不多。但是在日後繼續 merge 以前的老版本時有區別。因爲 git revert 是用一次逆向的 commit“中和” 之前的提交,因此日後合併老的 branch 時,導致這部分改變不會再次出現,減少衝突。但是 git reset 是之間把某些 commit 在某個 branch 上刪除,因而和老的 branch 再次 merge 時,這些被回滾的 commit 應該還會被引入,產生很多衝突。關於這一點,不太理解的可以看這篇文章。

  3. git reset 是把 HEAD 向後移動了一下,而 git revert 是 HEAD 繼續前進,只是新的 commit 的內容和要 revert 的內容正好相反,能夠抵消要被 revert 的內容。

push

git push 的一般形式爲 git push <遠程主機名> < 本地分支名 >  < 遠程分支名 > ,例如 git push origin master:refs/for/master ,即是將本地的 master 分支推送到遠程主機 origin 上的對應 master 分支, origin 是遠程主機名

  1. git push remote branch 上傳本地指定分支到遠程倉庫

  2. git push remote --force 強行推送當前分支到遠程倉庫,即使有衝突

  3. git push remote --all 推送所有分支到遠程倉庫

其他命令

  1. git status 顯示有變更的文件

  2. git log 顯示當前分支的版本歷史

  3. git diff 顯示暫存區和工作區的差異

  4. git diff HEAD 顯示工作區與當前分支最新 commit 之間的差異

  5. git cherry-pick commit 選擇一個 commit,合併進當前分支

以上就是關於 Git 的一些常用命令及詳細闡述,相信能對 Git 有一個初步的認識。

本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源https://mp.weixin.qq.com/s/zvJ6Ok1I2gbUjJgCdCNDjQ