Git版本控制器使用總結性梳理

散盡浮華發表於2016-06-08

 

Git為何物?
Git 是什麼?大家肯定會說不就是版本控制器嘛,是的Git是目前世界上最先進的分散式版本控制系統(沒有之一)。
1)那什麼是版本控制器?
舉個簡單的例子,比如我們用Word寫文章,那你一定有這樣的經歷:比如增加一個段落你得複製一份,你刪除一個段落你又得複製一份,防止下次又要修改保留上次你要刪除的段落。最後一個接一個的版本,你複製了很多版本,最後可能你自己都不知道修改了哪些?嘿嘿,然後你只能一個一個的找,太麻煩了,若是有東西幫你管理那應該多好。
2)分散式管理
你寫的文章或書,你肯定會給你朋友或者其他人看,讓他們給你建議並做相應的修改,然後他們用郵件或U盤再發給你,你再合併修改一下,真是麻煩,於是你想,如果有一個軟體,不但能自動幫我記錄每次檔案的改動,還可以讓朋友之間協作編輯,這樣就不用自己管理一堆類似的檔案了,也不需要把檔案傳來傳去。如果想檢視某次改動,只需要在軟體看一眼就可以,豈不是很方便?這個軟體用起來就應該像這個樣子,能記錄每次檔案的改動:

版本 使用者 說明 修改日期
1 user1 增加一行內容 2014/4/10 10:22
2 user2 修改一行內容 2014/4/10 13:12
3 user3 刪除幾個字 2014/4/15 20:42
4 user2 增加某個內容 2014/4/20 16:32

Git 的誕生
簡單說:Linus開發Linux核心,需要版本控制器,於是開發了Git。下面是開發週期:
1)2005/4/3 開發;
2)2005/4/6 釋出;
3)2005/4/7 管理自身;
4)2005/6/16 管理Kernel2.6.12。
大牛是怎麼定義的呢?大家可以體會一下。哈哈^_^…… Git 迅速成為最流行的分散式版本控制系統,尤其是2008年,GitHub網站上線了,它為開源專案免費提供Git儲存,無數開源專案開始遷移至GitHub,包括jQuery,PHP,Ruby等等。至於Git與GitHub的關係,會再下面的文章裡說明。

集中管理 VS 分散式管理
Linus一直痛恨的CVS及SVN都是集中式的版本控制系統,而Git是分散式版本控制系統,集中式和分散式版本控制系統有什麼區別呢? 下面我們來看看兩張圖:
1)集中管理

集中式版本控制系統,版本庫是集中存放在中央伺服器的,而幹活的時候,用的都是自己的電腦,所以要先從中央伺服器取得最新的版本,然後開始幹活,幹完活了,再把自己的活推送給中央伺服器。中央伺服器就好比是一個圖書館,你要改一本書,必須先從圖書館借出來,然後回到家自己改,改完了,再放回圖書館。

缺點:
集中式版本控制系統最大的毛病就是必須聯網才能工作,如果在區域網內還好,頻寬夠大,速度夠快,可如果在網際網路上,遇到網速慢的話,可能提交一個10M~20M的檔案就需要10分鐘甚至更多時間,這還不得把人給急死啊。

2)分散式管理

那分散式版本控制系統與集中式版本控制系統有何不同呢?首先,分散式版本控制系統沒有“中央伺服器”,每個人的電腦上都是一個完整的版本庫,這樣,你工作的時候,就不需要聯網了,因為版本庫就在你自己的電腦上。既然每個人電腦上都有一個完整的版本庫,那多個人如何協作呢?比方說你在自己電腦上改了檔案fiel,你的同事也在他的電腦上改了檔案file,這時,你們倆之間只需把各自的修改推送給對方,就可以互相看到對方的修改了。

和集中式版本控制系統相比,分散式版本控制系統的安全性要高很多,因為每個人電腦裡都有完整的版本庫,某一個人的電腦壞掉了不要緊,隨便從其他人那裡複製一個就可以了。而集中式版本控制系統的中央伺服器要是出了問題,所有人都沒法幹活了。
在實際使用分散式版本控制系統的時候,其實很少在兩人之間的電腦上推送版本庫的修改,因為可能你們倆不在一個區域網內,兩臺電腦互相訪問不了,也可能今天你的同事病了,他的電腦壓根沒有開機。因此,分散式版本控制系統通常也有一臺充當“中央伺服器”的電腦,但這個伺服器的作用僅僅是用來方便“交換”大家的修改,沒有它大家也一樣幹活,只是交換修改不方便而已。如上圖!

Git特點

