Git 備忘清單詳解
前言
Git 是一個免費并且開源的分布式版本控制系統(tǒng),旨在快速高效地處理從小到大所有項目的版本管理。Git 是目前最流行的版本管理工具,目前絕大部分公司都是使用 Git 作為項目的版本管理工具。目前最火的開源社區(qū) Github,就是基于 Git 版本控制系統(tǒng),所以掌握 Git 技能很重要。由于 Git 開發(fā)效率高、團隊協(xié)作方便,現(xiàn)在很多 IDE 都集成了 Git,并且提供一些相關(guān)的圖形化操作。也有很多很優(yōu)秀,專門用來簡化 Git 操作的 Git GUI 工具,例如 Sourcetree,Tortoise 等。我剛接觸 Git 的時候,就是從 GUI 入手的,使用 Sourcetree 可視化版本控制工具進行操作。Sourcetree 底層也是對常用的 Git 命令進行封裝實現(xiàn)的,傻瓜式操作,使用非常方便,但是我用完什么都不懂,Git 的內(nèi)部原理也不易理解,決定在回頭仔細學習一下 Git 命令行。學習完 Git 命令行后發(fā)現(xiàn),命令行很好學,非常靈活,而且使用起來非常帥氣。因此,直接 Git 命令,才是最靈活的操作。
本篇是我學習 Git 系列的開篇,主要講述 Git 的基本概念和工作原理,然后介紹一下 Git 安裝以及環(huán)境配置,最后探討一下 Git 常用命令以及使用場景。
Git 基本概念以及工作原理
你所不了解的 Git 誕生史
同生活中的許多偉大事件一樣,Git 誕生于一個極富紛爭大舉創(chuàng)新的年代。眾所周知,Linux 內(nèi)核開源項目有著為數(shù)眾廣的參與者,但是絕大多數(shù)的 Linux 內(nèi)核維護工作都花在了提交補丁和保存歸檔的繁瑣事務上(1991-2002 年間),即 Linus 本人通過手工方式合并世界各地志愿者通過 diff 的方式傳過來的代碼。
到 2002 年后,由于代碼庫太大,Linus 很難繼續(xù)通過手工方式進行管理,于是 Linux 整個項目組開始啟用分布式版本控制系統(tǒng) BitKeeper 來管理和維護代碼,BitKeeper 的東家 BitMover 公司出于人道主義精神,授權(quán) Linux 社區(qū)免費使用這個版本控制系統(tǒng)。
到了 2005 年,一位 Linux 開發(fā)成員 Andrew(Samba 協(xié)議之父)寫了一個可以連接 BitKeeper 倉庫的外掛,因此 BitMover 公司(BitKeeper 持有者)認為他反編譯了 BitKeeper;于是 BitMover 決定中止 Linux 免費使用 BitKeeper 的授權(quán)。最終 Linux 團隊與 BitMover 磋商無果,開發(fā) BitKeeper 的商業(yè)公司同 Linux 內(nèi)核開源社區(qū)的合作關(guān)系結(jié)束,他們收回了免費使用 BitKeeper 的權(quán)力。這就迫使 Linux 開源社區(qū)(特別是 Linux 的締造者 Linus Torvalds)不得不吸取教訓,只有開發(fā)一套屬于自己的版本控制系統(tǒng)才不至于重蹈覆轍。
于是,Linus 花了兩周時間用 C 語言寫了一個分布式版本控制系統(tǒng),這就是 Git!一個月之內(nèi),Linux 系統(tǒng)的源碼已經(jīng)由 Git 進行管理了!
Git 工作原理
Git 是一套內(nèi)容尋址文件系統(tǒng),Git 從核心上來看不過是簡單地存儲鍵值對(key-value)。它允許插入任意類型的內(nèi)容,并會返回一個鍵值,通過該鍵值可以在任何時候再取出該內(nèi)容??梢酝ㄟ^底層命令 hash-object 來示范這點,傳一些數(shù)據(jù)給該命令,它會將數(shù)據(jù)保存在 .git 目錄并返回表示這些數(shù)據(jù)的鍵值。
文件目錄

