[hermit auto="0" loop="1" unexpand="1" fullheight="0"]collect#:141663219[/hermit]

Linux就該這么學》最新正式版已出版上市,同學們可在線上京東網當當網淘寶網亞馬遜等電商平臺購買。

*亦可就近在新華書店購買*

章節概述:

本章節內完整的介紹了Git服務程序的來由,詳細介紹分布式版本控制系統原理,闡明對比CVS、Subversion等傳統版本控制的優勢。

帶領讀者在Git服務程序中提交數據、移除數據、移動數據、查詢歷史記錄、還原數據及管理標簽等實驗,滿足日常工作的需求。

同時還為包括了分支結構的創建與合并,遇到分支內容沖突的解決辦法,動手部署Git服務器及使用Github托管服務等超實用內容。

 

21.1 分布式版本控制系統

我想大家還記得前面章節中談到過Linus torvalds在1991年時發布了Linux操作系統吧,從那以后Linux系統便不斷發展壯大,因為Linux系統開源的特性,所以一直接受著來自全球Linux技術愛好者的貢獻,志愿者們通過郵件向Linus發送著自己編寫的源代碼文件,然后由Linus本人通過手工的方式將代碼合并,但這樣不僅沒有效率,而且真的是太痛苦了。
一直到2002年,Linux系統經過十余年的不斷發展,代碼庫已經龐大到無法再通過手工的方式管理了,但是Linus真的很不喜歡類似于CVS或者Subversion的一些版本控制系統,于是商業公司BitMover決定將其公司的BitKeeper分布式版本控制系統授權給Linux開發社區來免費使用,當時的BitKeeper可以比較文件內容的不同,還能夠將出錯的文檔還原到歷史某個狀態,Linus終于放下了心里的石頭。

Git分布式版本控制流程圖

分布式版本控制流程圖,原稿

CVS和Subversion屬于傳統的版本控制系統,而分布式版本控制系統最大的特點是不需要每次提交都把文件推送到版本控制服務器,而是采用分布式版本庫的機制,使得每個開發人員都夠從服務器中克隆一份完整的版本庫到自己計算機本地,不必再完全依賴于版本控制服務器,使得源代碼的發布和合并更加方便,并且因為數據都在自己本地,不僅效率提高了,而且即便我們離開了網絡依然可以執行提交文件、查看歷史版本記錄、創建分支等等操作,真的是開發者的福音啊。

就這樣平靜的度過了三年時間,但是Linux社區聚集著太多的黑客人物,2005年時,那位曾經開發Samba服務程序的Andrew因為試圖破解BitKeeper軟件協議而激怒了BitMover公司,當即決定不再向Linux社區提供免費的軟件授權了,此時的Linus其實也早已有自己編寫分布式版本控制系統的打算了,于是便用C語言創建了Git分布式版本控制系統,并上傳了Linux系統的源代碼。
gitGit不僅是一款開源的分布式版本控制系統,而且有其獨特的功能特性,例如大多數的分布式版本控制系統只會記錄每次文件的變化,說白了就是只會關心文件的內容變化差異,而Git則是關注于文件數據整體的變化,直接會將文件提交時的數據保存成快照,而非僅記錄差異內容,并且使用SHA-1加密算法保證數據的完整性。

Git為了提高效率,對于沒有被修改的文件,則不會重復存儲,而是創建一個鏈接指向之前存儲過的文件。

Git提交流程圖

Git提交流程圖,原稿

其實從發明計算機至今,編寫文檔工作早已融入到每個人的生活之中,但為了完成一篇好文章,一定免不了反復的修改,許多人習慣用復制整個文件的方式來保存不同的版本,或許還會改名加上備份時間以示區別,這樣做的好處就是簡單,但人們的思維如此活躍,一不小心就變成了這個樣子:
文檔編寫的問題
無意中就創建出了這么多亂七八糟的文檔,但是那個才是我想要的版本呢?而且又擔心要刪除的文檔中可能保留有某個不錯的想法,刪除后就不能找回了。更要命的是,有些章節還需要團隊一起編寫,于是需要把文件傳輸給他們,等到編寫后再傳回來,最后由我逐條對照差別后將新的內容添加進去,這樣真的是太麻煩了,我們更希望看到這樣的記錄吧:

版本 用戶 說明 日期
1 Ronny 創建Git章節文檔 10/12 13:48
2 Dave 新增Git命令介紹 10/15 12:19
3 Aaron 新增Github使用方法 10/20 8:32
4 Kim 改正文章中的錯別字 10/30 15:17

21.2 使用Git服務程序

在正式使用前,我們還需要弄清楚Git的三種重要模式,分別是已提交已修改已暫存

已提交(committed):表示數據文件已經順利提交到Git數據庫中。

已修改(modified):表示數據文件已經被修改,但未被保存到Git數據庫中。

已暫存(staged):表示數據文件已經被修改,并會在下次提交時提交到Git數據庫中。

提交前的數據文件可能會被隨意修改或丟失,但只要把文件快照順利提交到Git數據庫中,那就可以完全放心了,流程為:

1.在工作目錄中修改數據文件。

2.將文件的快照放入暫存區域。

3.將暫存區域的文件快照提交到Git倉庫中。

Git的三種工作狀態

Git的工作流程圖,原稿

執行yum命令來安裝Git服務程序:

[[email protected] ~]# yum install -y git
Loaded plugins: langpacks, product-id, subscription-manager
………………省略部分安裝過程………………
Installing:
 git                    x86_64       1.8.3.1-4.el7            rhel7       4.3 M
Installing for dependencies:
 perl-Error             noarch       1:0.17020-2.el7          rhel7        32 k
 perl-Git               noarch       1.8.3.1-4.el7            rhel7        52 k
 perl-TermReadKey       x86_64       2.30-20.el7              rhel7        31 k
………………省略部分安裝過程………………
Complete!

首次安裝Git服務程序后需要設置下用戶名稱、郵件信息和編輯器,這些信息會隨著文件每次都提交到Git數據庫中,用于記錄提交者的信息,而Git服務程序的配置文檔通常會有三份,針對當前用戶和指定倉庫的配置文件優先級最高