1)分散式
2)儲存快照而非差異
3)本地有完全的版本庫,幾乎所有操作都在本地
4)有內在的一致性,SHA1
5)優秀的分支管理
6)支援各種協同模式
7)開源,有一些第三方軟體可整合使用,幾乎所有操作都是

與CVS/SVN,Git 的優勢
1)支援離線開發,離線Repository(倉庫)
2)強大的分支功能,適合多個獨立開發者協作
3)速度塊

為什麼選擇Git來控制版本,理由如下:

1)快速 
如果你每移動一下滑鼠都要等待五秒,是不是很受不了?版本控制也是一樣的,每一個命令多那麼幾秒鐘,一天下來也會浪費你不少時間。Git的操作非常快速,你可以把時間用在別的更有意義的地方。 
2)離線工作 
在沒有網路的情況下如何工作?如果你用SVN或者CVS的話就很麻煩。而Git可以讓你在本地做所有操作,提交程式碼,檢視歷史,合併,建立分支等等。 
3)回退 
人難免犯錯。我很喜歡Git的一點就是你可以“undo”幾乎所有的命令。你可以用這個功能來修正你剛剛提交的程式碼中的一個問題或者回滾整個程式碼提交操作。你甚至可以恢復一個被刪除的提交,因為在後端,Git幾乎不做任何刪除操作。
4)省心 
你有沒有丟失過版本庫?我有,而那種頭疼的感覺現在還記憶猶新。而用Git的話,我就不必擔心這個問題,因為任何一個人機器上的版本都是一個完整的備份。 
5)選擇有用的程式碼提交 
當你把紐帶,冰塊還有西紅柿一起扔進攪拌機的時候至少有兩個問題。第一,攪拌過後,沒有人知道之前扔進去了些什麼東西。第二,你不能回退,重新把西紅柿拿出來。同樣的,當你提交了一堆無關的更改,例如功能A加強,新增功能B,功能C修復,想要理清這一堆程式碼到底幹了什麼是很困難的。當然,當發現功能A出問題的時候,你無法單獨回滾功能A。Git可以通過建立“顆粒提交”,幫你解決這個問題。“staging area”的概念可以讓你決定到底那些東西需要提交,或者更新,精確到行。
6)自由選擇工作方式 
使用Git,你可以同時和多個遠端程式碼庫連線,“rebase”而不是"merge"甚至只連線某個模組。但是你也可以選擇一箇中央版本庫,就像SVN那樣。你依然可以利用Git的其他優點。 
7)保持工作獨立 
把不同的問題分開處理將有助於跟蹤問題的進度。當你在為功能A工作的時候,其他人不應該被你還沒有完成的程式碼所影響。分支是解決這個問題的辦法。雖然其他的版本控制軟體業有分支系統,但是Git是第一個把這個系統變得簡單而快速的系統。
8)隨大流 
雖然只有死於才隨著波浪前進,但是很多時候聰明的程式設計師也是隨大流的。越來越多的公司,開源專案使用Git,包括Ruby On Rails,jQuery,Perl,Debian,Linux Kernel等等。擁有一個強大的社群是很大的優勢,有很多教程、工具。

Git原理

1)四種基本型別
BLOB:  每個blob代表一個(版本的)檔案,blob只包含檔案的資料,而忽略檔案的其他後設資料,如名字、路徑、格式等。
TREE:  每個tree代表了一個目錄的資訊,包含了此目錄下的blobs,子目錄(對應於子trees),檔名、路徑等後設資料。因此,對於有子目錄的目錄,git相當於儲存了巢狀的trees。
COMMIT:每個commit記錄了提交一個更新的所有後設資料,如指向的tree,父commit,作者、提交者、提交日期、提交日誌等。每次提交都指向一個tree物件,記錄了當次提交時的目錄資訊。一個commit可以有多個(至少一個)父commits。
TAG:   ag用於給某個上述型別的物件指配一個便於開發者記憶的名字, 通常用於某次commit。

2)工作區(Working Dir),提交區/暫存區(stage/index),版本庫 

 

Git的安裝

不同的系統不同的安裝命令,centos系統下直接yum就可以。
[root@master-node ~]# yum install -y git
安裝完成後,還需要最後一步設定,在命令列輸入:
[root@master-node ~]#git config --global user.email "you@example.com"
[root@master-node ~]#git config --global user.name "Your Name"
因為Git是分散式版本控制系統,所以,每個機器都必須自報家門:你的名字和Email地址。你也許會擔心,如果有人故意冒充別人怎麼辦?這個不必擔心,首先我們相信大家都是善良無知的群眾,其次,真的有冒充的也是有辦法可查的。
注意:
git config命令的--global引數,用了這個引數,表示你這臺機器上所有的Git倉庫都會使用這個配置,當然也可以對某個倉庫指定不同的使用者名稱和Email地址。