Git 文件目錄
Git 工作區(qū)有個隱藏目錄. git,核心文件包括:config 文件、objects 文件夾、HEAD 文件、index 文件以及 refs 文件夾。下面依次對其進行說明:
config 文件:該文件主要記錄針對該項目的一些配置信息,例如是否以 bare 方式初始化、remote 的信息等,通過 git remote add 命令增加的遠程分支的信息就保存在這里;
objects 文件夾:該文件夾主要包含 git 對象。Git 中的文件和一些操作都會以 git 對象來保存,git 對象分為 BLOB、tree 和 commit 三種類型,例如 git commit 便是 git 中的 commit 對象,而各個版本之間是通過版本樹來組織的,比如當前的 HEAD 會指向某個 commit 對象,而該 commit 對象又會指向幾個 BLOB 對象或者 tree 對象。objects 文件夾中會包含很多的子文件夾,其中 Git 對象保存在以其 sha-1 值的前兩位為子文件夾、后 38 位位文件名的文件中;除此以外,Git 為了節(jié)省存儲對象所占用的磁盤空間,會定期對 Git 對象進行壓縮和打包,其中 pack 文件夾用于存儲打包壓縮的對象,而 info 文件夾用于從打包的文件中查找 git 對象;
HEAD 文件:該文件指明了 git branch(即當前分支)的結(jié)果,比如當前分支是 master,則該文件就會指向 master,但是并不是存儲一個 master 字符串,而是分支在 refs 中的表示,例如 ref: refs/heads/master。
index 文件:該文件保存了暫存區(qū)域的信息。該文件某種程度就是緩沖區(qū)(staging area),內(nèi)容包括它指向的文件的時間戳、文件名、sha1 值等;
Refs 文件夾:該文件夾存儲指向數(shù)據(jù)(分支)的提交對象的指針。其中 heads 文件夾存儲本地每一個分支最近一次 commit 的 sha-1 值(也就是 commit 對象的 sha-1 值),每個分支一個文件;remotes 文件夾則記錄你最后一次和每一個遠程倉庫的通信,Git 會把你最后一次推送到這個 remote 的每個分支的值都記錄在這個文件夾中;tag 文件夾則是分支的別名,這里不需要對其有過多的了解;
工作區(qū)域
Git 本地有三個工作區(qū)域:工作目錄(Workspace)、暫存區(qū) (Stage/Index)、資源庫(Repository)。如果在加上遠程的 git 倉庫(Remote Directory) 就可以分為四個工作區(qū)域。文件在這四個區(qū)域之間的轉(zhuǎn)換關(guān)系如下:

git 工作區(qū)域
Workspace:工作區(qū),就是你平時存放項目代碼的地方。
Index / Stage:暫存區(qū),用于臨時存放你的改動,事實上它只是一個文件,保存即將提交到文件列表信息。
Repository:倉庫區(qū)(或本地倉庫),就是安全存放數(shù)據(jù)的位置,這里面有你提交到所有版本的數(shù)據(jù)。其中 HEAD 指向最新放入倉庫的版本。
Remote:遠程倉庫,托管代碼的服務器,可以簡單的認為是你項目組中的一臺電腦用于遠程數(shù)據(jù)交換。
工作流程
Git 的工作流程一般是這樣的:
1、在工作目錄中添加、修改文件;
2、將需要進行版本管理的文件放入暫存區(qū)域;
3、將暫存區(qū)域的文件提交到 Git 倉庫。

git 工作流程
Git 文件 4 種狀態(tài)
Untracked: 未跟蹤, 此文件在文件夾中, 但并沒有加入到 git 庫, 不參與版本控制. 通過 git add 狀態(tài)變?yōu)?Staged.
Unmodify: 文件已經(jīng)入庫, 未修改, 即版本庫中的文件快照內(nèi)容與文件夾中完全一致. 這種類型的文件有兩種去處, 如果它被修改, 而變?yōu)?Modified. 如果使用 git rm 移出版本庫, 則成為 Untracked 文件
Modified: 文件已修改, 僅僅是修改, 并沒有進行其他的操作. 這個文件也有兩個去處, 通過 git add 可進入暫存 staged 狀態(tài), 使用 git checkout 則丟棄修改過, 返回到 unmodify 狀態(tài), 這個 git checkout 即從庫中取出文件, 覆蓋當前修改
Staged: 暫存狀態(tài). 執(zhí)行 git commit 則將修改同步到庫中, 這時庫中的文件和本地文件又變?yōu)橐恢? 文件為 Unmodify 狀態(tài). 執(zhí)行 git reset HEAD filename 取消暫存, 文件狀態(tài)為 Modified