配置文件 作用
/etc/gitconfig 保存著系統中每個用戶及倉庫通用配置信息。
~/.gitconfig
~/.config/git/config
針對于當前用戶的配置信息。
工作目錄/.git/config 針對于當前倉庫數據的配置信息。


第一個要配置的是你個人的用戶名稱和電子郵件地址,這兩條配置很重要,每次 Git 提交時都會引用這兩條信息,記錄是誰提交了文件,并且會隨更新內容一起被永久納入歷史記錄:

[[email protected] ~]# git config --global user.name "Liu Chuan"
[[email protected] ~]# git config --global user.email "[email protected]"

設置vim為默認的文本編輯器:

[[email protected] ~]# git config --global core.editor vim

嗯,此時查看下剛剛配置的Git工作環境信息吧:

[[email protected] ~]# git config --list
user.name=Liu Chuan
[email protected]
core.editor=vim
21.2.1 提交數據

我們可以簡單的把工作目錄理解成是一個被Git服務程序管理的目錄,Git會時刻的追蹤目錄內文件的改動,另外在安裝好了Git服務程序后,默認就會創建好了一個叫做master的分支,我們直接可以提交數據到主線了。
創建本地的工作目錄:

[[email protected] ~]# mkdir linuxprobe
[[email protected] ~]# cd linuxprobe/

將該目錄初始化轉成Git的工作目錄:

[[email protected] linuxprobe]# git init
Initialized empty Git repository in /root/linuxprobe/.git/

Git只能追蹤類似于txt文件、網頁、程序源碼等文本文件的內容變化,而不能判斷圖片、視頻、可執行命令等這些二進制文件的內容變化,所以先來嘗試往里面寫入一個新文件吧。

[[email protected] linuxprobe]# echo "Initialization Git repository" > readme.txt

將該文件添加到暫存區:

[[email protected] linuxprobe]# git add readme.txt

將文件上傳到Git暫存區
添加到暫存區后再次修改文件的內容:

[[email protected] linuxprobe]# echo "Something not important" >> readme.txt

將暫存區的文件提交到Git版本倉庫,命令格式為“git commit -m "提交說明”

[[email protected] linuxprobe]# git commit -m "add the readme file"
[master (root-commit) 0b7e029] add the readme file
 1 file changed, 1 insertion(+)
 create mode 100644 readme.txt

將文件提交到Git版本倉庫

查看當前工作目錄的狀態(咦,為什么文件還是提示被修改了?):

[[email protected] linuxprobe]# git status
# On branch master
# Changes not staged for commit:
#   (use "git add ..." to update what will be committed)
#   (use "git checkout -- ..." to discard changes in working directory)
#
#	modified:   readme.txt
#
no changes added to commit (use "git add" and/or "git commit -a")

因為提交操作只是將文件在暫存區中的快照版本提交到Git版本數據庫,所以當你將文件添加到暫存區后,如果又對文件做了修改,請一定要再將文件添加到暫存區后提交到Git版本數據庫:

第一次修改 -> git add -> 第二次修改 -> git add -> git commit

查看當前文件內容與Git版本數據庫中的差別:

[[email protected] linuxprobe]# git diff readme.txt
diff --git a/readme.txt b/readme.txt
index cb06697..33d16d0 100644
--- a/readme.txt
+++ b/readme.txt
@@ -1 +1,2 @@
 Initialization Git repository
+Something not important

那么現在把文件提交到Git版本數據庫吧:

[[email protected] linuxprobe]# git add readme.txt 
[[email protected] linuxprobe]# git commit -m "added a line of words"
[master f7814dc] added a line of words
 1 file changed, 1 insertion(+)

再來查看下當前Git版本倉庫的狀態:

[[email protected] linuxprobe]# git status
# On branch master
nothing to commit, working directory clean

有些時候工作目錄內的文件會比較多,懶的把文件一個個提交到暫存區,可以先設置下要忽略上傳的文件(寫入到"工作目錄/.gitignore"文件中),然后使用"git add ."命令來將當前工作目錄內的所有文件都一起添加到暫存區域。

