細說GIT分布式版本控制器
一.Git介紹
Git是目前世界上最先進的分布式版本控制器。Svn CVS
版本控制器:就是用來追溯自己書寫的代碼的記錄信息。好處:可以非常方便的記錄何時何地何人操作了哪些代碼。
什麽是分布式版本控制器?
集中式:對於集中式的版本控制器,需要搭建一個中央服務器,然後在這個中央服務器裏面作為代碼的倉庫。
分布式:就是每個用戶的電腦都是一個獨立的倉庫,可以記錄代碼的變化,即使不聯網,完全也可以自己獨立開發。
二.Git安裝
https://git-for-windows.github.io/
安裝完後還需要最後一步設置,在命令行輸入:告訴你是誰
$ git config --global user.name
$ git config --global user.email
"[email protected]"
三.Git操作
1.創建倉庫
1).創建倉庫目錄
2).通過git init命令把這個目錄變成Git可以管理的倉庫
註意:Git倉庫創建好後,會在該文件存在一個.git的目錄,這個目錄是Git來跟蹤管理版本庫的,沒事千萬不要手動修改這個目錄裏面的文件,不然改亂了,就把Git倉庫給破壞了。
註意:如果你沒有看到.git目錄,那是因為這個目錄默認是隱藏的,用ls -ah命令就可以看見。
小結:git init 初始化倉庫
2.添加文件到版本庫
把文件添加到版本分兩步,首先將文件添加到暫存區,然後再提交到版本庫
1).創建文件並把文件添加到暫存區(git add)
git add . 表示將當前目錄下的所有文件都添加到暫存區
2).將文件從暫存區提交到版本庫
-m 表示本次提交的描述
小結:
git add xxx 添加單個文件到暫存區
git add . 將當前文件夾下的所有文件都添加到暫存區
git commit -m ‘xxx’ 將文件提交到版本庫 -m 表示描述
3.修改文件查看狀態與不同
此時我們將文件內容修改
1).查看狀態
git status 命可以讓我們時刻掌握倉庫當前的狀態,上面的命令告訴我們,readme.txt被修改過了,但還沒有準備提交的修改。
2).查看此次修改的文件與之前的不同
git diff顧名思義就是查看difference,可以從上面的命令輸出看到,我們添加了一行hello單詞
知道了對readme.txt作了什麽修改後,再把它提交到倉庫就放心多了,提前命令與之前相同。
小結:
git status 查看倉庫狀態
git diff 查看修改後文件與之前的不同。
4.版本回退
如果此時我們因為某種原因想回退到之前的某個版本該怎麽辦?
1).首先通過git log 查看之前的提交日誌
如果嫌這樣看著太亂的話可以試試加上—pretty=oneline參數
其中85d58ac82a21e8c587da900edff9a21566b1d708和78c5b099e84439c9b640a5028ee1fcb224432576 代表的是commit id(版本號),
它是通過SHA1計算出來的一個非常大的數字,用十六進制表示,而且你的commit id 和我的肯定不一樣,實際以你自己的為準。
2).回退到之前某個版本
知道了之前的提交日誌那如何回退到之前的版本呢?
首先,Git必須知道當前版本是哪個版本,在Git中,用HEAD表示當前版本,也就是最新的提交85d58ac82a21e8c587da900edff9a21566b1d708(註意我的提交ID和你的肯定不一樣),上一個版本就是HEAD^,上上一個版本就是HEAD^^,當然往上100個版本寫100個^比較容易數不過來,所以寫成HEAD~100。
我們現在回退到上一版本使用命令:git reset –hard HEAD^
查看此時的文件:cat readme.txt
通過查看文件發現確實是回到了上一個版本,那麽我們再通過git log查看此時版本庫的狀態。
最新的“修改readme.txt文件”這個版本已經不見了,就相當於我們坐了時光機一樣,穿越到了之前的版本,
3).回退到未來某個版本
現在肯定又有人想我穿越到了過去,那我再怎麽穿越回來呢?
在Git中,總是有後悔藥可以吃的。當你用$ git reset --hard HEAD^回退到”添加readme.txt文件”版本時,再想恢復到”修改readme.txt文件”,就必須找到”修改readme.txt文件”的commit id。Git提供了一個命令git reflog用來記錄你的每一次命令:
然後通過git reset –hard 85d58ac回退到”修改readme.txt文件”版本。
我們再通過查看文件內容,和歷史提交記錄發現又回到了”修改readme.txt文件”的版本
小結:
git log 查看提交歷史記錄,以便確認回退到歷史哪個版本。
git reflog 查看命令歷史,以便確定要回到未來哪個版本
git reset –hard commit_id 版本之間的穿梭回退
5.工作區與暫存區
1).工作區:
就是你在電腦裏能看到的目錄,比如我的learngit
文件夾就是一個工作區:
2).暫存區:
是邏輯上的一個概念,是看不到的。
3).版本庫:
工作區有一個隱藏目錄.git
,這個不算工作區,而是Git的版本庫。
Git的版本庫裏存了很多東西,其中最重要的就是稱為stage(或者叫index)的暫存區,還有Git為我們自動創建的第一個分支master
,以及指向master
的一個指針叫HEAD
。
分支和HEAD
的概念我們以後再講。
前面講了我們把文件往Git版本庫裏添加的時候,是分兩步執行的:
第一步是用git add
把文件添加進去,實際上就是把文件修改添加到暫存區;
第二步是用git commit
提交更改,實際上就是把暫存區的所有內容提交到當前分支。
因為我們創建Git版本庫時,Git自動為我們創建了唯一一個master
分支,所以,現在,git commit
就是往master
分支上提交更改。
你可以簡單理解為,需要提交的文件修改通通放到暫存區,然後,一次性提交暫存區的所有修改。
俗話說,實踐出真知。現在,我們再練習一遍,先對readme.txt
做個修改,比如加上一行內容:
然後,在工作區新增一個LICENSE
文本文件(內容隨便寫)。
先用git status
查看一下狀態:
Git非常清楚地告訴我們,readme.txt
被修改了,而LICENSE
還從來沒有被添加過,所以它的狀態是Untracked
。
現在,使用兩次命令git add
,把readme.txt
和LICENSE
都添加後,用git status
再查看一下:
現在,暫存區的狀態就變成這樣了:
所以,git add
命令實際上就是把要提交的所有修改放到暫存區(Stage),然後,執行git commit
就可以一次性把暫存區的所有修改提交到分支。
一旦提交後,如果你又沒有對工作區做任何修改,那麽工作區就是“幹凈”的:
現在版本庫變成了這樣,暫存區就沒有任何內容了:
小結:
工作區是指我們本地能夠目錄,暫存區是邏輯上的定義,我們想提交某個文件時,需要將工作區的文件放入到暫存區(git add),然後再提交。
6.管理修改
場景:比如說你修改readme.txt文件,並且git add 了,隨後你又修改了readme.txt文件,但是此時你沒有git add,直接git commit的了,這種情況下,你的第二次修改將不會被提交。
原因是你第二次修改沒有放入暫存區,而git commit 是將暫存區的內容提交到版本庫的。
下面實際操作一下:
1)第一次修改readme.txt文件:添加一行first update
2)git add 此次的修改
3)第二次修改readme.txt文件,添加一行second update
4)直接git commit 提交
發現第二次的修改沒有被提交。驗證了上面的結論,只有在暫存區的數據才能被提交。所以要想被提交必須先將文件放入到暫存區,然後再提交
我們在修改了某些文件時,也可以直接使用git commit -a -m ‘xxxx’ 提交。-a就相當於git add .操作,將當前目錄下所有修改了的文件放入暫存區。
小結:
在暫存區的數據才能被提交到版本庫。
修改了某些文件時,也可以直接使用git commit -a -m ‘xxxx’ 提交
7.撤銷修改
場景1:修改了工作區的某個文件內容如何丟棄修改?
場景2:不但修改了工作區的某個文件內容,而且還添加到了暫存區,如何丟棄修改?
場景3:不但添加到了暫存區而且還提交到了版本庫,如何丟棄修改?
場景1:修改了工作區的某個文件內容如何丟棄修改?
1.修改readme.txt文件 添加一行working undo
2.git status 查看狀態
你可以發現git會提示你,git checkout – file 可以丟棄工作區的修改
3.git checkout – file 丟棄修改
場景2:不但修改了工作區的某個文件內容,而且還添加到了暫存區,如何丟棄修改?
1.修改readme.txt文件 添加一行stage undo
2.git status查看狀態
Git同樣告訴我們,用命令git reset HEAD file可以把暫存區的修改撤銷掉(unstage),重新放回工作區.
3.git reset HEAD file 撤銷暫存區的修改,重放回工作區。
此時修改的文件已經被放回到工作區。
4.從工作區中撤銷本次修改
此時修改內容已被丟棄。
場景3:不但添加到了暫存區而且還提交到了版本庫,如何丟棄修改?
這種情況就需要參考版本回退這一節了,回退到上一個版本
- 修改readme.txt文件,添加一行repository undo
- 提交到版本庫
- 回退到上一個版本
小結:
對於只在工作區修改了文件內容,撤銷此次修改使用 git checkout – file撤銷
對於提交到了暫存區的文件內容修改,撤銷此次修改需要分兩步,第一步:git reset HEAD file 重放回工作區,第二步:git checkout – file 從工作區中撤銷。
對於已經提交到版本庫的文件內容修改,撤銷需要回退到上一個版本,使用git reset –hard commit_id.
8.刪除文件
如何將已經提交到版本庫的文件刪除?
- 添加文件到版本庫
- 從版本庫中刪除
小結:
將文件從版本庫刪除也是分為兩步:
第一步:git rm file 從版本庫刪除
第二步:git commit -m ‘xxx’ 提交本次的操作
四.遠程倉庫
1.添加遠程倉庫
以Github為例
1).在github上創建倉庫
創建完成之後
倉庫創建完成之後,github會提示我們從這個倉庫裏克隆出新的倉庫,或者創建新的倉庫與之個管理,或者推送已存在的倉庫與之關聯。
2).把本地的倉庫與github倉庫關聯。
origin 是遠程庫的名字,github的默認叫法
3).把本地倉庫的內容推送到github倉庫
-u 的意思是如果當前分支與多個主機存在追蹤關系,則可以使用-u選項指定一個默認主機,這樣後面就可以不加任何參數使用Git push。
推送完成後,github上就有了你提交的內容
從現在起,只要本地作了提交,就可以通過命令:
git push origin master
把本地master分支的最新修改推送至github上。
小結:
git remote add origin xxx關聯一個遠程庫
git push -u origin master 第一次將master分支的所有內容推送到遠程庫
git push origin master 上面命令第一次推送之後,以後的每次本地提交,使用不帶-u 參數的即可。
2克隆遠程倉庫
上面講了如何將本地倉庫與遠程倉庫關聯,還有一種情況就是將遠程倉庫克隆到本地
此時我將倉庫克隆到了E盤
這樣你的E盤就會出現你克隆的那個倉庫。
小結:
要克隆一個倉庫,首先你必須要知道倉庫的地址,然後使用git clone命令克隆
五.分支管理
分支:是指在開發主線上又開辟了一條線,這樣能在不影響主線的同時繼續工作
1. 創建與合並分支
在版本回退裏,你已經知道,每次提交,Git都把它們串成一條時間線,這條時間線就是一個分支。截止到目前,只有一條時間線,在Git裏,這個分支叫主分支,即master分支。HEAD嚴格來說不是指向提交,而是指向master,master才是指向提交的,所以,HEAD指向的就是當前分支。
一開始的時候,master分支是一條線,Git用master指向最新的提交,再用HEAD指向master,就能確定當前分支,以及當前分支的提交點:
每次提交,master分支都會向前移動一步,這樣,隨著你不斷提交,master分支的線也越來越長:
當我們創建新的分支,例如dev時,Git新建了一個指針叫dev,指向master相同的提交,再把HEAD指向dev,就表示當前分支在dev上:
你看,Git創建一個分支很快,因為除了增加一個dev指針,改改HEAD的指向,工作區的文件都沒有任何變化!
不過,從現在開始,對工作區的修改和提交就是針對dev分支了,比如新提交一次後,dev指針往前移動一步,而master指針不變:
假如我們在dev上的工作完成了,就可以把dev合並到master上。Git怎麽合並呢?最簡單的方法,就是直接把master指向dev的當前提交,就完成了合並:
所以Git合並分支也很快!就改改指針,工作區內容也不變!
合並完分支後,甚至可以刪除dev分支。刪除dev分支就是把dev指針給刪掉,刪掉後,我們就剩下了一條master分支:
實戰:
1).創建並切換分支
git checkout命令加上-b參數表示創建並切換,相當於以下兩條命令:
git branch dev
git checkout dev
2).查看當前分支
git branch命令會列出所有分支,當前分支前面會標一個*號。
3).修改文件內容提交
4).切換回主分支
切換回master分支後,再查看一個readme.txt文件,剛才添加的內容不見了!因為那個提交是在dev分支上,而master分支此刻的提交點並沒有變:
5).將dev分支合並到master分支
git merge命令用於合並指定分支到當前分支。合並後,再查看readme.txt的內容,就可以看到,和dev分支的最新提交是完全一樣的。
註意到上面的Fast-forward信息,Git告訴我們,這次合並是“快進模式”,也就是直接把master指向dev的當前提交,所以合並速度非常快。
當然,也不是每次合並都能Fast-forward,我們後面會講其他方式的合並。
合並完成後,就可以放心地刪除dev分支了:
6).刪除dev分支
刪除後,再看branch,就只剩下master分支了。
小結:
查看分支:git branch
創建分支:git branch <name>
切換分支:git checkout <name>
創建+切換分支:git checkout -b <name>
合並某分支到當前分支:git merge <name>
刪除分支:git branch -d <name>
2. 解決沖突
場景:你新建並切換到了分支feature1,然後修改了readme.txt文件並提交。隨後你又切換到了master分支,同樣修改了readme.txt文件並提交
這時如果你要合並feature1分支時,即會產生沖突。
模擬並解決:
1).創建並切換分支feature1,修改readme.txt文件,最後提交
2).切換到主分支,修改readme.txt文件,並提交
現在,master分支和feature1分支各自都分別有新的提交,變成了這樣:
3).合並分支
這是git告訴我們 readme.txt文件產生沖突,必須手動解決沖突後再提交
git status 也告訴我們沖突的文件。
4).查看沖突文件
Git用<<<<<<<,=======,>>>>>>>標記出不同分支的內容,我們修改如下後保存
5).修改文件,手動解決沖突
6).查看分支合並情況
7).刪除分支
擴展:
git merge xxx 默認是使用fast-forward,fast-forward方式就是當條件允許的時候,git直接把HEAD指針指向合並分支的頭,完成合並。屬於“快進方式”,不過這種情況如果刪除分支,則會丟失分支信息。因為在這個過程中沒有創建commit。
git merge --no-ff xxx 其中—no-ff指的是強行關閉fast-forward方式,使得合並分支後能夠保留分支的commit的歷史記錄。
小結:
當Git無法自動合並分支時,就必須首先解決沖突。解決沖突後,再提交,合並完成。
用git log --graph命令可以看到分支合並圖。
3. Bug分支
bug在軟件開發中就像家常便飯一樣,有了bug就需要修復,而git又特別提倡使用分支,所以每個bug都可以通過創建一個臨時分支來修復,修復後,合並分支,然後再刪除臨時分支。
場景:當你接到線上環境代號為007的bug時,很自然的會去創建一個bug分支,分支名為bug007,但是,此時你正在dev分支上進行工作還沒有提交,並不是你不想提交,而是你的工作還沒完成還沒法提交,但是現在這個bug又非常緊急,要求你盡快解決,這時我們該怎麽辦?
模擬並解決:
1).創建bug007分支
2).模擬在dev上正在工作還沒有提交
3).git提供了一個stash功能,可以把當前工作現場“儲藏”起來,等以後恢復現場後繼續工作
使用git stash將當前dev正在開發的工作儲藏起來
再通過git status查看發現工作區現在是幹凈的了。
此時我們就可以切換到bug分支進行bug修復
4).切換到bug007分支修復bug
此時bug已經修改,那麽我們就可以很放心的合並到主分支並將bug007分支刪掉。
因為bug007是在主分支上建立的,所以此時需要切換到主分支上刪除
5).合並並刪除bug007分支
6).之前為了修改bug,我們將dev的內容儲藏起來了,那麽現在我們怎麽恢復呢?
首先通過git stash list 查看
我們發現之前的工作現場還在。
那我們可以通過git stash pop來恢復現場
7).恢復工作現場
再git stash list 發現已經沒有內容了。
小結:
修復bug時,我們會通過創建新的bug分支進行修復,然後合並,最後刪除;
當手頭工作沒有完成時,先把工作現場git stash一下,然後去修復bug,修復後,再git stash pop,回到工作現場。
4.Feature分支
軟件開發中,總有無窮無盡的新的功能要不斷添加進來。
添加一個新功能時,你肯定不希望因為一些實驗性質的代碼,把主分支搞亂了,所以,每添加一個新功能,最好新建一個feature分支,在上面開發,完成後,合並,最後,刪除該feature分支。
現在你收到了一個新需求,要求你完成一個書籍管理的功能,
於是準備開發創建feature分支
半天後你開發完畢:
切換回dev分支準備合並
如果一切順利的話,合並刪除分支。
但是就在這時,接到上級命令說這個功能必須取消。
於是我們使用git branch -d 命令刪除這個feature分支
這時git會提示我們,feature_book沒有被合並,如果真的要強行刪除使用git branck -D 命令來刪除
刪除成功
小結:
開發一個新feature,最好新建一個分支;
如果要丟棄一個沒有被合並過的分支,可以通過git branch -D <name>強行刪除。
細說GIT分布式版本控制器