GIT 文件 4 種狀態(tài)
Git 安裝以及環(huán)境配置
本文統(tǒng)一使用軟件包管理器的方式安裝 Git,減少環(huán)境變量的配置,更加方便快捷。
Linux 安裝 Git
CentOS7 中使用 yum 安裝 Git 的方法
# 1. 查看 git 是否安裝
$ git --version
# 2. 顯示所有已經(jīng)安裝和可以安裝的 git 程序包
$ sudo yum list git
# 3. 使用 yum 安裝
$ sudo yum install git -y
# 4. 查看安裝是否成功
$ git --version
Mac 安裝 Git
Mac 中使用 brew 安裝 Git 的方法
# 1. 查看 git 是否安裝
$ git --version
# 2. 使用 brew 安裝
$ brew install git
# 3. 查看安裝是否成功
$ git --versionWindows 安裝 Git
Windows 中使用 choco 安裝 Git 的方法
# 1. 查看 git 是否安裝
$ git --version
# 2. 搜索 git 安裝包
$ choco search git
# 3. 使用 choco 安裝
$ choco install git
# 4. 查看安裝是否成功
$ git --version
全局配置
Git 用戶的配置文件位于 ~/.gitconfig
Git 單個倉庫的配置文件位于 ~/$PROJECT_PATH/.git/config
# 1. 顯示當前的 Git 配置
$ git config --list
# 2. 編輯 Git 配置文件 p.s.[--global: 表示全局配置, 如果不加, 則表示當前 git 倉庫的配置]
$ git config -e [--global]
# 3. 設置提交代碼時的用戶信息
$ git config [--global] user.name "[name]"
$ git config [--global] user.email "[email address]"
如果用了 –global 選項,那么更改的配置文件就是位于你用戶主目錄下的那個,以后你所有的項目都會默認使用這里配置的用戶信息。如果要在某個特定的項目中使用其他名字或者電郵,只要去掉 –global 選項重新配置即可,新的設定保存在當前項目的 .git/config 文件里。
服務器上的 Git - 生成 SSH 公鑰
大多數(shù) Git 服務器都會選擇使用 SSH 公鑰來進行授權(quán)。系統(tǒng)中的每個用戶都必須提供一個公鑰用于授權(quán),沒有的話就要生成一個。SSH 公鑰默認儲存在賬戶的主目錄下的 ~/.ssh 目錄。
# 1. 進入主目錄下的~/.ssh 目錄
$ cd ~/.ssh
# 2. 創(chuàng)建一個 SSH key p.s.[-t 指定密鑰類型,默認是 rsa,可以省略;-C 設置注釋文字,比如郵箱;-f 指定密鑰文件存儲文件名]
$ ssh-keygen -t rsa -C "[email protected]"
# 3. 添加你的 SSH key 到 github 上面去
$ cat ./id_rsa.pub
# 4. 測試一下該 SSH key
$ ssh -T [email protected]
關(guān)于在多個操作系統(tǒng)上設立相同 SSH 公鑰的教程,可以查閱 GitHub 上有關(guān) SSH 公鑰的向?qū)А?/span>
Git 常用命令以及使用場景