Git常用的命令(即git + 下面的引數組成的命令):

add           新增檔案內容至索引
bisect       通過二分查詢定位引入 bug 的變更
branch     列出、建立或刪除分支
checkout  檢出一個分支或路徑到工作區
clone       克隆一個版本庫到一個新目錄
commit    記錄變更到版本庫
diff          顯示提交之間、提交和工作區之間等的差異
fetch       從另外一個版本庫下載物件和引用
grep        輸出和模式匹配的行
init         建立一個空的 Git 版本庫或重新初始化一個已存在的版本庫
log         顯示提交日誌
merge    合併兩個或更多開發歷史
mv        移動或重新命名一個檔案、目錄或符號連結
pull       獲取併合並另外的版本庫或一個本地分支
push     更新遠端引用和相關的物件
rebase  本地提交轉移至更新後的上游分支中
reset    重置當前HEAD到指定狀態
rm       從工作區和索引中刪除檔案
show    顯示各種型別的物件
status  顯示工作區狀態
tag      建立、列出、刪除或校驗一個GPG簽名的 tag 物件

建立版本庫

什麼是版本庫呢?版本庫又名倉庫,英文名repository,你可以簡單理解成一個目錄,這個目錄裡面的所有檔案都可以被Git管理起來,每個檔案的修改、刪除,Git都能跟蹤,以便任何時刻都可以追蹤歷史,或者在將來某個時刻可以“還原”。
所以,建立一個版本庫非常簡單,首先,選擇一個合適的地方,建立一個空目錄:

第一步:建立一個倉庫的目錄
[root@master-node /]# mkdir git_test
[root@master-node /]# cd git_test/
[root@master-node git_test]# pwd
/git_test

第二步:通過git init 命令把這個目錄變成git可以管理的倉庫
[root@master-node git_test]# git init
初始化空的 Git 版本庫於 /git_test/.git/
[root@master-node git_test]# ls -al
總用量 12
drwxr-xr-x 3 root root 4096 5月 10 13:53 .
drwxr-xr-x. 19 root root 4096 5月 10 13:50 ..
drwxr-xr-x 7 root root 4096 5月 10 13:53 .git
瞬間Git就把倉庫建好了,而且告訴你是一個空的倉庫(empty Git repository),細心的讀者可以發現當前目錄下多了一個.git的目錄,這個目錄是Git來跟蹤管理版本庫的,沒事千萬不要手動修改這個目錄裡面的檔案,不然改亂了,就把Git倉庫給破壞了。也不一定必須在空目錄下建立Git倉庫,選擇一個已經有東西的目錄也是可以的。

第三步:把檔案新增到版本庫
首先這裡再明確一下,所有的版本控制系統,其實只能跟蹤文字檔案的改動,比如TXT檔案,網頁,所有的程式程式碼等等,Git也不例外。版本控制系統可以告訴你每次的改動,比如在第5行加了一個單詞“Linux”,在第8行刪了一個單詞“Windows”。而圖片、視訊這些二進位制檔案,雖然也能由版本控制系統管理,但沒法跟蹤檔案的變化,只能把二進位制檔案每次改動串起來,也就是隻知道圖片從100KB改成了120KB,但到底改了啥,版本控制系統不知道,也沒法知道。

現在我們編寫一個readme.txt檔案,內容如下
[root@master-node git_test]# cat readme.txt
Git is veryt good tool
auth :cgt
一定要放到git_test目錄下面,子目錄也可以,放到其他地方git找不到檔案。

把一個檔案放到Git倉庫只需要兩步。
1)用命令git add告訴git,把檔案新增到倉庫
[root@master-node git_test]# git add readme.txt
執行上面的命令,沒有任何的顯示就對了。Linux的哲學思想:沒有訊息就是最好的訊息,說明新增成功。
2)用命令git commit告訴git,把檔案提交到倉庫
[root@master-node git_test]# git commit -m "cgt write a readme file"
[master(根提交) 87818f5] cgt write a readme file
1 file changed, 2 insertions(+)
create mode 100644 readme.txt

簡單解釋一下git commit命令,-m後面輸入的是本次提交的說明,可以輸入任意內容,當然最好是有意義的,這樣你就能從歷史記錄裡方便地找到改動記錄。
嫌麻煩不想輸入-m "xxx"行不行?確實有辦法可以這麼幹,但是強烈不建議你這麼幹,因為輸入說明對自己對別人閱讀都很重要。實在不想輸入說明的童鞋請自行Google,我不告訴你這個引數。
git commit命令執行成功後會告訴你,1個檔案被改動(我們新新增的readme.txt檔案),插入了兩行內容(readme.txt有兩行內容)。

