Git 版本控制工具概述
Git是目前世界上最先進的分佈式版本控制系統。
本地版本控制系統
分佈式的版本控制系統
發展歷程:
Linus在1991年創建了開源的Linux,從此,Linux系統不斷髮展,已經成爲最大的服務器系統軟件了。
Linus雖然創建了Linux,但Linux的壯大是靠全世界熱心的志願者參與的,這麼多人在世界各地爲Linux編寫代碼,那Linux的代碼就必須要有一個管理工具來管理。
事實是,在2002年以前,世界各地的志願者把源代碼文件通過diff的方式發給Linus,然後由Linus本人通過手工方式合併代碼!
到了2002年,Linux系統已經發展了十年了,代碼庫之大讓Linus很難繼續通過手工方式管理了,社區的弟兄們也對這種方式表達了強烈不滿,於是Linus選擇了一個商業的版本控制系統BitKeeper,BitKeeper的東家BitMover公司出於人道主義精神,授權Linux社區免費使用這個版本控制系統。
Linux社區牛人聚集,有一個夥計試圖破解BitKeeper的協議,結果被BitMover公司發現了,於是BitMover公司怒了,要收回Linux社區的免費使用權。這個時候Linus向BitMover公司道個歉,保證以後不再發生這樣的事情。
道歉歸道歉,但實際上linus花了兩週時間自己用C寫了一個分佈式版本控制系統,這就是Git!一個月之內,Linux系統的源碼已經由Git管理了!從些Git分佈式版本軟件就此誕生了,Git迅速成爲最流行的分佈式版本控制系統。
2008年,GitHub網站上線了,它爲開源項目免費提供Git存儲,無數開源項目開始遷移至GitHub,包括jQuery,PHP,Ruby等等,很多很多。
整個的事情就是這樣,如果不是當年BitMover公司威脅Linux社區要收回使用權,現在我們可能就沒有免費而超級好用的Git分佈式版本系統了。
最早Git是在Linux上開發的,很長一段時間內,Git也只能在Linux和Unix系統上跑。不過,慢慢地有人把它移植到了Windows上。現在,Git可以在Linux、Unix、Mac和Windows這幾大平臺上正常運行了
Git的原理
Git的使用流程是
工作區(Working Directory)------->版本庫(Repository)暫緩區------>倉庫
工作區(Working Directory)
就是我們在本機的目錄,就是我們的工作區,比如/root/myproject
[root@git myproject]# pwd
/root/myproject
版本庫(Repository)
工作區有一個隱藏目錄.git,這個.git不算工作區,而是Git的版本庫。
Git的版本庫裏存了很多東西,其中最重要的就是稱爲stage(或者叫index)的暫存區,Git爲我們自動創建了一個分支master,以及指向master的一個指針叫HEAD。
我們把文件往Git版本庫裏添加的時候,要執行兩步:
第一步:git add 把文件添加進去,實際上就是把文件添加到暫存區;
第二步:git commit -m "版本描述信息" 提交到版本庫,實際上就是把暫存區的所有內容提交到倉庫的當前分支
創建Git版本庫時,Git自動爲我們創建了一個master分支,所以git commit就是往master分支上提交更改。
你可以簡單理解爲,需要提交的文件修改通通放到暫存區,然後,一次性提交暫存區的所有修改。
當工作區有文件更改,執行git add時,暫緩區就變成以下這樣了:
git add 命令實際上就是把工作區要提交的內容放到暫存區(Stage),然後,執行git commit就可以一次性把暫存區的所有修改提交到分支
一但執行git commit提交後,那麼工作區就是“乾淨”的:
現在版本庫變成了這樣,暫存區就沒有任何內容了:
以上就是git的流程,沒看明白的同學,可以再多看一次。
安裝Git
這裏我用Centos 7系統來安裝Git
# yum -y install git*
因爲Git是分佈式版本控制系統,所以,上傳版本文件時必須提供:你的名字和Email地址。
使用git,創建本地工作區
# git config --global user.name "liuxiang"
# git config --global user.email "[email protected]"
版本庫又名倉庫,英文名repository,可以理解成一個目錄,這個目錄裏面的所有文件都被Git管理起來,每個文件的修改、刪除,Git都能跟蹤.
第一步:創建一個版本庫,創建一個目錄:
# mkdir myproject
# cd myproject
# pwd
/root/myproject
第二步:git init命令把該目錄初始化爲Git可以管理的倉庫
# git init
Initialized empty Git repository in /root/myproject/.git/
使用tree命令可以查看 /root/myproject/.git/目錄下的樹結構
# tree .git
.git
├── branches
├── COMMIT_EDITMSG
├── config
├── description
├── HEAD
├── hooks
│ ├── applypatch-msg.sample
│ ├── commit-msg.sample
│ ├── post-update.sample
│ ├── pre-applypatch.sample
│ ├── pre-commit.sample
│ ├── prepare-commit-msg.sample
│ ├── pre-push.sample
│ ├── pre-rebase.sample
│ └── update.sample
├── index
├── info
│ └── exclude
├── logs
│ ├── HEAD
│ └── refs
│ ├── heads
│ │ └── master
│ └── remotes
│ └── origin
│ └── master
├── objects
├── ORIG_HEAD
└── refs
├── heads
│ └── master
├── remotes
│ └── origin
│ └── master
└── tags
開始編寫一個README自述文件
# echo "## README file" > README
使用git status 查看當前工作區的狀態
# git status
# On branch master
#
# Initial commit
#
# Untracked files:
# (use "git add <file>..." to include in what will be committed)
#
# README --有一個新的文件,上一名提示可以使用git add <file>添加至暫緩區
nothing added to commit but untracked files present (use "git add" to track)
使用git add 命令 將剛在工作區創建的README添加至暫緩區
# git add README --將README文件添加至暫緩區
# git status --再查看工作區狀態
# On branch master
#
# Initial commit
#
# Changes to be committed:
# (use "git rm --cached <file>..." to unstage)
#
# new file: README --顯示暫緩區裏有一個新的文件README
#
使用git commit -m "描述信息"將暫緩區的文件提交至本地倉庫
# git commit -m "add README"
[master (root-commit) 880705c] add README
1 file changed, 1 insertion(+)
create mode 100644 README
# git status
# On branch master
nothing to commit, working directory clean
文件對比git diff
有時候我們修改了本地的一個文件,但臨時有事走開了,回來的時候忘記了自己做了哪部分的修改。
此時就要用上git diff命令
來模擬以上環境
# echo "## new add text" >> README --往README裏添加內容
# git status --查看狀態
# On branch master
# Changes not staged for commit:
# (use "git add <file>..." to update what will be committed)
# (use "git checkout -- <file>..." to discard changes in working directory)
#
# modified: README --顯示README已經被修改過了
#
no changes added to commit (use "git add" and/or "git commit -a")
# git diff README --使用git diff 命令對比README文件看看和上次提交到倉庫的內容和現在的文件哪些地方被修改過了
diff --git a/README b/README
index 0abae28..2d2eb8f 100644
--- a/README
+++ b/README
@@ -1 +1,2 @@
## README file
+## new add text --這行前面有一個+ 表示是新增的內容
知道自己修改了什麼,接下來就可以放心的提交了
# git add README --提交至暫緩區
# git commit -m "mod README" --提交到本地倉庫
[master 56acaae] mod README
1 file changed, 1 insertion(+)
# git status --再查看狀態時顯示無內容可提交
# On branch master
nothing to commit, working directory clean
版本回退
在實際工作中,我們提交的版本可以有很多,很難記得每次提交了什麼內容,或是什麼時候提交的,這裏就要用上git log來查看提交的版本
# git log
commit 56acaae3bc05603ac012e1cd7c6604a7e2f7fcc1
Author: uplooking <[email protected]>
Date: Fri Dec 8 03:39:14 2017 -0500
mod README
commit 880705c78e9482703e6dfdbc67a16fc7dac133de
Author: uplooking <[email protected]>
Date: Fri Dec 8 03:25:06 2017 -0500
add README
這裏顯示的內容是最近提交的顯示在上面;
這裏看到有兩個版本,時間點也可以看得到,提交人和郵件也可以看得到,第一行的commit 後面的那一連串的字符是commit id是唯一標識符。
在真實環境中我們提交的版本有太多太多,要是這麼看的話,不方便看,此時我們可以加上--pretty=oneline參數:這樣顯示就簡潔多了
# git log --pretty=oneline
56acaae3bc05603ac012e1cd7c6604a7e2f7fcc1 mod README
880705c78e9482703e6dfdbc67a16fc7dac133de add README
現在準備回退版本:
在Git中,首先我們要知道現在是什麼版本,以前有什麼版本,用HEAD表示當前版本,上一個版本就是HEAD^,上上一個版本就是HEAD^^,上幾個版本就寫多少個^符,如果有很多的話也可以寫成HEAD~20。
也可以指定版本的commit id的前4位以上也可以
我們回退到 add README這個版本中,這個版本當時只有一行內容
# git log --pretty=oneline --先列出版本號
56acaae3bc05603ac012e1cd7c6604a7e2f7fcc1 mod README
880705c78e9482703e6dfdbc67a16fc7dac133de add README
# git reset --hard 8807 --回退到commit id開頭爲8807的版本
HEAD is now at 880705c add README
# git log --pretty=oneline --查看當前的版本
880705c78e9482703e6dfdbc67a16fc7dac133de add README
# cat README --查看內容,回退成功
## README file
現在,你回退到了某個版本,回退這之後後悔了,想恢復到新版本怎麼辦?
好在git提供了這麼一個功能,可以看到提交和回退的操作 git reflog命令
# git reflog --列出了所有的commit的操作和reset回退的作品,還顯示了對應的commit id
880705c HEAD@{0}: reset: moving to 8807
56acaae HEAD@{1}: reset: moving to 56acaa
880705c HEAD@{2}: reset: moving to 88070
56acaae HEAD@{3}: commit: mod README ---該條記錄就是最後一次提交到倉庫的commit動作
880705c HEAD@{4}: commit (initial): add README
# git reset --hard 56acaae --輸入最後一次提交的commit id 回退
HEAD is now at 56acaae mod README
# git log --pretty=oneline --查看當前版本,已經恢復到最新版本了
56acaae3bc05603ac012e1cd7c6604a7e2f7fcc1 mod README
880705c78e9482703e6dfdbc67a16fc7dac133de add README
# cat README --驗證下內容,是新後一次提交的內容
## README file
## new add text
撤消與刪除
在真實環境中我們會遇到修改了的文件,提交到了暫存區,想撤銷的。或是刪除倉庫裏不要的文件。
--撤消
第一種情況:修改了工作區的文件,還未提交到暫存區,要撤銷
# echo "## Git checkout" >>README --往README裏添加內容
# cat README --查看README內容
## README file.
## new file text
## 學習git軟件
## Git checkout
[root@git myproject]#
[root@git myproject]# git status --查看狀態,顯示README文件被修改
# On branch master
# Changes not staged for commit:
# (use "git add <file>..." to update what will be committed)
# (use "git checkout -- <file>..." to discard changes in working directory)
#
# modified: README
#
no changes added to commit (use "git add" and/or "git commit -a")
[root@git myproject]#
[root@git myproject]# git checkout -- README --此時如果要撤消修改,則執行該命令
[root@git myproject]#
[root@git myproject]#
[root@git myproject]# git status --再看狀態,顯示沒有文件修改或添加
# On branch master
nothing to commit, working directory clean
[root@git myproject]# cat README --再查看README內容時,內容已經撤消成功
## README file.
## new file text
## 學習git軟件
git checkout -- file 命令中的--很重要,沒有--,就變成了“切換到另一個分支”的命令,後面的分支管理中會講到git checkout命令切分支。
第二種情況:如果你修改了文件,還執行了git add 到暫存區,這時要撤消的話,要執行多一步,如下:
[root@git myproject]# echo "## Git checkout" >>README
[root@git myproject]#
[root@git myproject]# git status
# On branch master
# Changes not staged for commit:
# (use "git add <file>..." to update what will be committed)
# (use "git checkout -- <file>..." to discard changes in working directory)
#
# modified: README
#
no changes added to commit (use "git add" and/or "git commit -a")
[root@git myproject]#
[root@git myproject]# git add .
[root@git myproject]#
[root@git myproject]#
[root@git myproject]# git status --當我們修改一個文件執行了git add後,再看狀態時,會提示執行git reset HEAD <file> 可以撤消到工作區
# On branch master
# Changes to be committed:
# (use "git reset HEAD <file>..." to unstage)
#
# modified: README
#
[root@git myproject]#
[root@git myproject]# git reset HEAD README --把README暫存區修改的內容撤消到工作區
Unstaged changes after reset:
M README
[root@git myproject]# git status --再看狀態時,顯示文件已經到了工作區
# On branch master
# Changes not staged for commit:
# (use "git add <file>..." to update what will be committed)
# (use "git checkout -- <file>..." to discard changes in working directory)
#
# modified: README
#
no changes added to commit (use "git add" and/or "git commit -a")
[root@git myproject]#
[root@git myproject]# git checkout -- README --再執行git checkout -- README把修改的內容從工作區裏撤消
[root@git myproject]#
[root@git myproject]# git status --再看狀態,顯示沒有可提交的內容
# On branch master
nothing to commit, working directory clean
[root@git myproject]# cat README --再看內容,已經恢復到了
## README file.
## new file text
## 學習git軟件
刪除文件
第一種情況:誤刪了工作區的某個文件;
如果誤刪了工作區的某個文件,可以從倉庫裏恢復,實踐如下:
[root@git test]# rm README --刪除文件
rm: remove regular file ‘README’? y
[root@git test]#
[root@git test]# ls --文件已經被刪除
[root@git test]#
[root@git test]# git status --查看狀態,顯示工作區文件README文件被標記爲刪除
# On branch master
# Changes not staged for commit:
# (use "git add/rm <file>..." to update what will be committed)
# (use "git checkout -- <file>..." to discard changes in working directory)
#
# deleted: README
#
no changes added to commit (use "git add" and/or "git commit -a")
[root@git test]#
[root@git test]#
[root@git test]# git checkout -- README --此時執行git checkout -- <file> 可以將刪除的文件撤消
[root@git test]#
[root@git test]# git status
# On branch master
nothing to commit, working directory clean
[root@git test]#
[root@git test]# ls --再查看時文件已經恢復回來
README
[root@git test]#
第二種情況:確認要刪除倉庫的某個文件,git rm命令
實踐如下:
[root@git test]# git rm README --刪除工作區的文件README
rm 'README'
[root@git test]#
[root@git test]# git status --查看狀態,README已經被標識爲delete
# On branch master
# Changes to be committed:
# (use "git reset HEAD <file>..." to unstage)
#
# deleted: README
#
[root@git test]# git commit -m "delete README" --執行提交到倉庫
[master 3cd5280] delete README
1 file changed, 2 deletions(-)
delete mode 100644 README
[root@git test]#
[root@git test]# ls --文件已經被徹底刪除
[root@git test]#
當執行git rm 動作後,突然想撤消怎麼辦? 使用 git stash 恢復
示例:
[root@git Git]# git reset --hard 5e4e356 --恢復README文件回來
HEAD is now at 5e4e356 mod README
[root@git Git]# ls
README
[root@git Git]#
[root@git Git]# git rm README
rm 'README'
[root@git Git]# git status
# On branch master
# Changes to be committed:
# (use "git reset HEAD <file>..." to unstage)
#
# deleted: README
#
[root@git Git]#
[root@git Git]# git stash --執行還原到最後一次
Saved working directory and index state WIP on master: 5e4e356 mod README
HEAD is now at 5e4e356 mod README
[root@git Git]#
[root@git Git]#
[root@git Git]# git status --狀態已經變成沒有可提交的動作
# On branch master
nothing to commit, working directory clean
[root@git Git]# ls --文件已經回來
README
[root@git Git]#
遠程倉庫GitHub
講到遠程倉庫,那就有必要提下另外一個版本控制系統SVN,SVN就是集中式版本控制系統,且只就有集中式版本控制系統。但Git比SVN強大百倍,Git又可以實現集中式,分佈式,本地倉庫,是一個多元化版本控制系統 。後面還更多的Git殺手級的功能。
Git分佈式版本控制系統,同一個Git倉庫,可以分佈到不同的機器上。最開始只有一臺機器有一個原始版本庫,此後,別的機器可以“克隆”這個原始版本庫,而且每臺機器的版本庫其實都是一樣的,並沒有主次之分。
實際工作環境中,是找一臺電腦充當服務器的角色,7 * 24小時開機,個人電腦都從這個“服務器”倉庫克隆一份到自己的電腦上,並且各自把各自的提交推送到服務器倉庫裏,也從服務器倉庫中拉取別人的提交。
當然這裏不講本地環境中的倉庫,我們講官方GitHub代碼託管網站,這是一個免費代碼託管服務,所以,只要註冊一個GitHub賬號,就可以免費獲得Git遠程倉庫。
GitHub官方網站:https://github.com 大家自行註冊。
GitHub註冊成功後,我們還需要進行一些設置,創建ssh-key纔可以將個人電腦中的內容提交到GitHub中來,GitHub爲什麼要ssh-key呢?因爲GitHub需要識別出你推送提交確實是你推送的,而不是別人冒充的,而Git支持SSH協議,所以,GitHub只要知道了你的公鑰,就可以確認只有你自己才能推送。
GitHub允許你添加多個Key。假定你有若干電腦,你一會兒在公司提交,一會兒在家裏提交,只要把每臺電腦的Key都添加到GitHub,就可以在每臺電腦上往GitHub推送了。
友情提示下:在GitHub上免費託管的Git倉庫,任何人都可以看到,且還可以評論,但只有你自己纔可以修改,所以,敏感信息就要慎重了。
第一步:創建ssh-key
[root@git ~]# ssh-keygen -t rsa -C "[email protected]" --把郵箱換成你自己的郵箱,然後一直回車到結束
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:
SHA256:8XkbCTSVxBqZgs3rq7NtHVva+H2tf+HW5Rk4RwmVptk [email protected]
The key's randomart image is:
+---[RSA 2048]----+
| + o*o....|
| . +.+.o. o |
| .o.o * .|
| .o.o + E |
| .S o + o |
| . ...= +.|
| o B. +.B|
| ..o = .. +*|
| o=. .. ++o|
+----[SHA256]-----+
[root@git ~]#
[root@git ~]# ll ~/.ssh/ --查看家目錄下的.ssh目錄下有兩個文件id_rsa私鑰,不可泄露, id_rsa.pub公鑰,對外提供的,可以放心提供給任何人。
total 8
-rw------- 1 root root 1679 Dec 8 08:05 id_rsa
-rw-r--r-- 1 root root 398 Dec 8 08:05 id_rsa.pub
[root@git ~]# cat ~/.ssh/id_rsa.pub --查看公鑰內容,複製,一會有用
ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDt6OBCybnxDBlTIASrEkWWhdq3Eo4umeEJ6LWOdfUmGbu5EqmcB0mtxfRaiXMdB1DP4LFT7oNwGJwWSk5h/zGpXPzc8ONO698dvIJE/MU3buXF2uj28VWZBAMyyuuOQmgzIcZAntE2mCBjhIoE/UH+ourLHP+r1KJiuNVOTADo9MTSTVY9aq/0xm6tAXamtLV+cbOEKO66FYZnp0z0McgTbC9LSPakuUW8px1h4jRGi1dhWqfNJVNZonjfnSwWCbem+a313vVK9NU2cehOblv/KR1LVhhAIqi4EPnhV+ff2TVpxzI025NaHvr9a7pi04q6bLNFAEg4RHk8v2xJdWKp [email protected]
第二步:登陸GitHub網站:
點擊頭像 --> 點擊settings ---> 點向SSH and GPG keys --->New SSH key ---> Title 隨意填 ---> Key 就是上面的id_rsa.pub的內容複製上來 ---> Add SSH key
添加完成後的樣子:
第三步:接下創建遠程倉庫
點擊頭像旁邊的 + 號 ---> New repository ---> Repository name 處填寫倉庫名稱(自己命名,我這寫的是learngit) ---> 點擊Create repository 創建倉庫
創建倉庫完成的樣子:
GitHub上剛創建的倉庫learngit倉庫是空的,GitHub在界面上提示,可以從這個倉庫克隆出新的倉庫,也可以把一個已有的本地倉庫與之關聯,然後,把本地倉庫的內容推送到GitHub倉庫。
根據GitHub的提示,在本地的learngit倉庫下運行以下兩條命令:
第一條:git remote add origin https://github.com/liuyjishg/learngit.git
注意:把上面的liuyjishg替換成你自己的GitHub賬戶名,learngit替換爲你自己在GitHub上創建的倉庫名,否則,你在本地關聯的就是我的遠程庫,關聯沒有問題,但是你以後推送是推不上去的,因爲你的SSH Key公鑰不在我的賬戶列表中。
[root@git myproject]# git remote add origin https://github.com/liuyjishg/learngit.git
[root@git myproject]#
添加後,遠程庫的名字就是origin,這是Git默認的叫法,也可以改成別的,但是origin這個名字一看就知道是遠程庫。
第二條:git push -u origin master
把本地庫的內容推送到遠程GitHub倉庫,用git push命令,實際上是把當前分支master推送到遠程。
由於遠程庫是空的,我們第一次推送master分支時,加上了-u參數,Git不但會把本地的master分支內容推送的遠程新的master分支,還會把本地的master分支和遠程的master分支關聯起來,在以後的推送或者拉取時就可以簡化命令:git push
[root@git myproject]# git push -u origin master
Username for 'https://github.com': liuyjishg ---要求你輸入GitHub官方的帳號
Password for 'https://[email protected]': ---要求你輸入GitHub官方帳號對應的密碼
Counting objects: 11, done.
Compressing objects: 100% (4/4), done.
Writing objects: 100% (11/11), 843 bytes | 0 bytes/s, done.
Total 11 (delta 0), reused 0 (delta 0)
To https://github.com/liuyjishg/learngit.git
* [new branch] master -> master
Branch master set up to track remote branch master from origin.
推送成功後,可以在GitHub頁面中看到遠程庫的內容已經和本地是一樣的:
前面講到的是在GitHub創建倉庫,且把本地的倉庫關聯到GitHub中的公共倉庫。
接下來講從遠程倉庫中克隆到本地倉庫中來
現在,假設我們從零開發,那麼最好的方式是先創建遠程庫,然後從遠程庫克隆。
登陸GitHub,創建一個新的倉庫,名字叫project,
勾選Initialize this repository with a README,
這樣GitHub會自動爲我們創建一個README.md文件。
創建完畢後,可以看到README.md文件:
接下來用命令git clone將GitHub的遠程倉庫克隆到本地來
[root@git ~]# git clone https://github.com/liuyjishg/project.git
Cloning into 'project'...
remote: Counting objects: 3, done.
remote: Total 3 (delta 0), reused 0 (delta 0), pack-reused 0
Unpacking objects: 100% (3/3), done.
[root@git ~]#
[root@git ~]#
[root@git ~]# ls
anaconda-ks.cfg project
[root@git ~]# cd project/
[root@git project]# ls
README.md
[root@git project]#
注意把Git庫的用戶名liuyjishg換成你自己的,然後進入project目錄看看,已經有README.md文件了。
Git的版本分支
Git的分支是與衆不同的,無論創建、切換和刪除分支,Git在1秒鐘之內就能完成!無論你的版本庫有多少文件。
Git裏默認是有一個主分支叫master。
建議大家使用分支:
常用命令如下:
查看分支:git branch
創建分支:git branch <name>
切換分支:git checkout <name>
創建+切換分支:git checkout -b <name>
合併某分支到當前分支:git merge <name>
刪除分支:git branch -d <name>
[root@git Git]# git checkout -b aa --創建aa分支,然後切換到dev分支:
Switched to a new branch 'aa'
[root@git Git]# git branch --查看當前分支,*號代表當前所在的分支
* aa
master
[root@git Git]#
[root@git Git]# echo aa >> README --往README文件裏添加內容
[root@git Git]#
[root@git Git]#
[root@git Git]# git add .
[root@git Git]# git commit -m "aa mod README"
[aa e86a117] aa mod README
1 file changed, 4 deletions(-)
[root@git Git]#
[root@git Git]# git checkout master --切換到主分支
Switched to branch 'master'
現在,我們把aa分支的工作內容合併到master主分支上:
[root@git Git]# git merge aa
Updating 936e9d0..70373b6
Fast-forward
README | 1 +
1 file changed, 1 insertion(+)
[root@git Git]# cat README --再查看內容,此時aa分支上的內容已經合併到master主分支了
test
aa
test,test
aa
注意到上面的Fast-forward信息,Git告訴我們,這次合併是“快進模式”,也就是直接把master指向aa的當前提交,所以合併速度非常快。
當然,也不是每次合併都能Fast-forward,我們後面會講其他方式的合併。
合併完成後,就可以放心地刪除dev分支了:
[root@git Git]# git branch -d aa
Deleted branch aa (was 70373b6).
[root@git Git]#
[root@git Git]# git branch
* master
創建、合併和刪除分支非常快,所以Git鼓勵你使用分支完成某個任務,合併後再刪掉分支,這和直接在master分支上工作效果是一樣的,但過程更安全
合併時提示衝突
我們經常會遇到這種情況,主分支修改了配置文件,其它分支也修改配置文件,合併時會提示衝突。
示例:
[root@git Git]# git checkout -b feature1 --創建一個新的分支feature1
Switched to a new branch 'feature1'
往README文件裏追加內容
[root@git Git]# echo "test simple" >> README
[root@git Git]#
[root@git Git]# git add ./README
[root@git Git]#
[root@git Git]# git commit -m "test simple"
[feature1 594482c] test simple
1 file changed, 1 insertion(+)
切換到master主分支,並也往README文件裏追加內容
[root@git Git]# git checkout master
Switched to branch 'master'
[root@git Git]#
[root@git Git]# echo "aa simple" >> README
[root@git Git]#
[root@git Git]# git add ./README
[root@git Git]#
[root@git Git]# git commit -m "aa simple"
[master 34db356] aa simple
1 file changed, 1 insertion(+)
現在master主分支和feature1分支各自都分別有新的提交,變成了這樣:
這種情況,Git合併時會提示README文件衝突,文件內容也會有相應提示
[root@git Git]# git merge feature1
Auto-merging README
CONFLICT (content): Merge conflict in README --這裏已經提示我們README文件合併時內容衝突
Automatic merge failed; fix conflicts and then commit the result.
這裏我們來看README文件
[root@git Git]# cat README
test
aa
test,test
aa
<<<<<<< HEAD
aa simple
=======
test simple
>>>>>>> feature1
Git用<<<<<<<HEAD來表示是主分支修改的內容,=======表示分隔符,>>>>>>>表示是feature1分支修改的內容。
這種情況我們需要人工重新修正README文件,然後再提交才能解決衝突
[root@git Git]# vi README
test
aa
test,test
aa
test simple
[root@git Git]#
[root@git Git]# git add ./README
[root@git Git]#
[root@git Git]# git commit -m "conflict fixed"
[master ecea0d4] conflict fixed --提交過後,Git會提示你衝突解決
當衝突解決後,master主分支和feature1分支就變成了下面這樣:
我們可以用 git log 也可以看到分支的合併情況:
[root@git Git]# git log --graph --pretty=oneline --abbrev-commit
* ecea0d4 conflict fixed
|\
| * 594482c test simple
* | 34db356 aa simple
|/
* 2452222 add test
合併成功,解決衝突後,就可以將feature1分支刪除了
[root@git Git]# git branch
feature1
* master
[root@git Git]# git branch -d feature1
Deleted branch feature1 (was 594482c).
[root@git Git]#
[root@git Git]#
[root@git Git]# git branch
* master
[root@git Git]#
禁用Fast forward模式管理
合併分支時,Git默認使用用Fast forward模式,刪除分支後,分支信息會永久刪除。
如果要強制禁用Fast forward模式,Git就會在merge時生成一個新的commit,從分支歷史上就可以看出分支信息。
示例:
創建並切換dev分支,往README文件裏追加新的內容,並提交一個新的commit
[root@git Git]# git checkout -b dev
Switched to a new branch 'dev'
[root@git Git]#
[root@git Git]# echo "test branch" >> README
[root@git Git]#
[root@git Git]# git add ./README
[root@git Git]#
[root@git Git]# git commit -m "add merge"
[dev 93df64b] add merge
1 file changed, 1 insertion(+)
現在,我們切回master主分支,合併dev分支,使用--no-ff 參數來禁用Fast forward模式
[root@git Git]# git checkout master
Switched to branch 'master'
[root@git Git]#
[root@git Git]# git merge --no-ff -m "merge with no-ff" dev --當禁用Fast forward模式時,合併就是一個新的commit,所以加上-m參數
Merge made by the 'recursive' strategy.
README | 1 +
1 file changed, 1 insertion(+)
合併後,我們用git log看看分支歷史:
[root@git Git]# git log --graph --pretty=oneline --abbrev-commit
* 7668b3d merge with no-ff
|\
| * 93df64b add merge
|/
* ecea0d4 conflict fixed
可以看到,不使用Fast forward模式,merge後就像這樣
分支策略
在實際開發中,我們應該按照幾個基本原則進行分支管理:
首先,master分支應該是非常穩定的,也就是僅用來發布新版本,平時不能在上面幹活;
那在哪幹活呢?幹活都在dev分支上,也就是說,dev分支是不穩定的,到某個時候,比如1.0版本發佈時,再把dev分支合併到master上,在master分支發佈1.0版本;
你和你的小夥伴們每個人都在dev分支上幹活,每個人都有自己的分支,時不時地往dev分支上合併就可以了。
所以,團隊合作的分支看起來就像這樣:
Git分支十分強大,在團隊開發中應該充分應用。
合併分支時,加上--no-ff參數就可以用普通模式合併,合併後的歷史有分支,能看出來曾經做過合併,而fast forward合併就看不出來曾經做過合併
Bug臨時分支
例如:你正在開發中,突然收到一個問題工單,說要解決主線上的一個BUG,這時我們就可以先保存當前正在寫的代碼,再使用Git創建一個臨時的分支來修復這個BUG,然後再合併,恢復原來正在寫的代碼工作。
示例:修復一個代號bug-issue-50的bug的任務,這裏我們創建一個分支bug-issue-50來修復它,但是,當前正在dev上進行的工作還沒有提交,此時需要將當前的工作保存,然後才能修復:
先模擬正在工作的場景:
[root@git Git]# git branch
* dev
master
[root@git Git]# echo "測試臨時分支" >> README
[root@git Git]# echo "work Hello" > hello.py
[root@git Git]# git add ./*
[root@git Git]# git status
# On branch dev
# Changes to be committed:
# (use "git reset HEAD <file>..." to unstage)
#
# modified: README
# new file: hello.py
#
[root@git Git]#
上面模擬正在工作的場景,我們修改了README文件,添加了一個新的hello.py文件,因未完成,還不能提交,所以只做了add。此時你收到問題工單必須馬上解決Bug,怎麼辦?
這時Git的stash功能可以派上用場了,stash是把當前工作內容 "保存" 起來,等以後恢復現場後繼續工作:
[root@git Git]# git stash
Saved working directory and index state WIP on dev: f879521 分支管理
HEAD is now at f879521 分支管理
[root@git Git]# git stash list --使用git stash list 可以看到剛剛保存的工作區名稱
stash@{0}: WIP on dev: f879521 分支管理
[root@git Git]# git status --
# On branch dev
nothing to commit, working directory clean