Git 三大分區(qū)
倉庫
# 1. 在當前目錄新建一個 Git 代碼庫
$ git init
# 2. 新建一個目錄, 將其初始化為 Git 代碼庫
$ git init [project-name]
# [3]. 下載一個項目和它的整個代碼歷史
$ git clone [url]增加 / 刪除文件
# 1. 添加指定文件到暫存區(qū)
$ git add [file1] [file2] ...
# 2. 添加指定目錄到暫存區(qū), 包括子目錄
$ git add [dir]
# [3]. 添加當前目錄的所有文件到暫存區(qū)
$ git add .
# 4. 添加每個變化前, 都會要求確認[對于同一個文件的多處變化, 可以實現(xiàn)分次提交]
$ git add -p
# 5. 刪除工作區(qū)文件, 并且將這次刪除放入暫存區(qū) p.s.[git rm -f [file1]強制刪除暫存區(qū)某個文件]
$ git rm [file1] [file2] ...
# [6]. 停止追蹤指定文件, 但該文件會保留在工作區(qū)
$ git rm --cached [file]
# 7. 改名文件, 并且將這個改名放入暫存區(qū)
$ git mv [file-original] [file-renamed]代碼提交
# 1. 提交暫存區(qū)到倉庫區(qū)
$ git commit -m [message]
# 2. 提交暫存區(qū)的指定文件到倉庫區(qū)
$ git commit [file1] [file2] ... -m [message]
# [3]. 提交工作區(qū)自上次 commit 之后的變化, 直接到倉庫區(qū)
$ git commit -a
# [4]. 提交時顯示所有 diff 信息
$ git commit -v
# [5]. 使用一次新的 commit, 替代上一次提交[如果代碼沒有任何新變化, 則用來改寫上一次 commit 的提交信息]
$ git commit --amend -m [message]
# 6. 重做上一次 commit, 并包括指定文件的新變化
$ git commit --amend [file1] [file2] ...
分支
# 1. 列出所有本地分支
$ git branch
# 2. 列出所有遠程分支
$ git branch -r
# [3]. 列出所有本地分支和遠程分支
$ git branch -a
# 4. 新建一個分支, 但依然停留在當前分支
$ git branch [branch-name]
# [5]. 新建一個分支, 并切換到該分支 p.s.[git checkout -b [branch] [remote/branch]根據(jù)遠程分支,創(chuàng)建本地分支]
$ git checkout -b [branch]
$ git checkout -b [branch] [remote/branch]
# 6. 新建一個分支, 指向指定 commit
$ git branch [branch] [commit]
# 7. 新建一個分支, 與指定的遠程分支建立追蹤關(guān)系
$ git branch --track [branch] [remote-branch]
# 8. 切換到指定分支, 并更新工作區(qū)
$ git checkout [branch-name]
# [9]. 切換到上一個分支
$ git checkout -
# 10. 建立追蹤關(guān)系, 在現(xiàn)有分支與指定的遠程分支之間
$ git branch --set-upstream [branch] [remote-branch]
# [11]. 合并指定分支到當前分支 p.s.[git merge --no-ff -m "message" [branch]: 合并某分支到當前分支, 可以保存你之前的分支歷史]
$ git merge [branch]
# [12]. 選擇一個 commit,合并進當前分支 p.s.[選擇某一個分支中的一個或幾個 commit(s)來進行操作(操作的對象是 commit)]
$ git cherry-pick [commit1] [commit2] ...
# [13]. 刪除分支
$ git branch -d [branch-name]
# [14]. 刪除遠程分支
$ git push origin --delete [branch-name]
$ git branch -dr [remote/branch]
標簽
# [1]. 列出所有 tag
$ git tag
# 2. 新建一個 tag 在當前 commit p.s.[git tag -a [tag] -m [message]]
$ git tag [tag]
# 3. 新建一個 tag 在指定 commit
$ git tag [tag] [commit]
# 4. 刪除本地 tag
$ git tag -d [tag]
# [5]. 刪除遠程 tag
$ git push origin :refs/tags/[tagName]
# [6]. 查看 tag 信息
$ git show [tag]
# [7]. 提交指定 tag
$ git push [remote] [tag]
# 8. 提交所有 tag
$ git push [remote] --tags
# [9]. 新建一個分支, 指向某個 tag
$ git checkout -b [branch] [tag]
查看信息
# [1]. 顯示有變更的文件
$ git status
# 2. 顯示當前分支的版本歷史
$ git log
# [3]. 顯示 commit 歷史, 以及每次 commit 發(fā)生變更的文件
$ git log --stat
# 4. 搜索提交歷史, 根據(jù)關(guān)鍵詞
$ git log -S [keyword]
# 5. 顯示某個 commit 之后的所有變動, 每個 commit 占據(jù)一行
$ git log [tag] HEAD --pretty=format:%s
# 6. 顯示某個 commit 之后的所有變動, 其 "提交說明" 必須符合搜索條件
$ git log [tag] HEAD --grep feature
# [7]. 顯示某個文件的版本歷史, 包括文件改名
$ git log --follow [file]
$ git whatchanged [file]
# [8]. 顯示指定文件相關(guān)的每一次 diff
$ git log -p [file]
# [9]. 顯示過去 5 次提交
$ git log -5 --pretty --oneline
# [10]. 顯示所有提交過的用戶, 按提交次數(shù)排序
$ git shortlog -sn
# [11]. 顯示指定文件是什么人在什么時間修改過
$ git blame [file]
# [12]. 顯示暫存區(qū)和工作區(qū)的差異
$ git diff
# [13]. 顯示暫存區(qū)和上一個 commit 的差異
$ git diff --cached [file]
# 14. 顯示工作區(qū)與當前分支最新 commit 之間的差異
$ git diff HEAD
# [15]. 顯示兩次提交之間的差異
$ git diff [first-branch]...[second-branch]
# 16. 顯示今天你寫了多少行代碼
$ git diff --shortstat "@{0 day ago}"
# [17]. 顯示某次提交的元數(shù)據(jù)和內(nèi)容變化
$ git show [commit]
# 18. 顯示某次提交發(fā)生變化的文件
$ git show --name-only [commit]
# [19]. 顯示某次提交時,某個文件的內(nèi)容
$ git show [commit]:[filename]
# [20]. 顯示當前分支的最近幾次提交
$ git reflog
遠程同步
# [1]. 下載遠程倉庫的所有變動 p.s.[從遠程 refs/heads / 命名空間復制所有分支, 并將它們存儲到本地的 refs/remotes/origin / 命名空間]
$ git fetch [remote]
# 1.1.1 將遠程倉庫的 master 分支下載到本地當前 branch 中
$ git fetch orgin master
# 1.1.2 比較本地的 master 分支和 origin/master 分支的差別
$ git log -p master ..origin/master
# 1.1.3 最后進行合并
$ git merge origin/master
# 1.2.1 從遠程倉庫 master 分支獲取最新, 在本地建立 tmp 分支
$ git fetch origin master:tmp
# 1.2.2 將當前分支和 tmp 進行對比
$ git diff tmp
# 1.2.3 合并 tmp 分支到當前分支
$ git merge tmp
# 2. 顯示所有遠程倉庫
$ git remote -v
# 3. 顯示某個遠程倉庫的信息
$ git remote show [remote]
# 4. 增加一個新的遠程倉庫, 并命名
$ git remote add [shortname] [url]
# [5]. 取回遠程倉庫的變化,并與本地分支合并
$ git pull [remote] [branch]
# [6]. 上傳本地指定分支到遠程倉庫
$ git push [remote] [branch]
# [7]. 強行推送當前分支到遠程倉庫, 即使有沖突 p.s.[git push -f [remote] [branch]: 強行用本地倉庫覆蓋遠端倉庫, 強制推送是非常不好的行為, 建議禁止使用這個方式]
$ git push [remote] --force
# 8. 推送所有分支到遠程倉庫
$ git push [remote] --all
撤銷
# 1. 恢復暫存區(qū)的指定文件到工作區(qū) p.s.[撤銷對工作區(qū)修改: 這個命令是以最新的存儲時間節(jié)點 (add 和 commit) 為參照, 覆蓋工作區(qū)對應文件 file; 這個命令改變的是工作區(qū)]
$ git checkout [file]
# 2. 恢復某個 commit 的指定文件到暫存區(qū)和工作區(qū)
$ git checkout [commit] [file]
# [3]. 恢復暫存區(qū)的所有文件到工作區(qū), 即放棄工作區(qū)所有改動
$ git checkout .
# [4]. 重置暫存區(qū)的指定文件, 與上一次 commit 保持一致, 但工作區(qū)不變 p.s.[git reset HEAD <filename>:取消暫存某個文件]
$ git reset [file]
# 5. 重置暫存區(qū)與工作區(qū), 與上一次 commit 保持一致
$ git reset --hard
# [6]. 重置當前分支的指針為指定 commit, 同時重置暫存區(qū), 但工作區(qū)不變 p.s.[git reset HEAD:重置暫存區(qū)]
$ git reset [commit]
# [7]. 重置當前分支的 HEAD 為指定 commit, 同時重置暫存區(qū)和工作區(qū), 與指定 commit 一致 p.s.[git reset --hard HEAD:強制恢復 git 管理的文件夾的內(nèi)容及狀態(tài)]
$ git reset --hard [commit]
# 8. 重置當前 HEAD 為指定 commit, 但保持暫存區(qū)和工作區(qū)不變
$ git reset --keep [commit]
# [9]. 抵消式撤銷, 新建一個 commit, 用來撤銷指定 commit, 后者的所有變化都將被前者抵消, 并且應用到當前分支 p.s.[git revert [commit]..HEAD: 撤銷指定 commit 到當前 HEAD 之間所有的變化]
$ git revert [commit]
# [10]. 暫時將未提交的變化移除, 稍后再移入
$ git stash
# 10.1 將當前 stash 中的內(nèi)容彈出, 并應用到當前分支對應的工作目錄上
$ git stash pop
# 10.2 查看當前 stash 中的內(nèi)容
$ git stash list
# 10.3 查看堆棧中最新保存的 stash 和當前目錄的差異
$ git stash slow
# [11]. 重置當前 HEAD 為指定 commit[hard: 強行合并 - 重置 stage 區(qū)和工作目錄; soft: 軟合并 - 保留工作目錄, 并把重置 HEAD 所帶來的新的差異放進暫存區(qū); mixed: 混合合并 - 保留工作目錄, 并清空暫存區(qū)]
$ git reset [--hard|soft|mixed|merge|keep] [commit|HEAD]
Git 三大分區(qū)
參考博文
[1]. 深入淺出 Git 教程
[2]. 常用 Git 命令清單
[3]. 圖解 Git
source:https://morning-pro.github.io/archives/1cad84b0.html
喜歡,在看