為什麼Git新增檔案需要add,commit一共兩步呢?因為commit可以一次提交很多檔案,所以你可以多次add不同的檔案,比如:
[root@master-node git_test]# touch file1 file2 file3
[root@master-node git_test]# ls
file1 file2 file3 readme.txt
[root@master-node git_test]# git add file1 file2 file3
[root@master-node git_test]# git commit -m "add 3 files"
[master 827526e] add 3 files
3 files changed, 0 insertions(+), 0 deletions(-)
create mode 100644 file1
create mode 100644 file2
create mode 100644 file3

再次插足一下,說明一下git的工作流:

你的本地倉庫由 git 維護的三棵"樹"組成。第一個是你的 工作目錄,它持有實際檔案;第二個是 暫存區(staging),它像個快取區域,臨時儲存你的改動;最後是 HEAD,它指向你最後一次提交的結果。
你可以提出更改(把它們新增到暫存區),使用如下命令:
git add <filename>
git add *
這是 git 基本工作流程的第一步;使用如下命令以實際提交改動:
git commit -m "程式碼提交資訊"
現在,你的改動已經提交到了 HEAD,但是還沒到你的遠端倉庫。

回滾-讓去哪就去哪

前面已經建立了一個readme.txt檔案,現在我們對他進行一些改動操作。
[root@master-node git_test]# cat readme.txt
Git is veryt good tool
auth :cgt
date:2016-5-10
執行git status 
[root@master-node git_test]# git status
# 位於分支 master
# 尚未暫存以備提交的變更:
# (使用 "git add <file>..." 更新要提交的內容)
# (使用 "git checkout -- <file>..." 丟棄工作區的改動)
#
# 修改: readme.txt
#
修改尚未加入提交(使用 "git add" 和/或 "git commit -a")
git status命令可以讓我們時刻掌握倉庫當前的狀態,上面的命令告訴我們,readme.txt被修改過了,但還沒有準備提交的修改。
雖然Git告訴我們readme.txt被修改了,但如果能看看具體修改了什麼內容,自然是很好的。比如你第二天上班時,已經記不清上次怎麼修改的readme.txt,所以,需要用git diff這個命令看看,然後add之後在看一下status,是顯示要commit的檔案,現在再回想一下那個工作流圖
[root@master-node git_test]# git diff
diff --git a/readme.txt b/readme.txt
index b7cffdb..43b7253 100644
--- a/readme.txt
+++ b/readme.txt
@@ -1,2 +1,3 @@
Git is veryt good tool
auth :cgt
+date:2016-5-10
[root@master-node git_test]# git add readme.txt
[root@master-node git_test]# git status
# 位於分支 master
# 要提交的變更:
# (使用 "git reset HEAD <file>..." 撤出暫存區)
#
# 修改: readme.txt
接下來進行commit操作
[root@master-node git_test]# git commit -m "add date"
[master de00305] add date
1 file changed, 1 insertion(+)
提交之後,在檢視status
[root@master-node git_test]# git status
# 位於分支 master
無檔案要提交,乾淨的工作區

版本的回退

現在,你已經學會了修改檔案,然後把修改提交到Git版本庫,現在,再練習一次,修改readme.txt檔案如下:
[root@master-node git_test]# cat readme.txt
Git is veryt good tool
auth :cgt
date:2016-5-10
version:1
[root@master-node git_test]# git add readme.txt
[root@master-node git_test]# git commit -m "version"
[master 8b7d4ee] version
1 file changed, 1 insertion(+)

目前我們已經提交了三次,暫時你還能記住,但是在實際工作中我們是記不住的,不然要版本控制系統幹什麼。版本控制系統肯定有某個命令可以告訴我們歷史記錄,在Git中,我們用git log命令檢視:
[root@master-node git_test]# git log
commit 8b7d4eebe4e03809162f8193d6b2338926896ab4
Author: caoxiaojian <1099415469@qq.com>
Date: Tue May 10 14:59:16 2016 +0800
version
commit de003058c91312f695b57f42724f826f6ef42f17
Author: caoxiaojian <1099415469@qq.com>
Date: Tue May 10 14:52:10 2016 +0800
add date
commit 827526ee243c93bfaf8f4f2f9dc22d31325cb47a
Author: caoxiaojian <1099415469@qq.com>
Date: Tue May 10 14:23:08 2016 +0800
add 3 files
commit 87818f5454a2bc41cfbeca4b923a510d11fe72ac
Author: caoxiaojian <1099415469@qq.com>
Date: Tue May 10 14:19:08 2016 +0800
cgt write a readme file
git log 顯示從最近到最遠的提交日誌,我們可以看到四次提交,最近的一次是version,上一次是date,最早的一次是cgt write a readme file 。

