小丁帶你走進git的世界二-工作區暫存區分支
小丁帶你走進git的世界二-工作區暫存區分支
一、Git基本工作流程
1.初始化一個倉庫
git init
git clone
git倉庫分為兩種情況:
第一種是在現有項目或目錄下導入所有文件到 Git 中; 第二種是從一個服務器克隆一個現有的 Git 倉庫
git init –bare git 創建一個裸倉庫
2.git倉庫有三個區域
- 工作區working directory日常編輯代碼的地方
- 歷史倉庫history repository是commit指向的一個樹形結構
- 暫存區Staging area相當于是工作區與歷史提交中間的緩存,它代表著是你要提交代碼的一個工作狀態,它維護的是一個虛擬的樹形結構
講述完了git的區域,接下來講解下git文件的狀態,其實git的狀態無外乎兩種:已跟蹤和未跟蹤,已跟蹤的文件說明的是被納入版本控制的文件,在上一次快照中有它們的記錄,在工作一段時間后,它們的狀態可能處于未修改,已修改或已放入暫存區。 工作目錄中除已跟蹤文件以外的所有其它文件都屬于未跟蹤文件,它們既不存在于上次快照的記錄中,也沒有放入暫存區。 初次克隆某個倉庫的時候,工作目錄中的所有文件都屬于已跟蹤文件,并處于未修改狀態。
講解下文件變化周期,比如我們在項目中添加了一個文件a,這時候文件a處于的狀態則是Untracked未跟蹤狀態,當我們git add的時候這時候就會將文件放到暫存區中這時候狀態變為Stage,當我們commit這個暫存區的文件后這個文件就變成未修改狀態,因為沒有進行修改過了相對于版本控制中的文件,當我們修改了a文件后這時候從unmodified變成modified(修改狀態),這是再git add的時候就會將修改的文件變成stage,放入暫存區用于等待commit。
接下來就詳細講解下這些命令:
往暫存區里面添加東西使用如下git指令
git add
從暫存區提交到歷史記錄使用如下指令
git commit
查看工作區和暫存區之間的區別,來確保提交時我們所需要的呢?
git status
從暫存區里面刪除內容
git rm
工作區內重命名文件或者移動文件,然后再把他們添加到暫存區
git mv
確保工作區內里面不需要的文件不被添加進去添加到暫存區和歷史。
.gitignore
實例講解:
新建一個倉庫
git init git_init
cd git_init
touch a //新建a文件
git add a //將a添加到暫存區
git status會顯示我們要提交的內容a文件,這時候a兩個文件都是Changes to be committed也就是待commit的狀態。
$ git status On branch master Changes to be committed: (use "git reset HEAD <file>..." to unstage) new file: a
git commit -m “initial commit”//提交a文件到版本控制中。
touch b新建文件b
git add b //將b放入暫存區中
$ git status On branch master Changes to be committed: (use "git reset HEAD <file>..." to unstage) new file: b
編輯一下a文件,這時候在調用git status
$ git status On branch master Changes to be committed: (use "git reset HEAD <file>..." to unstage) modified: a new file: bb
這時候a文件是出于修改狀態,那么如果我們在進行修改a文件時候,在調用下git status會發生什么奇怪的事呢?let’s go。
$ git status On branch master Changes to be committed: (use "git reset HEAD <file>..." to unstage) modified: a new file: bb 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: a
這時候我們會發現兩個modified a,這是為什么呢?但是兩個文件分別存在暫存區和非暫存區,實際上暫存區內只保存了git add的版本,而最新修改的一份沒有提交到暫存區內還在工作區域中,所以我們會看到兩個,如果這時候調用git commit的話就只會講已經在暫存區的文件存入到版本控制中,而最新修改的那一次記錄沒有被提交。
當然這里還提供了跳過暫存區的方法就是git commit –a –m“注釋內容”可以直接跳過暫存區直接提交到記錄里面去。
將a移除整個項目
git rm a
清楚緩存中的內容,也就是已經暫存的文件
git rm a –cached
給文件重命名將a文件名換成c
git mv a c
二、Git暫存區
假設工作區里面有這么一些文件如下圖所示:
在第一次add都還沒有進行的時候,暫存區還沒有被創建出來,當我們add的時候會創建一個暫存區出來(文件分開add產生兩個數據對象),如下圖所示:
.git目錄下面多了一個index的文件,那么這個index文件就是我們所說的暫存區的文件,那么每一條索引都是hash值表示以及它對應的文件名。每個索引還包含了他的文件模式權限還有status number來表示他的合并狀態、時間戳等。每一個索引都是跟對象庫的文件是對應的。比如說file.txt這里對應的是標號1,那么file2.txt對應的就是標號2(上圖所示標號),這個index里面除了維護了索引之外還維護了提前計算好的tree對象的內容,也就是我們的頂層的目錄tree以及folder的tree對象內容,當我們提交的時候他可以迅速的根據我們已經計算好的內容生成一個tree對象,然后添加到歷史記錄里面。
當我們修改了file2.txt的時候,這時候git add到暫存區時,發現對象庫里面新創了一個對象,那么暫存區的這條索引就被指向新對象的索引替換掉。這時候git暫存區又從新計算了下頂層目錄tree對象的一個內容。當我們提交的時候我們直接使用已經計算好的內容創建好一個新的tree對象,生成一個commit對象將master分支上面的HEAD指針指向當前commit對象上去。如下圖所示:
這里的標號8指向的就是新生成的commit對象。
三、Git本地分支與合并
1.創建分支git branch
2.給固定的commit做標記 git tag
3.分支之間進行切換 git checkout
4.切換分支之前保存本地修改 git stash
5.合并分支 git merge
例子創建一個分支名字叫test并切換到當前的分支上去。
這時候分支的名稱不再是master而是改變成為test分支。編輯下a文件用test分支進行提交
切換到master分支上面我們看一下a的內容
并沒有test分支在a中添加的內容這時候我們切換分支時會使用我們分支上最新的一個提交來還原我們的暫存區和工作區內容,那么可以讓我們在不同的分支之間獨立的做自己的工作。
git log –oneline –decorate –graph –all
我們可以看到提交的記錄或提交這些提交的引用
給第一個提交加一個名稱tag
git tag “v0” f6699b3
在查看下git log –oneline –decorate –graph –all
多了一個tag:v0使用git show v0查看下內容,這是打的tag是一個輕量級的只是一個固定的引用。
而使用git tag –a的方式進行添加時會是一個tag對象,他有tag的屬性,包括提交人時間等信息。
Tag指向了一個提交commit
Tag也可以使用checkout進行操作
但是它當前的沒有指向一個分支而是指向了一個commit,這時候就會出現一個問題就是我們切換分支的時候這一部分內容就有可能被遺棄掉,也就是說head引用直接指向了一個commit而不是一個分支名,checkout提供了針對當前commit新建一個分支的方法并切換到當前分支。
git checkout -b “ttt_v0”
接下來切換master分支
這時候會用的git stash進行保存工作區,因為我們切換checkout的時候會覆蓋掉當前的內容所以我們先將其保存起來。如果想要保存暫存區就可以使用-a來保存。
Git stash save -a “stash1”
這時候我們來查看下緩存區狀態git status的時候是很干凈的如下圖:
下面我們切換到master分之下,在切換會ttt_v0下時我們要還原stash里面的內容
會有一個stash的緩存內容存在,下面我們來恢復stash里面的內容,并將緩存區內容還原。
git stash pop –index stash@{0} 這里的標記紅色的表示stash里面的第0個stash
我們會發現之前修改的a文件已經在暫存區內貯備提交了
使用git stash apply –index stash@{0}這種方式時git stash list里面的內容是不會被清理掉的這里–index是恢復暫存區內容
使用git stash drop stash@{0}清除掉,如果不加stash@{0}引用的話他默認會清楚stash棧最上面的一個。
清楚多個stash的時候使用git stash clear
四、查看與對比歷史記錄
1.git show
2.git log
3.git diff
輸入git log –oneline –decorate –graph –all查看完整的歷史示意圖。
git log -p常用的選項是 -p,用來顯示每次提交的內容差異, 你也可以加上 -2 來僅顯示最近兩次提交:
我們可以根據查看git show f6699b3的信息
最新的一次提交可以用git show master 或者git show HEAD,也可以使用git show master^表示master分支的第一次提交master^2表示第二次提交
通常我們會用git diff來回答兩個問題:第一個就是當前做了什么還沒有提交暫存區?第二個問題就是當前那些文件已經暫存了等待提交?
git diff 這個命令是查看當前工作區與暫存區快照的差異,也就是那些還沒有暫存起來。
圖中顯示的是暫存區和工作區的區別在于a文件被修改了添加了next edit file。請注意,git diff 本身只顯示尚未暫存的改動,而不是自上次提交以來所做的所有改動。 所以有時候你一下子暫存了所有更新過的文件后,運行 git diff 后卻什么也沒有,就是這個原因。
回顧:
所謂的分支就是一個commit記錄的引用如下圖所示:
在這些分支上工作會產生各自的歷史記錄(commit記錄) 所謂的分支切換就是指的Head指針的切換以及暫存區和工作區的一個還原 ,比如說我們現在當前Head指向的位置處再建兩個分支之后他們所指向的commit都是同一個如下圖所示:
分支切換后如下圖所示:
而tag指向了一個固定的commit記錄,如下圖所示:
分為兩種一種是輕量級的一種是標簽對象,主要區別就是輕量級的tag只是一個固定的連接,但是標簽對象就是創建標簽的時候會產生一個標簽對象,這個標簽對象存儲在.git/object目錄里面,tag對象里面包括對數據對象的指向,以及創建時間數據對象類型和創建人等信息。
舉個例子來說明一下分支切換以及合并:
開始的狀態是這樣的,我們提交了三次commit后的示意圖如下:
當我們使用了git branch test的時候,將會在當前位置處新建一個分支,分支的名稱叫test,但是分支并沒有切換依然是master分支(Head的指向)如下圖所示:
我們在master分支上面工作會產生master分支上面的commit記錄如下圖所示:
這個時候我們是用git checkout test的時候HEAD指針就會指向test,并且還原暫存區和工作區內容。如下圖所示:
繼續在test分支上面進行工作,會產生test對應分支上面的commit記錄
我們來說一下合并,合并分為兩種一種是fast-farword merge也就是說test所指向的commit記錄他其實是master所指向的commit所衍生出來的如下圖所示:
藍色部分是master分支,紅色部分是master分支所衍生出來的分支。這時候我們在master分支上面進行合并git merge test不會產生新的commit記錄出來,master分支指向test分支所指向的commit記錄上去,然后把工作區和暫存區還原成了test的暫存區和工作區內容。如下圖所示:
另外一種就是non-fast-farword merge也就是我們的三方合并。
我們可以看到master指向的commit和test只想的commit有一個共同的父節點就是70e4d8這個節點。Master指向的commit和test指向的commit并沒有一個衍生的關系,這時候我們在master分支上面git merge test,會生成一個新的commit來承載兩個分支的歷史內容
五、結束語
這篇文章非一天寫完的我也是在學習的過程中總結,以及參考大嬸們寫的文章加自己的理解寫成的,如果這里有些錯的或者有雷同的請各位大神指點一二。小丁謝過,寫一篇文章確實不容易,能支持的就支持下不能支持的就當沒看過這篇文章就好了。寫篇文章容易么?寫完了還被移除首頁,已經發了兩篇了第一篇只是開始被移除了首頁~看看這個給不給我移除首頁吧~