//忽略所有以.a為后綴的文件。
*.a
//但是lib.a這個文件除外,依然會被提交。
!lib.a
//忽略build目錄內的所有文件。
build/
//忽略build目錄內以txt為后綴的文件。
build/*.txt
//指定忽略名字為git.c的文件。
git.c

先在工作目錄中創建一個名字為git.c的文件:

[[email protected] linuxprobe]# touch git.c

然后創建忽略文件列表:

[[email protected] linuxprobe]# vim .gitignore
git.c

添加將當前工作目錄中的所有文件快照上傳到暫存區:

[[email protected] linuxprobe]# git add .
[[email protected] linuxprobe]# git commit -m "add the .gitignore file"
[master c2cce40] add the .gitignore file
 1 file changed, 1 insertion(+)
 create mode 100644 .gitignore

經過剛剛的實驗,大家一定發現“添加到暫存區”真是個很麻煩的步驟,雖然使用暫存區的方式可以讓提交文件更加的準確,但有時卻略顯繁瑣,如果對要提交的文件完全有把握,我們完全可以追加-a參數,這樣Git會將以前所有追蹤過的文件添加到暫存區后自動的提交,從而跳過了上傳暫存區的步驟,再來修改下文件:

[[email protected] linuxprobe]# echo "Modified again" >> readme.txt

文件被直接提交到Git數據庫:

[[email protected] linuxprobe]# git commit -a -m "Modified again"
[master f587f3d] Modified again
 1 file changed, 1 insertion(+)
[[email protected] linuxprobe]# git status
# On branch master
nothing to commit, working directory clean

比如想把git.c也提交上去,便可以這樣強制添加文件:

[[email protected] linuxprobe]# git add -f git.c

然后重新提交一次(即修改上次的提交操作):

[[email protected] linuxprobe]# git commit --amend
Modified again
# Please enter the commit message for your changes. Lines starting
# with '#' will be ignored, and an empty message aborts the commit.
# On branch master
# Changes to be committed:
#   (use "git reset HEAD^1 ..." to unstage)
#
#       new file:   git.c
#       modified:   readme.txt
#
//我們簡單瀏覽下提交描述,然后輸入:wq!保存并退出。
[master c6f4adf] Modified again
 2 files changed, 1 insertion(+)
 create mode 100644 git.c

出現問題?大膽提問!

因讀者們硬件不同或操作錯誤都可能導致實驗配置出錯,請耐心再仔細看看操作步驟吧,不要氣餒~

Linux技術交流請加A群:560843(滿),B群:340829(推薦),點此查看全國群

*本群特色:通過口令驗證確保每一個群員都是《Linux就該這么學》的讀者,答疑更有針對性,不定期免費領取定制禮品。

21.2.2 移除數據

有些時候會想把已經添加到暫存區的文件移除,但仍然希望文件在工作目錄中不丟失,換句話說,就是把文件從追蹤清單中刪除。
先添加一個新文件,并上傳到暫存區:

[[email protected] linuxprobe]# touch database
[[email protected] linuxprobe]# git add database

查看當前的Git狀態:

[[email protected] linuxprobe]# git status
# On branch master
# Changes to be committed:
#   (use "git reset HEAD ..." to unstage)
#
#	new file:   database
#

將該文件從Git暫存區域的追蹤列表中移除(并不會刪除當前工作目錄內的數據文件):

[[email protected] linuxprobe]# git rm --cached database
rm 'database'
[[email protected] linuxprobe]# ls
database  git.c  readme.txt

此時文件已經是未追蹤狀態了:

[[email protected] linuxprobe]# git status
# On branch master
# Untracked files:
#   (use "git add ..." to include in what will be committed)
#
#	database
nothing added to commit but untracked files present (use "git add" to track)

而如果我們想將文件數據從Git暫存區和工作目錄中一起刪除,可以這樣操作:
再將database文件提交到Git暫存區:

[[email protected] linuxprobe]# git add .

使用git rm命令可以直接刪除暫存區內的追蹤信息及工作目錄內的數據文件:
但如果在刪除之前數據文件已經被放入到暫存區域的話,Git會擔心你勿刪未提交的文件而提示報錯信息,此時可追加強制刪除-f參數。

[[email protected] linuxprobe]# git rm -f database 
rm 'database'
[[email protected] linuxprobe]# ls
git.c  readme.txt

查看當前Git的狀態:

[[email protected] linuxprobe]# git status
# On branch master
nothing to commit, working directory clean
21.2.3 移動數據

Git不像其他版本控制系統那樣跟蹤文件的移動操作,如果要修改文件名稱,則需要使用git mv命令

[[email protected] linuxprobe]# git mv readme.txt introduction.txt

發現下次提交時會有一個改名操作:

[[email protected] linuxprobe]# git status
# On branch master
# Changes to be committed:
#   (use "git reset HEAD ..." to unstage)
#
#	renamed:    readme.txt -> introduction.txt
#

提交文件到Git版本倉庫:

[[email protected] linuxprobe]# git commit -m "changed name"
[master c2674b7] changed name
 1 file changed, 0 insertions(+), 0 deletions(-)
 rename readme.txt => introduction.txt (100%)

其實我們還可以這樣來修改文件名,首先將工作目錄下的數據文件改名:

[[email protected] linuxprobe]# mv introduction.txt readme.txt

然后刪除Git版本倉庫內的文件快照:

[[email protected] linuxprobe]# git rm introduction.txt
rm 'introduction.txt'

最后再將新的文件添加進入:

[[email protected] linuxprobe]# git add readme.txt 
[[email protected] linuxprobe]# git commit -m "changed the file name again"
[master d97dd0b] changed the file name again
 1 file changed, 0 insertions(+), 0 deletions(-)
 rename introduction.txt => readme.txt (100%)
21.2.4 歷史記錄

在完成上面的實驗后,我們已經不知不覺有了很多次的提交操作了,可以用git log命令來查看提交歷史記錄:

[[email protected] linuxprobe]# git log
commit d97dd0beafa082933e044f9213809564ce2bc617
Author: Liu Chuan <[email protected]>
Date: Mon Jan 18 01:30:52 2016 -0500

 changed the file name again

commit c2674b7194f1f794dbb980e5d68137dec658672b
Author: Liu Chuan <[email protected]>
Date: Mon Jan 18 01:14:56 2016 -0500

 changed name

commit c6f4adf87e2d16e39335b621e8a14f58217fcbc4
Author: Liu Chuan <[email protected]>
Date: Mon Jan 18 01:12:06 2016 -0500

 Modified again

commit c2cce402ca019f4cbbf5f0bb5eb41a7e2fe277c3
Author: Liu Chuan <[email protected]>
Date: Mon Jan 18 01:11:21 2016 -0500

 add the .gitignore file

commit f7814dc9297799fa000159b0e291142024f0a11a
Author: Liu Chuan <[email protected]>
Date: Mon Jan 18 01:10:25 2016 -0500

 added a line of words

commit 0b7e02927d36e82eaa2ef7353d6910a30bf16119
Author: Liu Chuan <[email protected]>
Date: Mon Jan 18 01:08:38 2016 -0500

 add the readme file 

像上面直接執行git log命令后會看到所有的更新記錄(按時間排序,最近更新的會在上面),歷史記錄會除了保存文件快照,還會詳細的記錄著文件SHA-1校驗和,作者的姓名,郵箱及更新時間,如果只想看最近幾條記錄,可以直接這樣操作:

[[email protected] linuxprobe]# git log -2
commit d97dd0beafa082933e044f9213809564ce2bc617
Author: Liu Chuan <[email protected]>
Date: Mon Jan 18 01:30:52 2016 -0500

 changed the file name again

commit c2674b7194f1f794dbb980e5d68137dec658672b
Author: Liu Chuan <[email protected]>
Date: Mon Jan 18 01:14:56 2016 -0500

 changed name

我也常用-p參數來展開顯示每次提交的內容差異,例如僅查看最近一次的差異:

[[email protected] linuxprobe]# git log -p -1
commit d97dd0beafa082933e044f9213809564ce2bc617
Author: Liu Chuan <[email protected]>
Date: Mon Jan 18 01:30:52 2016 -0500

 changed the file name again

diff --git a/introduction.txt b/introduction.txt
deleted file mode 100644
index f3c8232..0000000
--- a/introduction.txt
+++ /dev/null
@@ -1,3 +0,0 @@
-Initialization Git repository
-Something not important
-Modified again
diff --git a/readme.txt b/readme.txt
new file mode 100644
index 0000000..f3c8232
--- /dev/null
+++ b/readme.txt
@@ -0,0 +1,3 @@
+Initialization Git repository
+Something not important
+Modified again
(END)

我們還可以使用--stat參數來簡要的顯示數據增改行數,這樣就能夠看到提交中修改過的內容、對文件添加或移除的行數,并在最后列出所有增減行的概要信息(僅看最近兩次的提交歷史):

[[email protected] linuxprobe]# git log --stat -2 
commit d97dd0beafa082933e044f9213809564ce2bc617
Author: Liu Chuan <[email protected]>
Date: Mon Jan 18 01:30:52 2016 -0500

 changed the file name again

 introduction.txt | 3 ---
 readme.txt | 3 +++
 2 files changed, 3 insertions(+), 3 deletions(-)

commit c2674b7194f1f794dbb980e5d68137dec658672b
Author: Liu Chuan <[email protected]>
Date: Mon Jan 18 01:14:56 2016 -0500

 changed name

 introduction.txt | 3 +++
 readme.txt | 3 ---
 2 files changed, 3 insertions(+), 3 deletions(-)

還有一個超級常用的--pretty參數,它可以根據不同的格式為我們展示提交的歷史信息,比如每行顯示一條提交記錄:

[[email protected] linuxprobe]# git log --pretty=oneline
d97dd0beafa082933e044f9213809564ce2bc617 changed the file name again
c2674b7194f1f794dbb980e5d68137dec658672b changed name
c6f4adf87e2d16e39335b621e8a14f58217fcbc4 Modified again
c2cce402ca019f4cbbf5f0bb5eb41a7e2fe277c3 add the .gitignore file
f7814dc9297799fa000159b0e291142024f0a11a added a line of words
0b7e02927d36e82eaa2ef7353d6910a30bf16119 add the readme file

以更詳細的模式輸出最近兩次的歷史記錄:

[[email protected] linuxprobe]# git log --pretty=fuller -2
commit d97dd0beafa082933e044f9213809564ce2bc617
Author: Liu Chuan <[email protected]>
AuthorDate: Mon Jan 18 01:30:52 2016 -0500
Commit: Liu Chuan <[email protected]>
CommitDate: Mon Jan 18 01:30:52 2016 -0500

 changed the file name again

commit c2674b7194f1f794dbb980e5d68137dec658672b
Author: Liu Chuan <[email protected]>
AuthorDate: Mon Jan 18 01:14:56 2016 -0500
Commit: Liu Chuan <[email protected]>
CommitDate: Mon Jan 18 01:14:56 2016 -0500

 changed name

還可以使用format參數來指定具體的輸出格式,這樣非常便于后期編程的提取分析哦,常用的格式有:

%s 提交說明。
%cd 提交日期。
%an 作者的名字。
%cn 提交者的姓名。
%ce 提交者的電子郵件。
%H 提交對象的完整SHA-1哈希字串。
%h 提交對象的簡短SHA-1哈希字串。
%T 樹對象的完整SHA-1哈希字串。
%t 樹對象的簡短SHA-1哈希字串。
%P 父對象的完整SHA-1哈希字串。
%p 父對象的簡短SHA-1哈希字串。
%ad 作者的修訂時間。


另外作者和提交者是不同的,作者才是對文件作出實際修改的人,而提交者只是最后將此文件提交到Git版本數據庫的人。
查看當前所有提交記錄的簡短SHA-1哈希字串提交者的姓名

[[email protected] linuxprobe]# git log --pretty=format:"%h %cn"d97dd0b Liu Chuan
c2674b7 Liu Chuan
c6f4adf Liu Chuan
c2cce40 Liu Chuan
f7814dc Liu Chuan
0b7e029 Liu Chuan
21.2.5 還原數據

還原數據是每一個版本控制的基本功能,先來隨意修改下文件吧:

[[email protected] linuxprobe]# echo "Git is a version control system" >> readme.txt

然后將文件提交到Git版本數據庫:

[[email protected] linuxprobe]# git add readme.txt
[[email protected] linuxprobe]# git commit -m "Introduction software"
[master a41441f] Introduction software
 1 file changed, 1 insertion(+)

此時覺得寫的不妥,想要還原某一次提交的文件快照:

[[email protected] linuxprobe]# git log --pretty=oneline
a41441f985549a0b91f22f535ebc611c530b5b27 Introduction software
d97dd0beafa082933e044f9213809564ce2bc617 changed the file name again
c2674b7194f1f794dbb980e5d68137dec658672b changed name
c6f4adf87e2d16e39335b621e8a14f58217fcbc4 Modified again
c2cce402ca019f4cbbf5f0bb5eb41a7e2fe277c3 add the .gitignore file
f7814dc9297799fa000159b0e291142024f0a11a added a line of words
0b7e02927d36e82eaa2ef7353d6910a30bf16119 add the readme file

Git服務程序中有一個叫做HEAD的版本指針,當用戶申請還原數據時,其實就是將HEAD指針指向到某個特定的提交版本而已,但是因為Git是分布式版本控制系統,所以不可能像SVN那樣使用1、2、3、4來定義每個歷史的提交版本號,為了避免歷史記錄沖突,故使用了SHA-1計算出十六進制的哈希字串來區分每個提交版本,像剛剛最上面最新的提交版本號就是5cee15b32d78259985bac4e0cbb0cdad72ab68ad,另外默認的HEAD版本指針會指向到最近的一次提交版本記錄哦,而上一個提交版本會叫HEAD^,上上一個版本則會叫做HEAD^^,當然一般會用HEAD~5來表示往上數第五個提交版本哦~。

好啦,既然我們已經鎖定了要還原的歷史提交版本,就可以使用git reset命令來還原數據了:

[[email protected] linuxprobe]# git reset --hard HEAD^
HEAD is now at d97dd0b changed the file name again

再來看下文件的內容吧(怎么樣,內容果然已經還原了吧~):

[[email protected] linuxprobe]# cat readme.txt
Initialization Git repository
Something not important
Modified again

剛剛的操作實際上就是改變了一下HEAD版本指針的位置,說白了就是你將HEAD指針放在那里,那么你的當前工作版本就會定位在那里,要想把內容再還原到最新提交的版本,先查看下提交版本號吧:

[[email protected] linuxprobe]# git log --pretty=oneline
d97dd0beafa082933e044f9213809564ce2bc617 changed the file name again
c2674b7194f1f794dbb980e5d68137dec658672b changed name
c6f4adf87e2d16e39335b621e8a14f58217fcbc4 Modified again
c2cce402ca019f4cbbf5f0bb5eb41a7e2fe277c3 add the .gitignore file
f7814dc9297799fa000159b0e291142024f0a11a added a line of words
0b7e02927d36e82eaa2ef7353d6910a30bf16119 add the readme file

怎么搞得?竟然沒有了Introduction software這個提交版本記錄??
原因很簡單,因為我們當前的工作版本是歷史的一個提交點,這個歷史提交點還沒有發生過Introduction software更新記錄,所以當然就看不到了,要是想“還原到未來”的歷史更新點,可以用git reflog命令來查看所有的歷史記錄:

[[email protected] linuxprobe]# git reflog
d97dd0b [email protected]{0}: reset: moving to HEAD^
a41441f [email protected]{1}: commit: Introduction software
d97dd0b [email protected]{2}: commit: changed the file name again
c2674b7 [email protected]{3}: commit: changed name
c6f4adf [email protected]{4}: commit (amend): Modified again
f587f3d [email protected]{5}: commit: Modified again
c2cce40 [email protected]{6}: commit: add the .gitignore file
f7814dc [email protected]{7}: commit: added a line of words
0b7e029 [email protected]{8}: commit (initial): add the readme file

找到歷史還原點的SHA-1值后,就可以還原文件了,另外SHA-1值沒有必要寫全,Git會自動去匹配:x

[[email protected] linuxprobe]# git reset --hard 5cee15b 
HEAD is now at 5cee15b Introduction software
[[email protected] linuxprobe]# cat readme.txt 
Initialization Git repository
Something not important
Modified again
Git is a version control system

如是只是想把某個文件內容還原,就不必這么麻煩,直接用git checkout命令就可以的,先隨便寫入一段話:

[[email protected] linuxprobe]# echo "Some mistakes words" >> readme.txt
[[email protected] linuxprobe]# cat readme.txt
Initialization Git repository
Something not important
Modified again
Git is a version control system
Some mistakes words

哎呀,我們突然發現不應該寫一句話的,可以手工刪除(當內容比較多的時候會很麻煩),還可以將文件內容從暫存區中恢復:

[[email protected] linuxprobe]# git checkout -- readme.txt
[[email protected] linuxprobe]# cat readme.txt 
Initialization Git repository
Something not important
Modified again
Git is a version control system

checkou規則是如果暫存區中有該文件,則直接從暫存區恢復,如果暫存區沒有該文件,則將還原成最近一次文件提交時的快照。
出現問題?大膽提問!

因讀者們硬件不同或操作錯誤都可能導致實驗配置出錯,請耐心再仔細看看操作步驟吧,不要氣餒~

Linux技術交流請加A群:560843(滿),B群:340829(推薦),點此查看全國群

*本群特色:通過口令驗證確保每一個群員都是《Linux就該這么學》的讀者,答疑更有針對性,不定期免費領取定制禮品。

21.2.6 管理標簽

當版本倉庫內的數據有個大的改善或者功能更新,我們經常會打一個類似于軟件版本號的標簽,這樣通過標簽就可以將版本庫中的某個歷史版本給記錄下來,方便我們隨時將特定歷史時期的數據取出來用,另外打標簽其實只是向某個歷史版本做了一個指針,所以一般都是瞬間完成的,感覺很方便吧。
在Git中打標簽非常簡單,給最近一次提交的記錄打個標簽:

[[email protected] linuxprobe]# git tag v1.0

查看所有的已有標簽:

[[email protected] linuxprobe]# git tag
v1.0

查看此標簽的詳細信息:

[[email protected] linuxprobe]# git show v1.0
commit a41441f985549a0b91f22f535ebc611c530b5b27
Author: Liu Chuan <[email protected]>
Date: Mon Jan 18 01:37:45 2016 -0500

 Introduction software

diff --git a/readme.txt b/readme.txt
index f3c8232..955efba 100644
--- a/readme.txt
+++ b/readme.txt
@@ -1,3 +1,4 @@
 Initialization Git repository
 Something not important
 Modified again
+Git is a version control system

還可以創建帶有說明的標簽,用-a指定標簽名,-m指定說明文字:

[[email protected] linuxprobe]# git tag v1.1 -m "version 1.1 released" d316fb

我們為同一個提交版本設置了兩次標簽,來把之前的標簽刪除吧:

[[email protected] linuxprobe]# git tag -d v1.0
Deleted tag 'v1.0' (was d316fb2)
[[email protected] linuxprobe]# git tag
v1.1
21.3 管理分支結構

分支即是平行空間,假設你在為某個手機系統研發拍照功能,代碼已經完成了80%,但如果將這不完整的代碼直接提交到git倉庫中,又有可能影響到其他人的工作,此時我們便可以在該軟件的項目之上創建一個名叫“拍照功能”的分支,這種分支只會屬于你自己,而其他人看不到,等代碼編寫完成后再與原來的項目主分支合并下即可,這樣即能保證代碼不丟失,又不影響其他人的工作。
Git團隊合作流程圖
一般在實際的項目開發中,我們要盡量保證master分支是非常穩定的,僅用于發布新版本,平時不要隨便直接修改里面的數據文件,而工作的時候則可以新建不同的工作分支,等到工作完成后在合并到master分支上面,所以團隊的合作分支看起來會像上面圖那樣。

Git分支示意圖

另外如前面所講,git會將每次的提交操作串成一個時間線,而在前面的實驗中實際都是在對master分支進行操作,Git會在創建分支后默認創建一個叫做Photograph的指針,所以我們還需要再將HEAD指針切換到“Photograph”的位置才正式使用上了新分支哦,這么說起來可能比較抽象,趕緊學習下面的實驗吧。

21.3.1 創建分支

首先創建分支:

[[email protected] linuxprobe]# git branch linuxprobe

切換至分支:

[[email protected] linuxprobe]# git checkout linuxprobe
Switched to branch 'linuxprobe'

查看當前分支的情況(會列出該倉庫中所有的分支,當前的分支前有*號):

[[email protected] linuxprobe]# git branch
* linuxprobe
 master

我們對文件再追加一行字符串吧:

[[email protected] linuxprobe]# echo "Creating a new branch is quick." >> readme.txt

將文件提交到git倉庫:

[[email protected] linuxprobe]# git add readme.txt
[[email protected] linuxprobe]# git commit -m "new branch"
[linuxprobe 2457d98] new branch
 1 file changed, 1 insertion(+)

為了讓大家更好理解分支的作用,我們在提交文件后再切換回master分支:

[[email protected] linuxprobe]# git checkout master
Switched to branch 'master'

然后查看下文件內容,發現并沒有新追加的字符串哦:

[[email protected] linuxprobe]# cat readme.txt
Initialization Git repository
Something not important
Modified again
Git is a version control system
21.3.2 合并分支

現在,我們想把linuxprobe的工作成果合并到master分支上了,則可以使用"git merge"命令來將指定的的分支與當前分支合并:
合并分支

[[email protected] linuxprobe]# git merge linuxprobe
Updating a41441f..2457d98
Fast-forward
 readme.txt | 1 +
 1 file changed, 1 insertion(+)

查看合并后的readme.txt文件:

[[email protected] linuxprobe]# cat readme.txt
Initialization Git repository
Something not important
Modified again
Git is a version control system
Creating a new branch is quick.

確認合并完成后,就可以放心地刪除linuxprobe分支了:

[[email protected] linuxprobe]# git branch -d?linuxprobe
Deleted branch linuxprobe (was 2457d98).

刪除后,查看branch,就只剩下master分支了:

[[email protected] linuxprobe]# git branch
* master
21.3.3 內容沖突

但是Git并不能每次都為我們自動的合并分支,當遇到了內容沖突比較復雜的情況,則必須手工將差異內容處理掉,比如這樣的情況:
沖突分支合并

創建分支并切換到該分支命令:git checkout -b 分支名稱

創建一個新分支并切換到該分支命令:

[[email protected] linuxprobe]# git checkout -b linuxprobe
Switched to a new branch 'linuxprobe'

修改readme.txt文件內容:

[[email protected] linuxprobe]# vim readme.txt
Initialization Git repository
Something not important
Modified again
Git is a version control system
Creating a new branch is quick.
Creating a new branch is quick & simple.

在linuxprobe分支上提交:

[[email protected] linuxprobe]# git add readme.txt
[[email protected] linuxprobe]# git commit -m "Creating a new branch is quick & simple.?" 
[linuxprobe 882a128] Creating a new branch is quick & simple.
 1 file changed, 1 insertion(+)

切換到master分支:

[[email protected] linuxprobe]# git checkout master
Switched to branch 'master'

在在master分支上修改readme.txt文件同一行的內容:

[[email protected] linuxprobe]# vim readme.txt
Initialization Git repository
Something not important
Modified again
Git is a version control system
Creating a new branch is quick.
Creating a new branch is quick AND simple.

提交至Git版本倉庫:

[[email protected] linuxprobe]# git add readme.txt
Creating a new branch is quick AND simple.
[[email protected] linuxprobe]# git commit -m "Creating a new branch is quick AND simple.?"
[master c21741c] Creating a new branch is quick AND simple.
 1 file changed, 1 insertion(+)

那么此時,我們在master與linuxprobe分支上都分別對中readme.txt文件進行了修改并提交了,那這種情況下Git就沒法再為我們自動的快速合并了,它只能告訴我們readme.txt文件的內容有沖突,需要手工處理沖突的內容后才能繼續合并:

[[email protected] linuxprobe]# git merge linuxprobe
Auto-merging readme.txt
CONFLICT (content): Merge conflict in readme.txt
Automatic merge failed; fix conflicts and then commit the result.

沖突的內容為:

[[email protected] linuxprobe]# vim readme.txt
Initialization Git repository
Something not important
Modified again
Git is a version control system
Creating a new branch is quick.
<<<<<<< HEAD
Creating a new branch is quick AND simple.
=======
Creating a new branch is quick & simple.
>>>>>>> linuxprobe

Git用< <<<<<<,=======,>>>>>>>分割開了各個分支沖突的內容,我們需要手工的刪除這些符號,并將內容修改為:

Initialization Git repository
Something not important
Modified again
Git is a version control system
Creating a new branch is quick.
Creating a new branch is quick and simple.

解決沖突內容后則可順利的提交:

[[email protected] linuxprobe]# git add readme.txt 
[[email protected] linuxprobe]# git commit -m "conflict fixed" 
[master 90c607a] conflict fixed

查看Git歷史提交記錄(可以看到分支的變化):

[[email protected] linuxprobe]# git log --graph --pretty=oneline --abbrev-commit
* 90c607a conflict fixed
|\ 
| * 882a128 Creating a new branch is quick & simple.
* | c21741c Creating a new branch is quick AND simple.
|/ 
* 2457d98 new branch
* a41441f Introduction software
* d97dd0b changed the file name again
* c2674b7 changed name
* c6f4adf Modified again
* c2cce40 add the .gitignore file
* f7814dc added a line of words
* 0b7e029 add the readme file

最后,放心的刪除linuxprobe分支吧:

[[email protected] linuxprobe]# git branch -d linuxprobe
Deleted branch linuxprobe (was 882a128).
[[email protected] linuxprobe]# git branch
* master
21.4 部署Git服務器

Git是分布式的版本控制系統,我們只要有了一個原始Git版本倉庫,就可以讓其他主機克隆走這個原始版本倉庫,從而使得一個Git版本倉庫可以被同時分布到不同的主機之上,并且每臺主機的版本庫都是一樣的,沒有主次之分,極大的保證了數據安全性,并使得用戶能夠自主選擇向那個Git服務器推送文件了,其實部署一個git服務器是非常簡單的事情,我們需要用到兩臺主機,分別是:

主機名稱 操作系統 IP地址
Git服務器 紅帽RHEL7操作系統 192.168.10.10
Git客戶端 紅帽RHEL7操作系統 192.168.10.20


首先我們分別在Git服務器和客戶機中安裝Git服務程序(剛剛實驗安裝過就不用安裝了):

[[email protected] ~]# yum install git 
Loaded plugins: langpacks, product-id, subscription-manager
This system is not registered to Red Hat Subscription Management. You can use subscription-manager to register.
Package git-1.8.3.1-4.el7.x86_64 already installed and latest version
Nothing to do

然后創建Git版本倉庫,一般規范的方式要以.git為后綴:

[[email protected] ~]# mkdir linuxprobe.git

修改Git版本倉庫的所有者與所有組:

[[email protected] ~]# chown -Rf git:git linuxprobe.git/

初始化Git版本倉庫:

[[email protected] ~]# cd linuxprobe.git/
[[email protected] linuxprobe.git]# git --bare init
Initialized empty Git repository in /root/linuxprobe.git/

其實此時你的Git服務器就已經部署好了,但用戶還不能向你推送數據,也不能克隆你的Git版本倉庫,因為我們要在服務器上開放至少一種支持Git的協議,比如HTTP/HTTPS/SSH等,現在用的最多的就是HTTPS和SSH,我們切換至Git客戶機來生成SSH密鑰:

[[email protected] ~]# ssh-keygen 
Generating public/private rsa key pair.
Enter file in which to save the key (/root/.ssh/id_rsa): 
Created directory '/root/.ssh'.
Enter passphrase (empty for no passphrase): 
Enter same passphrase again: 
Your identification has been saved in /root/.ssh/id_rsa.
Your public key has been saved in /root/.ssh/id_rsa.pub.
The key fingerprint is:
65:4a:53:0d:4f:ee:49:4f:94:24:82:16:7a:dd:1f:28 [email protected]
The key's randomart image is:
+--[ RSA 2048]----+
|        .o+oo.o. |
|       .oo *.+.  |
|      ..+ E * o  |
|       o = + = . |
|        S   o o  |
|                 |
|                 |
|                 |
|                 |
+-----------------+

將客戶機的公鑰傳遞給Git服務器:

[[email protected] ~]# ssh-copy-id 192.168.10.10
[email protected]'s password: 
Number of key(s) added: 1
Now try logging into the machine, with: "ssh '192.168.10.10'"
and check to make sure that only the key(s) you wanted were added.

此時就已經可以從Git服務器中克隆版本倉庫了(此時目錄內沒有文件是正常的):

[[email protected] ~]# git clone [email protected]:/root/linuxprobe.git
Cloning into 'linuxprobe'...
warning: You appear to have cloned an empty repository.
[[email protected] ~]# cd linuxprobe
[[email protected] linuxprobe]# 

初始化下Git工作環境:

[[email protected] ~]# git config --global user.name "Liu Chuan"
[[email protected] ~]# git config --global user.email "[email protected]"
[[email protected] ~]# git config --global core.editor vim

向Git版本倉庫中提交一個新文件:

[[email protected] linuxprobe]# echo "I successfully cloned the Git repository" > readme.txt
[[email protected] linuxprobe]# git add readme.txt 
[[email protected] linuxprobe]# git status
# On branch master
#
# Initial commit
#
# Changes to be committed:
#   (use "git rm --cached ..." to unstage)
#
#	new file:   readme.txt
#
[[email protected] linuxprobe]# git commit -m "Clone the Git repository"
[master (root-commit) c3961c9] Clone the Git repository
 Committer: root 
1 file changed, 1 insertion(+)
 create mode 100644 readme.txt
[[email protected] linuxprobe]# git status
# On branch master
nothing to commit, working directory clean

但是這次的操作還是只將文件提交到了本地的Git版本倉庫,并沒有推送到遠程Git服務器,所以我們來定義下遠程的Git服務器吧:

[[email protected] linuxprobe]# git remote add server [email protected]:/root/linuxprobe.git

將文件提交到遠程Git服務器吧:

[[email protected] linuxprobe]# git push -u server master
Counting objects: 3, done.
Writing objects: 100% (3/3), 261 bytes | 0 bytes/s, done.
Total 3 (delta 0), reused 0 (delta 0)
To [email protected]:/root/linuxprobe.git
 * [new branch]      master -> master
Branch master set up to track remote branch master from server.

為了驗證真的是推送到了遠程的Git服務,你可以換個目錄再克隆一份版本倉庫(雖然在工作中毫無意義):

[[email protected] linuxprobe]# cd ../Desktop
[[email protected] Desktop]# git clone [email protected]:/root/linuxprobe.git
Cloning into 'linuxprobe'...
remote: Counting objects: 3, done.
remote: Total 3 (delta 0), reused 0 (delta 0)
Receiving objects: 100% (3/3), done.
[[email protected] Desktop]# cd linuxprobe/
[[email protected] linuxprobe]# cat readme.txt 
I successfully cloned the Git repository
21.5 Github托管服務

其實自己部署一臺Git服務器或許很有意思,但想到你要保證這臺主機能夠7*24小時穩定運行,還要保證各種權限及版本庫的安全就覺得很累吧,
github
Github顧名思義是一個Git版本庫的托管服務,是目前全球最大的軟件倉庫,擁有上百萬的開發者用戶,也是軟件開發和尋找資源的最佳途徑,Github不僅可以托管各種Git版本倉庫,還擁有了更美觀的Web界面,您的代碼文件可以被任何人克隆,使得開發者為開源項貢獻代碼變得更加容易,當然也可以付費購買私有庫,這樣高性價比的私有庫真的是幫助到了很多團隊和企業。
大多數用戶都是為了尋找資源而愛上Github的,首先進入網站,點擊注冊(Sign up):
github注冊帳戶
填寫注冊信息:
填寫注冊信息
選擇倉庫類型:
選擇倉庫類型
好棒,我們的GitHub帳號注冊完成了:
Git注冊成功
我們在向Github推送文件時,可以選擇SSH協議模式,在本機生成密鑰文件(上面實驗已經做過,就不需要再生成了):

[[email protected] ~]# ssh-keygen
Generating public/private rsa key pair.
Enter file in which to save the key (/root/.ssh/id_rsa): 
Created directory '/root/.ssh'.
Enter passphrase (empty for no passphrase): 
Enter same passphrase again: 
Your identification has been saved in /root/.ssh/id_rsa.
Your public key has been saved in /root/.ssh/id_rsa.pub.
The key fingerprint is:
88:91:4c:db:85:b6:b4:69:ba:44:4d:b1:89:da:48:78 [email protected]
The key's randomart image is:
+--[ RSA 2048]----+
|    . .o.        |
| . o ==+         |
|. E *=++         |
| o +.o*.         |
|  o.oo. S        |
|    o            |
|   . .           |
|    .            |
|                 |
+-----------------+
[[email protected] ~]# ssh-add

點擊進入Github的帳戶配置頁面:
點擊進入Github的帳戶配置頁面
點擊添加SSH公鑰:
點擊添加SSH公鑰
將本機中的ssh公鑰(.ssh/id_rsa.pub)復制到頁面中,填寫ssh公鑰信息:填寫ssh公鑰信息
查看ssh公鑰信息:
查看ssh公鑰信息
好的,現在我們的準備工作已經妥當,讓我們在Github上創建自己第一個Git版本倉庫吧,點擊創建一個新的版本倉庫:
點擊創建一個新的版本倉庫
填寫版本倉庫的信息:
Git創建倉庫
創建成功后會自動跳轉到該倉庫的頁面:
查看Git版本倉庫信息
復制版本倉庫的克隆地址:
復制版本倉庫的克隆地址
嘗試把版本倉庫克隆到本地(這個版本庫我會一直保留,大家可以動手克隆下試試。):

[[email protected] ~]# git clone  https://github.com/K130/linuxprobe.git
Cloning into 'linuxprobe'...
remote: Counting objects: 3, done.
remote: Total 3 (delta 0), reused 0 (delta 0), pack-reused 0
Unpacking objects: 100% (3/3), done.
[[email protected] ~]# cat linuxprobe/
.git/      README.md  
[[email protected] ~]# cat linuxprobe/README.md 
# linuxprobe
The highest quality Linux materials

將該Github版本倉庫添加到本機的遠程列表中:

[[email protected] linuxprobe]# git remote add linuxprobe [email protected]:K130/linuxprobe.git
[[email protected] linuxprobe]# git remote
linuxprobe
origin

編寫一個新文件:

[[email protected] ~]# cd linuxprobe/
[[email protected] linuxprobe]# echo "Based on the RHEL&Centos system" > features

將該文件提交到本地的Git版本倉庫:

[[email protected] linuxprobe]# git add features 
[[email protected] linuxprobe]# git commit -m "add features"

然后將本地的Git倉庫同步到遠程Git服務器上(第一次請加上參數-u,代表關聯本地與遠程):

[[email protected] linuxprobe]# git push -u linuxprobe  masterCounting objects: 4, done.
Delta compression using up to 4 threads.
Compressing objects: 100% (2/2), done.
Writing objects: 100% (3/3), 303 bytes | 0 bytes/s, done.
Total 3 (delta 0), reused 0 (delta 0)
To [email protected]:K130/linuxprobe.git
   8a5455a..f1bc411  master -> master
Branch master set up to track remote branch master from linuxprobe.

刷新一下Web頁面,果然看到版本倉庫已經同步了:
查看Git版本倉庫
出現問題?大膽提問!

因讀者們硬件不同或操作錯誤都可能導致實驗配置出錯,請耐心再仔細看看操作步驟吧,不要氣餒~

Linux技術交流請加A群:560843(滿),B群:340829(推薦),點此查看全國群

*本群特色:通過口令驗證確保每一個群員都是《Linux就該這么學》的讀者,答疑更有針對性,不定期免費領取定制禮品。

本章節的復習作業(答案就在問題的下一行哦,用鼠標選中即可看到的~)

1:分布式版本控制系統對比傳統版本控制系統有那些優勢?

答案:去中心化設計讓數據更加安全,管理文件數據更加的有效率。

2:將Git工作目錄中的文件提交到暫存區的命令是?

答案:git add?

3:將Git暫存區的文件上傳到版本倉庫的命令是?

答案:git commit

4:Git是一種非常智能的程序,它能夠為我們自動解決任何分支的內容沖突情況?

答案:錯誤,很多時候內容沖突后需要由人來解決合并沖突。

5:為什么我們要將ssh公鑰上傳到Github資料中?

答案:為了讓Github能夠驗證用戶身份,從而才能順利的管理文件數據。

本文原創地址:http://www.52437949.com/chapter-21.html編輯:劉遄,審核員:暫無