如果嫌輸出的資訊太多,可以使用--pretty=oneline
[root@master-node git_test]# git log --pretty=oneline
8b7d4eebe4e03809162f8193d6b2338926896ab4 version
de003058c91312f695b57f42724f826f6ef42f17 add date
827526ee243c93bfaf8f4f2f9dc22d31325cb47a add 3 files
87818f5454a2bc41cfbeca4b923a510d11fe72ac cgt write a readme file

需要友情提示的是,你看到的一大串8b7d4eebe4e03809162f8193d6b2338926896ab4類似的是commit id(版本號),和SVN不一樣,Git的commit id不是1,2,3……遞增的數字,而是一個SHA1計算出來的一個非常大的數字,用十六進位制表示,而且你看到的commit id和我的肯定不一樣,以你自己的為準。為什麼commit id需要用這麼一大串數字表示呢?因為Git是分散式的版本控制系統,後面我們還要研究多人在同一個版本庫裡工作,如果大家都用1,2,3……作為版本號,那肯定就衝突了。

現在開始回滾,
準備把readme.txt回退到上一個版本,也就是“date”的那個版本,怎麼做呢?
首先,Git必須知道當前版本是哪個版本,在Git中,用HEAD表示當前版本,也就是最新的提交3628164...882e1e0(注意我的提交ID和你的肯定不一樣),上一個版本就是HEAD^,上上一個版本就是HEAD^^,當然往上100個版本寫100個^比較容易數不過來,所以寫成HEAD~100。
回滾,我們可以使用git reset這個命令

[root@master-node git_test]# git reset --hard HEAD^
HEAD 現在位於 de00305 add date
[root@master-node git_test]# cat readme.txt
Git is veryt good tool
auth :cgt
date:2016-5-10
可以看出,他沒有version那行,說明回滾成功。

再來看git log 
[root@master-node git_test]# git log
commit de003058c91312f695b57f42724f826f6ef42f17
Author: caoxiaojian <1099415469@qq.com>
Date: Tue May 10 14:52:10 2016 +0800
add date
commit 827526ee243c93bfaf8f4f2f9dc22d31325cb47a
Author: caoxiaojian <1099415469@qq.com>
Date: Tue May 10 14:23:08 2016 +0800
add 3 files
commit 87818f5454a2bc41cfbeca4b923a510d11fe72ac
Author: caoxiaojian <1099415469@qq.com>
Date: Tue May 10 14:19:08 2016 +0800
cgt write a readme file
沒有了之前的version,那我要怎麼才能恢復呢,回你的終端上,看看version的commit id,我們找到了:
8b7d4eebe4e03809162f8193d6b2338926896ab4 version,執行恢復。恢復的時候ID不需要寫全部的。
[root@master-node git_test]# git reset --hard 8b7d4eebe4
HEAD 現在位於 8b7d4ee version
[root@master-node git_test]# cat readme.txt
Git is veryt good tool
auth :cgt
date:2016-5-10
version:1

回滾原理解析:
Git的版本回退速度非常快,因為Git在內部有個指向當前版本的HEAD指標,當你回退版本的時候,Git僅僅是把HEAD從當前的version,指到date

回滾後

然後順便把工作區的檔案更新了。所以你讓HEAD指向哪個版本號,你就把當前版本定位在哪。
到了這裡肯定有童鞋要問,那我要是不知道我前面的ID了,我去哪裡回滾,我是不是該收拾工位回滾到家中了,git早就替你想好了,可以使用git reflog,把之前的ID都顯示出來
[root@master-node git_test]# git reflog
8b7d4ee HEAD@{0}: reset: moving to 8b7d4eebe4
de00305 HEAD@{1}: reset: moving to HEAD^
8b7d4ee HEAD@{2}: commit: version
de00305 HEAD@{3}: commit: add date
827526e HEAD@{4}: commit: add 3 files
87818f5 HEAD@{5}: commit (initial): cgt write a readme file

工作區和暫存區
不知道你是不是理解了我之前說的那個工作流,我們們這裡再來囉嗦一遍。
Git和其他版本控制系統如svn不同之處是有暫存區的概念

先弄清楚這幾個名次
工作區:
就是在你的電腦裡能看到的目錄,比如我們們建立的git_test

版本庫:
工作區中有一個隱藏目錄.git(之前已經給童鞋們提到過這個檔案),它不算工作區,而是git的版本庫。

git的版本庫裡面存放了很多的東西,其中最重要的就是稱為stage(index)的暫存區,還有git為我們自動建立的第一個分支master,以及指向master的一個指標叫head。

