文章目錄
1 版本控制系統
版本控制系統,VCS(Version Control System),是一種記錄一個或若干文件內容變化,以便將來查閱特定版本修訂情況的系統。歷史上版本控制系統的演進又以下三種:
1.1 本地版本控制系統
採用某種簡單的數據庫或硬盤來記錄文件的歷次更新差異。RCS就是代表性的本地版本控制系統。
1.2 集中化版本控制系統
爲了讓不同系統上的開發者協同工作,集中化版本控制系統(Centralized Version Control Systems,簡稱CVCS)應運而生。
代表產品CVS、Subversion 、 Perforce 以及當下使用廣泛的SVN。
集中化版本控制系統有一個單一的集中管理的服務器,保存所有文件的修訂版本,而協同工作的人們都通過客戶端連到這臺服務器,取出最新的文件或者提交更新。
優點:特別是相較於老式的本地 VCS 來說,CVCS允許多人協同工作,而管理員也可以輕鬆掌控每個開發者的權限,並且管理一個 CVCS 要遠比在各個客戶端上維護本地數據庫來得輕鬆容易。
缺點:依賴中央服務器,用戶本地只有自己以前所同步的版本,每次都需要聯網拿到服務器的最新版本,如果中央服務器出現單點故障,就會造成數據丟失。
1.3 分佈式版本控制系統
分佈式版本控制系統(Distributed Version Control System,簡稱 DVCS)解決了集中式版本控制系統的弊端。客戶端並不只提取最新版本的文件快照,而是把代碼倉庫完整地鏡像下來。 這麼一來,任何一處協同工作用的服務器發生故障,事後都可以用任何一個鏡像出來的本地倉庫恢復。 因爲每一次的克隆操作,實際上都是一次對代碼倉庫的完整備份。用戶在本地倉庫做版本控制,需要與其他用戶交互時才聯網push到服務器。
代表產品有Mercurial、Bazaar 、 Darcs 以及Git。
分佈式版本控制系統功能強大,不可避免地增加了本地存儲空間的佔用,邏輯較爲複雜。
1.4 git的優缺點
優點:
- 適合分佈式開發,強調個體。
- 公共服務器壓力和數據量都不會太大。
- 速度快、靈活。
- 任意兩個開發者之間可以很容易的解決衝突。
- 離線工作。
缺點:
- 模式上比SVN更加複雜。
- 代碼保密性差。
2 git相關術語
- 倉庫(Repository):受版本控制的所有文件修訂歷史的共享數據庫
- 工作空間(Workspace) :本地硬盤上的文件
- 工作樹/區(Working tree):工作區中包含了倉庫的工作文件
- 暫存區(Staging area):用來暫存Workspace文件commit之前的變化,通過git add命令添加到暫存區。
- 索引(Index):即暫存區
- 本地倉庫(Local Repository):本地完整的文件版本倉庫
- 歷史庫(History):即本地倉庫
- 遠程倉庫(Remote Repository):服務器的文件版本倉庫
- 跟蹤(track):將本地文件賦予git版本控制功能
- 簽出(Checkout):從倉庫中將文件的最新修訂版本複製到工作空間
- 提交(Commit):對各自文件的工作副本做了更改,並將這些更改提交到倉庫,提交可以是名詞,代表提交的倉庫版本
- 推送(Push):將本地倉庫的版本推送到遠程倉庫
- 衝突(Conflict):多人對同一文件的工作副本進行更改,並將這些更改提交到倉庫
- 分支(Branch):從主線上分離開的副本,默認分支叫master
- 頭(HEAD):頭是一個象徵性的參考,最常用以指向當前選擇的分支。
- 修訂(Revision):表示代碼的一個版本狀態。Git通過用SHA1 hash算法表示的ID來標識不同的版本。
- 標記(Tags):標記指的是某個分支某個特定時間點的狀態。通過標記,可以很方便的切換到標記時的狀態。
- 鎖(Lock):獲得修改文件的專有權限。
3 git配置
3.1 git三個配置文件
Git 自帶一個 git config
的工具來幫助設置控制 Git 外觀和行爲的配置變量。 這些變量存儲在三個不同的位置,分別代表三個不同級別的配置,系統配置、用戶配置、當前倉庫配置:
-
/etc/gitconfig
文件: (windows系統該文件在git安裝目錄/Git/mingw64/etc/gitconfig),包含系統上每一個用戶及他們倉庫的通用配置。 如果使用帶有--system
選項的git config
時,它會從此文件讀寫配置變量。例如,打開git bash工具,鍵入:git config --system user.name 張三
-
~/.gitconfig
或~/.config/git/config
文件:(windows系統該文件在用戶目錄/gitconfig),只針對當前用戶。 可以傳遞--global
選項讓 Git 讀寫此文件。git config --global user.name 張三
-
當前使用倉庫的 Git 目錄中的
config
文件(就是.git/config
):針對該倉庫。git config --local user.name 張三
當三個配置文件有配置項重複時,每一個級別覆蓋上一級別的配置,當前倉庫配置覆蓋用戶配置,用戶配置覆蓋系統配置。
*git bash是個shell命令工具,可以在windows上敲各種bash shell命令,還可以使用vim哦
相關命令:
查看所有配置項(相同配置項按文件級別覆蓋):
git config -l
查看指定配置項(以用戶名爲例,下同)
git config --get user.name
查看配置項,使用正則表達式匹配
git config --get-regexp user.*
刪除配置項
git config [--local|--global|--system] --unset user.name
爲git命令設置別名
git config [--local|--global|--system] alias.ci commit #用ci代替commit
更多用法鍵入git config會顯示help
3.2 .gitignore文件
在工作空間中有些文件不需要納入git版本控制,比如編譯生成的.class文件,IDE相關的文件(.ipr、.idea、.setting等)、數據庫相關文件等等。
此時可以在主目錄下建立".gitignore"文件,然後在裏面配置需要忽略的文件,文件內容規則如下圖:
.gitignore文件定義的文件將不受git版本控制管理,執行git add .
時這些忽略的文件不會添加到暫存區。
4 git文件操作
4.1 git工作原理
在進行提交操作時,Git 會保存一個提交對象(commit object)。使用git log
可以查看每一個commit object的對象信息。
Git會在暫存操作時使用SHA-1哈希算法爲每一個要暫存文件計算校驗和,然後會把當前版本的文件快照保存到 Git 倉庫中(Git 使用 blob 對象來保存它們),最終將校驗和加入到暫存區域等待提交。
當使用 git commit
進行提交操作時,Git 會先計算每一個子目錄(本例中只有項目根目錄)的校驗和,然後在 Git 倉庫中這些校驗和保存爲樹對象。 隨後,Git 便會創建一個提交對象,它除了包含上面提到的那些信息外,還包含指向這個樹對象(項目根目錄)的指針。如此一來,Git 就可以在需要的時候重現此次保存的快照。
提交完成後,Git 倉庫中有五個對象:三個 blob 對象(保存着文件快照)、一個樹對象(記錄着目錄結構和 blob 對象索引)以及一個提交對象(包含着指向前述樹對象的指針和所有提交信息)。
4.2 git工作區
Git本地有三個工作區域:工作目錄(Workspace)、暫存區(Stage/Index)、本地資源庫(Repository或History)。加上遠程的git倉庫(Remote Repository)一共四個工作區域。文件在這四個區域之間的轉換關係如下:
4.3 git文件狀態
根據文件保存到工作區的不同,就有四種文件狀態:
- Untracked: 未跟蹤,此文件不受git版本控制. 通過
git add filename
命令狀態變爲Staged
,通過git clean -df filename
命令可刪除未跟蹤文件。 - Unmodify: 文件已經入庫, 未修改, 即版本庫中的文件快照內容與文件夾中完全一致. 這種類型的文件有兩種去處, 如果它被修改, 而變爲
Modified
. 如果使用git rm
移出版本庫, 則成爲Untracked
文件 - Modified: 文件已修改, 僅僅是修改, 並沒有進行其他的操作. 這個文件也有兩個去處, 通過
git add
可進入暫存staged
狀態, 使用git checkout
則丟棄修改過, 返回到unmodify
狀態, 這個git checkout
即從庫中取出文件, 覆蓋當前修改 - Staged: 暫存狀態. 執行
git commit
則將修改同步到庫中, 這時庫中的文件和本地文件又變爲一致, 文件爲Unmodify
狀態. 執行git reset HEAD filename
取消暫存, 文件狀態爲Modified
四種狀態的轉換如下圖:
使用git status
命令查看文件狀態。
4.4 git文件操作命令
-
init:在當前目錄初始化一個版本庫,會生成.git文件夾。之後可以關聯遠程分支
#初始化版本庫 $ git init
-
clone:克隆遠程倉庫到本地,完成之後工作區文件狀態都是Unmodified
#克隆遠程倉庫到本地 $ git clone https://github.com/yozzs/xxx.git
-
add:將untracked或modefied狀態的文件添加到暫存區
# 添加指定文件到暫存區 $ git add [file1] [file2] ... # 添加指定目錄到暫存區,包括子目錄 $ git add [dir] # 添加當前目錄的所有文件到暫存區 $ git add .
-
commit:將暫存區或工作空間(-a)提交到本地倉庫
# 提交暫存區到倉庫區,message是提交的信息,用戶自己寫。如果不用-m選項,則會跳轉到vi頁面編寫提交信息,寫完:wq保存退出,如果不寫提交信息直接退出則提交失敗 $ git commit -m [message] # 提交暫存區的指定文件到倉庫區,在分支衝突的狀態下不能指定文件提交 $ git commit [file1] [file2] ... -m [message] # 提交工作區自上次commit之後的變化,直接到倉庫區,跳過add,對新文件無效 $ git commit -a # 提交時顯示所有diff信息 $ git commit -v # 使用一次新的commit,替代上一次提交,如果代碼沒有任何新變化,則用來改寫上一次commit的提交信息 $ git commit --amend -m [message] # 重做上一次commit,幷包括指定文件的新變化 $ git commit --amend [file1] [file2] ...
-
revert:撤銷提交
# 把指定的提交的所有修改回滾,並同時生成一個新的提交 $ git revert <commit-id>
-
push:將指定本地分支推送到指定的遠程主機
# 上傳本地指定分支到遠程倉庫,remote默認爲origin $ git push [remote] [localbranch]:[remotebranch] # 強行推送當前分支到遠程倉庫,即使有衝突 $ git push [remote] --force # 推送所有分支到遠程倉庫 $ git push [remote] --all
-
pull:拉取遠程倉庫的版本,並與本地分支合併,可以理解爲
git fetch
+git merge
# 拉取遠程倉庫指定分支的變化,並與指定本地分支合併,remote爲遠程主機名,默認origin $ git pull [remote] [remotebranch]:[localbranch] # 如果合併到當前分支,省略本地分支名 $ git pull [remote] [remotebranch] # 如果當前分支已指定追蹤遠程分支,可省略遠程分支名 $ git pull [remote] # 如果當前分支只有一個追蹤分支,連遠程主機名都可以省略 $ git pull
注意,分支推送順序的寫法是<來源地>:<目的地>,所以
git pull
是<遠程分支>:<本地分支>,而git push
是<本地分支>:<遠程分支>。 -
fetch:獲取遠程倉庫的變動到本地倉庫,不合並本地分支
# 獲取遠程倉庫所有更新,但不自動合併當前分支 $ git fetch [remote]
-
remote:操作遠程倉庫
# 簡單查看單個倉庫名 $ git remote # 顯示所有遠程倉庫 $ git remote -v # 顯示某個遠程倉庫的信息 $ git remote show [remote] # 增加一個新的遠程倉庫,並命名 $ git remote add [shortname] [url] # 修改遠程倉庫 $ git remote rename [oldname] [newname] # 刪除遠程倉庫 $ git remote rm [remote-name]
-
log:查看提交日誌,會vi進入log文件。會顯示每一次提交的commit_id、分支、作者、日期信息。
# 查看提交記錄 $ git log #可通過管道和其他shell命令操作,獲得想要的統計結果 # 查看提交記錄,顯示最近5次提交 $ git log -5 # 以圖形化的方式顯示提交歷史的關係,可以直觀地看到分支合併情況 $ git log --graph # 查看這個倉庫中所有的分支的所有更新記錄,包括已經撤銷的更新 $ git reflog
-
rm:刪除版本庫或暫存區的文件
# 只刪除暫存區文件,工作空間和倉庫不變 $ git rm --cached <file> # 刪除本地倉庫文件,會同時刪除工作空間和暫存區該文件 $ git rm -f <file>
-
clean:清理工作區未跟蹤狀態文件(不會清除.gitignore指定文件)
# 刪除工作區未跟蹤狀態文件 $ git clean -df #-d表示包含目錄,-f表示強制清除 # 刪除指定未跟蹤狀態文件 $ git clean -f <file>
-
reset:回退文件的本次修改,
reset
與git rm --cache
的區別是前者重寫本地倉庫的版本,效果可以是回退添加或修改,後者是刪除暫存區文件。# 將本地倉庫指定文件的版本重寫到暫存區 $ git reset HEAD <file> #HEAD表示HEAD指向的提交 # 放棄工作區和index的改動,同時HEAD指針指向前一個commit對象,一個commit對象實際就是一個倉庫的版本。效果就是撤銷提交。HEAD~1也可以改爲HEAD~n,n表示前n個提交 $ git reset --hard HEAD~1 #功能同上,一個^表示前一次提交,^^表示前兩次提交 $ git reset --hard HEAD^ #將HEAD,index(暫存區)和workspace全部重置爲指定某次提交的目錄樹 $ git reset --hard <commit_id> #只將HEAD重置爲指向指定某次提交的目錄樹,不重置工作區和暫存區 $ git reset --soft <commit_id> #只將HEAD和index重置爲指向指定某次提交的目錄樹,不充值工作區 $ git reset --mixed <commit_id>
-
checkout:簽出,用暫存區或倉庫的目錄樹替換工作區目錄樹。會修改工作空間,所以很危險。但是也很常用。
# 用暫存區目錄樹替換工作空間。此操作會刪除本地未暫存的modified文件,慎用 $ git checkout . # 用暫存區目錄樹指定文件替換工作空間該文件。 $ git checkout -- <file> # 用HEAD指向的分支(默認是master)倉庫目錄樹指定文件替換工作空間該文件。 $ git checkout HEAD -- <file> # 檢出branch分支,更新HEAD以指向branch分支,然後用branch分支指向的樹指定文件替換工作空間該文件。 $ git checkout <branch> -- <file> # 用某個提交(commit_id)目錄樹指定文件替換工作空間該文件。 $ git checkout commit_id -- <file>
-
diff:顯示WorkSpace中的文件和暫存區文件的差異,會顯示文件內容的差異
# 比較工作空間和暫存區 $ git diff [file] # 比較兩個提交對象的差異 $ git diff <commit_id1> <commit_id2>
-
ls-files:查看文件列表
# 查看所有緩存的文件 $ git ls-files # 查看所有未被跟蹤的文件,包括.gitignore的文件 $ git ls-files -o # 查看modified狀態的文件 $ git ls-files -modified # 查看暫存區的文件詳細信息 $ git ls-files -s
4.5 文件操作命令示意圖
5 git分支
5.1 分支概念
Git 的分支模型是它的“必殺技特性”,也正因爲這一特性,使得 Git 從衆多版本控制系統中脫穎而出。git官網有一句介紹:Git 保存的不是文件的變化或者差異,而是一系列不同時刻的文件快照。這也正是git實現分支模型及分佈式版本控制的基石。
Git 分支,本質上僅僅是指向提交對象的可變指針。創建分支時就是在當前的提交上創建了一個新的可移動的指針。然後git有一個特殊的指針:HEAD,指向當前所在的本地分支。每一個分支指向一個提交對象。HEAD指針指向當前本地分支。我們所說的本地倉庫,實際上就是HEAD指針指向的本地分支所指向的提交對象。
由於 Git 的分支實質上僅是包含所指提交對象校驗和(長度爲 40 的 SHA-1 值字符串)的文件,所以它的創建和銷燬都異常高效。
5.2 分支操作
-
新建testing分支
git branch testing
-
切換到testing分支
git checkout testing
此時master分支和testing分支指向同一個提交,所以工作空間看起來並沒有變化。接下來修改工作空間文件,然後提交。
-
使用testing分支提交
#修改文件後 git commit -a -m testing分支提交
此時testing分支指針向新的提交對象前進,master分支還是指向原來的提交對象。
-
切回到master分支
git checkout master
此時工作空間的文件會變爲master所指向的提交對象的狀態。
-
master分支提交
# 修改文件後 git commit -a -m master分支提交
此時分支出現分叉,使用
git log --graph
命令可以圖形化(字符圖形)查看分支提交歷史。 -
分支合併
#當前分支爲master git merge testing
合併之後會將testing分支指向的commit object的修改內容添加到master指向的commit object,並生成一次新的提交。然後就可以刪除被合併的testing分支。
-
刪除分支
git branch -d testing
-
發生衝突的合併
如果合併的兩個分支都修改了同一份文件,git將無法成功完成合並生成一個新的提交,此時Git 會暫停下來,等待你去解決合併產生的衝突。分支狀態變爲合併中,衝突文件狀態變爲both modified。衝突將在下一節具體復現。
-
其他分支操作
# 查看所有本地分支 $ git branch # 查看所有遠程分支 $ git branch -r # 查看所有本地和遠程分支 $ git branch -a # 新建分支並將其指向某個提交對象 $ git branch [branch] [commit_id] # 新建一個分支,與指定的遠程分支建立追蹤關係 $ $ git branch --track [branch] [remote-branch] # 刪除本地分支,-D表示強制刪除 $ git branch -d [branch] # 查看所有本地分支 $ git branch # 刪除遠程分支,remote表示遠程主機名,默認origin $ git push origin --delete [branch-name] $ git branch -dr [remote/branch]
5.3 合併衝突解決方案
前面說到如果合併的兩個分支都修改了同一份文件,合併會產生衝突。具體情景:
創建test1分支,在test1分支修改testconflict.txt文件並提交:
切換到master分支,master分支也修改testconflict.txt文件並提交,
然後合併master分支和test1分支,發生衝突
查看衝突文件,發現git有記錄衝突內容的格式。衝突文件狀態爲both modified,且分支狀態爲master|MERGING。解決衝突的方式是手動修改文件改正衝突內容後重新提交
重新提交。分支狀態變爲正常的master
git的分支是分佈式版本控制的核心概念,功能強大,也比較難以理解。可以查看官網資料學習,https://git-scm.com/book/en/v2
6 總結
作爲一套內容尋址文件系統,Git 不僅僅是一個版本控制系統,它同時是一個非常強大且易用的工具。git已經是開發人員必備技能,也是世界上最先進的分佈式版本控制系統,許多公司普遍使用基於git的代碼託管網站。有名的代碼託管網站有github、gitlab、bitbucket、gitee(碼雲)。各種git圖形化工具也是層出不窮,eclipse、idea等IDE軟件也都集成了git插件,但是使用圖形化工具的同時,也必須理解git命令及git版本控制原理。