當我們在工作區建立了檔案後,執行add後,再來看這個圖

當你執行commit後,暫存區的內容就沒有

管理修改

為什麼Git比其他版本控制系統設計得優秀,因為Git跟蹤並管理的是修改,而非檔案。
你會問,什麼是修改?比如你新增了一行,這就是一個修改,刪除了一行,也是一個修改,更改了某些字元,也是一個修改,刪了一些又加了一些,也是一個修改,甚至建立一個新檔案,也算一個修改。
為什麼說Git管理的是修改,而不是檔案呢?我們還是做實驗。第一步,對readme.txt做一個修改,第二步,新增到暫存區。第三步,檢視狀態
[root@master-node git_test]# cat readme.txt
Git is veryt good tool
auth :cgt
date:2016-5-10
version:1
modify----------1
[root@master-node git_test]# git add readme.txt
[root@master-node git_test]# git status
# 位於分支 master
# 要提交的變更:
# (使用 "git reset HEAD <file>..." 撤出暫存區)
#
# 修改: readme.txt
#
第四步,再次編輯readme.txt,然後直接commit,再次檢視status

[root@master-node git_test]# cat readme.txt
Git is veryt good tool
auth :cgt
date:2016-5-10
version:1
modify----------1
modify----------2
[root@master-node git_test]# git commit -m "modify"
[master 766baac] modify
1 file changed, 1 insertion(+)
[root@master-node git_test]# git status
# 位於分支 master
# 尚未暫存以備提交的變更:
# (使用 "git add <file>..." 更新要提交的內容)
# (使用 "git checkout -- <file>..." 丟棄工作區的改動)
#
# 修改: readme.txt
#
修改尚未加入提交(使用 "git add" 和/或 "git commit -a")
發現第二次的修改沒有commit,那是因為你沒有add

提交後,用git diff HEAD -- readme.txt命令可以檢視工作區和版本庫裡面最新版本的區別:

[root@master-node git_test]# git diff HEAD -- readme.txt
diff --git a/readme.txt b/readme.txt
index 4416460..07c12e7 100644
--- a/readme.txt
+++ b/readme.txt
@@ -3,3 +3,4 @@ auth :cgt
date:2016-5-10
version:1
modify----------1
+modify----------2

撤銷修改

編輯了readme,
[root@master-node git_test]# cat readme.txt
Git is veryt good tool
auth :cgt
date:2016-5-10
version:1
modify----------1
modify----------2
caoxiaojian is xiaojianjian
已經編輯完成,忽然發現了問題,最後一行,道出了筆者的心聲。既然已經發現錯誤,那就很容易糾正它,你可以刪除掉最後一行,手動把檔案恢復到上一個版本的狀態,如果用git status檢視一下,

[root@master-node git_test]# git status
# 位於分支 master
# 尚未暫存以備提交的變更:
# (使用 "git add <file>..." 更新要提交的內容)
# (使用 "git checkout -- <file>..." 丟棄工作區的改動)
#
# 修改: readme.txt
#
修改尚未加入提交(使用 "git add" 和/或 "git commit -a")
你可以發現上面提示你使用 "git checkout -- <file>..." 丟棄工作區的改動,那我們執行下

[root@master-node git_test]# git checkout -- readme.txt
[root@master-node git_test]# cat readme.txt
Git is veryt good tool
auth :cgt
date:2016-5-10
version:1
modify----------1
已經回來了。

命令git checkout -- readme.txt意思就是,把readme.txt檔案在工作區的修改全部撤銷,這裡有兩種情況:
一種是readme.txt自修改後還沒有被放到暫存區,現在,撤銷修改就回到和版本庫一模一樣的狀態;
一種是readme.txt已經新增到暫存區後,又作了修改,現在,撤銷修改就回到新增到暫存區後的狀態。
總之,就是讓這個檔案回到最近一次git commit或git add時的狀態。
git checkout -- file命令中的--很重要,沒有--,就變成了“切換到另一個分支”的命令,我們在後面的分支管理中會再次遇到git checkout命令。
剛剛我們們沒有將修改提交到暫存區,那假如你提交到了呢??????你說咋整呢?????是不是嚇尿了????
[root@master-node git_test]# cat readme.txt
Git is veryt good tool
auth :cgt
date:2016-5-10
version:1
modify----------1
caoxiaojian is xiaojianjian
[root@master-node git_test]# git add readme.txt
[root@master-node git_test]# git status
# 位於分支 master
# 要提交的變更:
# (使用 "git reset HEAD <file>..." 撤出暫存區)
# 修改: readme.txt
你要是聰明的話,你應該已經知道要怎麼做了。。。。對是的,就是你想的那樣。

[root@master-node git_test]# git reset HEAD readme.txt
重置後撤出暫存區的變更:
M readme.txt
[root@master-node git_test]# git status
# 位於分支 master
# 尚未暫存以備提交的變更:
# (使用 "git add <file>..." 更新要提交的內容)
# (使用 "git checkout -- <file>..." 丟棄工作區的改動)
#
# 修改: readme.txt
#
修改尚未加入提交(使用 "git add" 和/或 "git commit -a")
已經退出了暫存區。你可能要問,那要是已經commit了呢??你小子膽子還真不小啊,教你一招,之前不是講過回滾嘛,直接回滾。不過這個也是有條件的,就是你還沒有把自己的本地版本庫推送到遠端。Git是一個分散式版本控制,他還有遠端版本庫,一旦你提交到遠端版本庫,那你就可以git go home

刪除檔案

先建立檔案,後add,在commit,然後刪除工作區的檔案
[root@master-node git_test]# cat test.txt
qwertyuiop[
adfghjjljh
fdgscvxz
[root@master-node git_test]# git add test.txt
[root@master-node git_test]# git commit -m "del test"
[master 63d3bf7] del test
1 file changed, 3 insertions(+)
create mode 100644 test.txt
[root@master-node git_test]# rm -rf test.txt
[root@master-node git_test]# git status
# 位於分支 master
# 尚未暫存以備提交的變更:
# (使用 "git add/rm <file>..." 更新要提交的內容)
# (使用 "git checkout -- <file>..." 丟棄工作區的改動)
#
# 修改: readme.txt
# 刪除: test.txt
#
修改尚未加入提交(使用 "git add" 和/或 "git commit -a")
現在你有兩個選擇,一是確實要從版本庫中刪除該檔案,那就用命令git rm刪掉,並且git commit:
[root@master-node git_test]# git rm test.txt
rm 'test.txt'
[root@master-node git_test]# git commit -m "remove test.txt"
[master 5f04ee2] remove test.txt
1 file changed, 3 deletions(-)
delete mode 100644 test.txt
現在檔案就從版本庫中徹底的刪除了。

命令git rm用於刪除一個檔案。如果一個檔案已經被提交到版本庫,那麼你永遠不用擔心誤刪,但是要小心,你只能恢復檔案到最新版本,你會丟失最近一次提交後你修改的內容。

另一種情況是刪錯了,因為版本庫裡還有呢,所以可以很輕鬆地把誤刪的檔案恢復到最新版本:

使用

git checkout -- test.txt
git checkout其實是用版本庫裡的版本替換工作區的版本,無論工作區是修改還是刪除,都可以“一鍵還原”。

遠端倉庫

可以使用github給我們提供的服務,作為我們的一個遠端倉庫。但是需要做一下的設定。
由於你的本地Git倉庫和GitHub倉庫之間的傳輸是通過SSH加密的,所以我們首先生成祕鑰。

第1步:建立SSH Key。
在使用者主目錄下,看看有沒有.ssh目錄,如果有,再看看這個目錄下有沒有id_rsa和id_rsa.pub這兩個檔案,如果已經有了,可直接跳到下一步。如果沒有,開啟Shell(Windows下開啟Git Bash),建立SSH Key:
ssh-keygen -t rsa -C "youremail@example.com"
你需要把郵件地址換成你自己的郵件地址,然後一路回車,使用預設值即可,由於這個Key也不是用於軍事目的,所以也無需設定密碼。
如果一切順利的話,可以在使用者主目錄裡找到.ssh目錄,裡面有id_rsa和id_rsa.pub兩個檔案,這兩個就是SSH Key的祕鑰對,id_rsa是私鑰,不能洩露出去,id_rsa.pub是公鑰,可以放心地告訴任何人。

第2步:新增公鑰到你的
登陸GitHub,開啟“Account settings”,“SSH Keys”頁面,然後將你的key新增上。
為什麼GitHub需要SSH Key呢?因為GitHub需要識別出你推送的提交確實是你推送的,而不是別人冒充的,而Git支援SSH協議,所以,GitHub只要知道了你的公鑰,就可以確認只有你自己才能推送。

新增遠端庫
你原本在本地已經建立了一個Git倉庫,現在又想在github上也建立一個倉庫,讓這兩個倉庫可以遠端同步。這樣github的倉庫既可以作為備份,又可以讓其他人通過該倉庫來協作。

第一步:建立一個新的倉庫repository
右上角有個➕,然後現在new repository

然後進入建立頁面

倉庫內容的提交

接下來我們們看如何使用
本地沒有倉庫的情況下,要先建立倉庫。
[root@master-node ~]# mkdir /test
[root@master-node ~]# cd /test
使用git init 建立定義倉庫
[root@master-node ~]# git init
Initialized empty Git repository in C:/Program Files/Git/test/.git/
[root@master-node ~]# ls -al
total 8
drwxr-xr-x 1 cgt 197121 0 五月 16 06:11 ./
drwxr-xr-x 1 cgt 197121 0 五月 16 06:11 ../
drwxr-xr-x 1 cgt 197121 0 五月 16 06:11 .git/

在倉庫中建立一個檔案,然後add commit
[root@master-node ~]# echo "# test " >> README.md
[root@master-node ~]# git add README.md
warning: LF will be replaced by CRLF in README.md.
The file will have its original line endings in your working directory.

[root@master-node ~]# git commit -m "first commit"
[master (root-commit) 7eeb945] first commit
warning: LF will be replaced by CRLF in README.md.
The file will have its original line endings in your working directory.
1 file changed, 1 insertion(+)
create mode 100644 README.md
至此,提交到本地的head中。也就是本地倉庫中,然後從本地倉庫向遠端倉庫提交。

使用remote關聯一個遠端庫,add是定義一個遠端的名稱,預設一般使用origin,後面跟的是遠端倉庫的名稱
[root@master-node ~]# git remote add origin git@github.com:caoxiaojian/test.git
把本地庫的內容推送到遠端,實際上是將本地的當前分支master,推送到遠端
[root@master-node ~]# git push -u origin master
Warning: Permanently added the RSA host key for IP address '192.30.252.128' to the list of known hosts.
Counting objects: 3, done.
Writing objects: 100% (3/3), 210 bytes | 0 bytes/s, done.
Total 3 (delta 0), reused 0 (delta 0)
To git@github.com:caoxiaojian/test.git
* [new branch] master -> master
Branch master set up to track remote branch master from origin.
由於遠端庫是空的,我們第一次推送master分支時,加上了-u引數,Git不但會把本地的master分支內容推送的遠端新的master分支,還會把本地的master分支和遠端的master分支關聯起來,在以後的推送或者拉取時就可以簡化命令。

再次修改README.md
[root@master-node ~]# echo "# this is my test file " >> README.md
[root@master-node ~]# git add README.md
warning: LF will be replaced by CRLF in README.md.
The file will have its original line endings in your working directory.
[root@master-node ~]# git commit -m "2 commit"
[master warning: LF will be replaced by CRLF in README.md.
The file will have its original line endings in your working directory.
80bc0e7] 2 commit
warning: LF will be replaced by CRLF in README.md.
The file will have its original line endings in your working directory.
1 file changed, 1 insertion(+)
[root@master-node ~]# git push origin master
Warning: Permanently added the RSA host key for IP address '192.30.252.120' to the list of known hosts.
Counting objects: 3, done.
Writing objects: 100% (3/3), 257 bytes | 0 bytes/s, done.
Total 3 (delta 0), reused 0 (delta 0)
To git@github.com:caoxiaojian/test.git
7eeb945..80bc0e7 master -> master
到github下面對應的倉庫裡檢視

 

 

 

 

每次本地提交後,只要有必要,就可以使用命令git push origin master推送最新修改

克隆遠端庫
在剛建立的倉庫中建立新檔案

建立檔案

 建立完成後可以看見你的新檔案

因為之前已經建立了test這個本地倉庫,所以先刪除,然後再clone
[root@master-node ~]# rm -rf /test/
[root@master-node ~]# git clone git@github.com:caoxiaojian/test.git
Cloning into 'test'...
Warning: Permanently added the RSA host key for IP address '192.30.252.121' to the list of known hosts.
remote: Counting objects: 9, done.
remote: Compressing objects: 100% (4/4), done.
remote: Total 9 (delta 0), reused 6 (delta 0), pack-reused 0
Receiving objects: 100% (9/9), done.
Checking connectivity... done.
[root@master-node ~]# cd /test/
test (master)
[root@master-node ~]# ls
new_file README.md
test (master)
[root@master-node ~]# cat new_file
make a new file for clone test

分支管理
分支在實際中有什麼用呢?假設你準備開發一個新功能,但是需要兩週才能完成,第一週你寫了50%的程式碼,如果立刻提交,由於程式碼還沒寫完,不完整的程式碼庫會導致別人不能幹活了。如果等程式碼全部寫完再一次提交,又存在丟失每天進度的巨大風險。
現在有了分支,就不用怕了。你建立了一個屬於你自己的分支,別人看不到,還繼續在原來的分支上正常工作,而你在自己的分支上幹活,想提交就提交,直到開發完畢後,再一次性合併到原來的分支上,這樣,既安全,又不影響別人工作。

相關文章