關(guān)鍵詞:Git
所有內(nèi)容來(lái)自于《Git從入門到精通》此書(shū)的內(nèi)容提要善榛,作為學(xué)習(xí)筆記
Git概述
Git是一種分布式版本控制系統(tǒng)咕别,記錄項(xiàng)目代碼不同進(jìn)度或版本的內(nèi)容收叶,可以方便地查看不同版本的內(nèi)容改動(dòng),Git是共享的菇夸,可以查看每個(gè)文件是誰(shuí)在什么時(shí)候加入進(jìn)來(lái)的琼富,什么時(shí)候被修改或者刪除。
Git的優(yōu)點(diǎn)
- 免費(fèi)開(kāi)源
- 速度快庄新,文件體積芯厦肌:不同版本的內(nèi)容不需要復(fù)制備份,Git記錄文件內(nèi)容的快照择诈,可以非承堤#快速地切換版本
- 分布式系統(tǒng):SVN等集中式控制系統(tǒng)需要有中央服務(wù)器,存在單點(diǎn)故障的問(wèn)題吭从,且必須聯(lián)網(wǎng)才能操作朝蜘,Git是沒(méi)有中央服務(wù)器,開(kāi)發(fā)者有本地倉(cāng)庫(kù)涩金,在沒(méi)有網(wǎng)絡(luò)的情況下也可以進(jìn)行版控
Git和Github的區(qū)別:Git是版本控制軟件谱醇,Github是商業(yè)網(wǎng)站,其本體是一個(gè)Git服務(wù)器
Linux下安裝Git
使用如下命令
$ sudo apt-get install git
查看git的路徑和版本
$ which git
/usr/bin/git
$ git version
git version 2.25.1
設(shè)置Git
首先要設(shè)置Git的用戶名和郵箱步做,使用git config
在終端輸入以下命令
$ git config --global user.name "xxx"
$ git config --global user.email "xxx@xxx.com"
通過(guò)命令查看配置
$ git config --list
user.name=xxx
user.email=xxx@xxx.com
Git的設(shè)置參數(shù)保存位置在該用戶的根目錄下/home/username
的.gitconfig
中副渴,可以直接vim該文件修改
$ cat .gitconfig
[user]
name = xxx
email = xxx@xxx.com
可以以不同作者的身份參與不同項(xiàng)目,只需要將--global
改為--local
即可
使用Git
(1)新建倉(cāng)庫(kù)
在ubuntu終端新建一個(gè)文件夾全度,并且使用git初始化它即可
$ cd /tmp
$ mkdir git-practice
$ cd git-practice
$ git init
此時(shí)在項(xiàng)目目錄下生成一個(gè).git
目錄
/tmp/git-practice$ ll
total 32
drwxrwxr-x 4 xxx xxx 4096 8月 12 17:58 ./
drwxrwxrwt 23 root root 12288 8月 12 22:03 ../
drwxrwxr-x 9 xxx xxx 4096 8月 12 22:04 .git/
如果不想讓Git管控這個(gè)文件夾煮剧,直接刪除.git文件即可,整個(gè)文件夾中除了.git文件刪除了都能被找回來(lái)将鸵,如果.git刪除了就不能找回了
(2)Git管控文件夾
使用git status
查看哪些文件和目錄已經(jīng)被Git追蹤勉盅,哪些還沒(méi)有被追蹤
/tmp/git-practice$ git status
On branch master
No commits yet
nothing to commit (create/copy files and use "git add" to track)
該項(xiàng)目目錄下沒(méi)有任何文件時(shí)沒(méi)有內(nèi)容可以提交,因?yàn)檫€沒(méi)有任何內(nèi)容被add到暫存區(qū)顶掉。新建一個(gè)文件再次查看git status
/tmp/git-practice$ touch a.txt
/tmp/git-practice$ git status
On branch master
No commits yet
Untracked files:
(use "git add <file>..." to include in what will be committed)
a.txt
nothing added to commit but untracked files present (use "git add" to track)
顯示找到一個(gè)沒(méi)有被追蹤的文件草娜,沒(méi)有文件被add到暫存區(qū)但是確實(shí)有文件存在。下一步將該文件使用git add
加入到暫存區(qū)痒筒,再查看status
/tmp/git-practice$ git add a.txt
/tmp/git-practice$ git status
On branch master
No commits yet
Changes to be committed:
(use "git rm --cached <file>..." to unstage)
new file: a.txt
顯示在暫存區(qū)有心文件準(zhǔn)備要被提交宰闰。add可以接多個(gè)文件或者通配符,例如
$ git add b.txt c.txt
$ git add *.txt
注意add只會(huì)作用于新增
簿透,改動(dòng)過(guò)
移袍,刪除
的文件加入暫存區(qū),已經(jīng)存在且沒(méi)有修改的不會(huì)處理老充,例如本地刪除某個(gè)文件葡盗,該文件處于暫存區(qū)
/tmp/git-practice$ rm -rf e.txt
/tmp/git-practice$ git status
On branch master
No commits yet
Changes to be committed:
(use "git rm --cached <file>..." to unstage)
new file: a.txt
new file: b.txt
new file: c.txt
new file: d.txt
new file: e.txt
Changes not staged for commit:
(use "git add/rm <file>..." to update what will be committed)
(use "git restore <file>..." to discard changes in working directory)
deleted: e.txt
此時(shí)顯示暫存區(qū)存在一個(gè)已經(jīng)本地刪除的文件e.txt,雖然本地已經(jīng)刪除啡浊,還是可以使用git add操作
/tmp/git-practice$ git add e.txt
/tmp/git-practice$ git status
On branch master
No commits yet
Changes to be committed:
(use "git rm --cached <file>..." to unstage)
new file: a.txt
new file: b.txt
new file: c.txt
new file: d.txt
add之后暫存區(qū)也沒(méi)有了e.txt戳粒。
如果想將目錄想的所有文件都提交到暫存區(qū)路狮,可以只用git add .
或者git add --all
。在Git 2.x之后的版本蔚约,如果都位于項(xiàng)目根目錄下使用,兩者沒(méi)有區(qū)別涂籽,都是對(duì)新增苹祟,改動(dòng),刪除的內(nèi)容要處理评雌,區(qū)別是git add .只會(huì)對(duì)當(dāng)前目錄以及子目錄下的文件處理树枫,上層目錄不管,而git add --all不管在哪個(gè)目錄景东,整個(gè)項(xiàng)目有變化的文件一起提交到暫存區(qū)砂轻。
在項(xiàng)目根目錄下新建一個(gè)目錄test,test下新建aa.txt斤吐,在根目錄下新建一個(gè)文件e.txt
/tmp/git-practice$ git status
On branch master
No commits yet
Changes to be committed:
(use "git rm --cached <file>..." to unstage)
new file: a.txt
new file: b.txt
new file: c.txt
new file: d.txt
Untracked files:
(use "git add <file>..." to include in what will be committed)
e.txt
test/
在test目錄下使用git add .
/tmp/git-practice$ cd test/
/tmp/git-practice/test$ git add .
/tmp/git-practice/test$ cd ..
/tmp/git-practice$ git status
On branch master
No commits yet
Changes to be committed:
(use "git rm --cached <file>..." to unstage)
new file: a.txt
new file: b.txt
new file: c.txt
new file: d.txt
new file: test/aa.txt
Untracked files:
(use "git add <file>..." to include in what will be committed)
e.txt
根目錄下的e.txt沒(méi)有被add到搔涝,再試一下add --all
/tmp/git-practice/test$ git add --all
/tmp/git-practice/test$ cd ..
/tmp/git-practice$ git status
On branch master
No commits yet
Changes to be committed:
(use "git rm --cached <file>..." to unstage)
new file: a.txt
new file: b.txt
new file: c.txt
new file: d.txt
new file: e.txt
new file: test/aa.txt
全部add成功
(3)把暫存區(qū)文件提交到本地倉(cāng)庫(kù)
如果想把暫存區(qū)的內(nèi)容永久保存下載使用git commit
命令
/tmp/git-practice$ git commit -m "init commit"
[master (root-commit) c6e5309] init commit
6 files changed, 0 insertions(+), 0 deletions(-)
create mode 100644 a.txt
create mode 100644 b.txt
create mode 100644 c.txt
create mode 100644 d.txt
create mode 100644 e.txt
create mode 100644 test/aa.txt
-m
后面的內(nèi)容表示本次提交做了什么,中英文都可以和措,簡(jiǎn)短說(shuō)明即可庄呈。
只有暫存區(qū)有文件才能commit,如果為空可以使用--allow-empty
提交派阱,例如
/tmp/git-practice$ git commit --allow-empty -m "空文件"
[master 966f3e3] 空文件
(4)工作區(qū)诬留,暫存區(qū),存儲(chǔ)區(qū)
- git add將文件從工作目錄移動(dòng)至?xí)捍鎱^(qū)
- git commit將暫存區(qū)的內(nèi)容移動(dòng)至存儲(chǔ)區(qū)
如圖使用二段操作先add后commit,這樣的好處是先將文件放入暫存區(qū)腺劣,積累到一定數(shù)量之后再統(tǒng)一提交绿贞,避免commit太零碎,減少commit次數(shù)誓酒,導(dǎo)致代碼不完整不方便代碼review樟蠕。也可以使用在commit中使用-a
參數(shù)直接提交,但是僅對(duì)存儲(chǔ)區(qū)中已經(jīng)有的文件生效靠柑,例如創(chuàng)建一個(gè)f.txt寨辩,修改e.txt
/tmp/git-practice$ git status
On branch master
Changes not staged for commit:
(use "git add <file>..." to update what will be committed)
(use "git restore <file>..." to discard changes in working directory)
modified: e.txt
Untracked files:
(use "git add <file>..." to include in what will be committed)
f.txt
no changes added to commit (use "git add" and/or "git commit -a")
此時(shí)不使用二段式使用-a直接提交
/tmp/git-practice$ git commit -a -m "test"
[master 13a5a9e] test
1 file changed, 1 insertion(+)
/tmp/git-practice$ git status
On branch master
Untracked files:
(use "git add <file>..." to include in what will be committed)
f.txt
nothing added to commit but untracked files present (use "git add" to track)
已經(jīng)修改的e.txt已經(jīng)提交,但是新建的f.txt還是沒(méi)有被git追蹤歼冰,需要add靡狞。
(5)查看整個(gè)工作項(xiàng)目的提交記錄(log)
使用git log
命令可以查看提交記錄,在項(xiàng)目根目錄下操作
/tmp/git-practice$ git log
commit 13a5a9e45fbc3ced0c9b05cfb8a008d948f4d7cb (HEAD -> master)
Author: xxx <xxx@xxx.com>
Date: Sat Aug 13 07:16:46 2022 +0800
test
commit 966f3e362e0778318f5dbc8d7c9c9f32c67b101f
Author: xxx <xxx@xxx.com>
Date: Sat Aug 13 07:08:46 2022 +0800
空文件
commit c6e53097ec92cab959507b8eafab96b11f9ca18b
Author: xxx <xxx@xxx.com>
Date: Fri Aug 12 22:58:18 2022 +0800
init commit
可以看到提交了3次隔嫡,越新的提交越在上面甸怕。每次有commit號(hào)
甘穿,作者
,提交時(shí)間
梢杭,commit內(nèi)容
温兼,其中commit號(hào)是使用SHA-1計(jì)算出來(lái)的重復(fù)率極低的文本∥淦酰可以再加額外參數(shù)募判,比如
/tmp/git-practice$ git log --oneline
13a5a9e (HEAD -> master) test
966f3e3 空文件
c6e5309 init commit
這樣更清楚只顯示一行文本簡(jiǎn)潔內(nèi)容≈渌簦或者--pretty=oneline
顯示完整sha1碼
$ git log --pretty=oneline
db14c2561ea537d769c4581a7b113a222b811324 (HEAD -> master) fourth commit
c6468b5c9b3da548ea3eee62aae1a4c2cd068819 thrid commit
8cbf52670ce848582dfd76c4be419fcbf8e55265 seconf commit
3dfd0b8adf67a961bf2282ea0e0f057ecd4e0838 first commit
進(jìn)一步可以添加參數(shù)進(jìn)行搜索届垫,例如所有某個(gè)指定作者的提交
/tmp/git-practice$ git log --oneline --author="xxx"
搜索提交內(nèi)容帶有關(guān)鍵字的
/tmp/git-practice$ git log --oneline --grep="init"
c6e5309 init commit
(6)使用git刪除文件
在git中刪除文件相當(dāng)于一次改動(dòng),可以使用linux的rm直接刪除全释,此時(shí)相當(dāng)于工作區(qū)該文件刪除装处,同時(shí)該文件狀態(tài)改為delete
/tmp/git-practice$ rm -rf a.txt
/tmp/git-practice$ git status
On branch master
Changes not staged for commit:
(use "git add/rm <file>..." to update what will be committed)
(use "git restore <file>..." to discard changes in working directory)
deleted: a.txt
no changes added to commit (use "git add" and/or "git commit -a")
a.txt狀態(tài)改為delete
/tmp/git-practice$ ls
b.txt c.txt d.txt e.txt f.txt test
本地也沒(méi)了,如果再add就將該文件放到暫存區(qū)
/tmp/git-practice$ git add
/tmp/git-practice$ git status
On branch master
Changes to be committed:
(use "git restore --staged <file>..." to unstage)
deleted: a.txt
第二種是使用git的rm進(jìn)行刪除浸船,相當(dāng)于本地和倉(cāng)庫(kù)rm妄迁,同時(shí)直接加入暫存區(qū)
/tmp/git-practice$ git rm c.txt
rm 'c.txt'
/tmp/git-practice$ git status
On branch master
Changes to be committed:
(use "git restore --staged <file>..." to unstage)
deleted: b.txt
deleted: c.txt
/tmp/git-practice$ ls
d.txt e.txt f.txt test
注意git rm的文件必須已經(jīng)commit,如果僅是未追蹤或者僅是add沒(méi)有提交git rm會(huì)報(bào)錯(cuò)
fatal: pathspec 'aaa.txt' did not match any files # 沒(méi)有add報(bào)錯(cuò)找不到文件
error: the following file has changes staged in the index: # 沒(méi)有commit報(bào)錯(cuò)和倉(cāng)庫(kù)不一致
還有一種情況是將文件取消git控制糟袁,但是本地保留判族,在git rm的時(shí)候增加--cached
參數(shù)
/tmp/git-practice$ git rm d.txt --cached
rm 'd.txt'
/tmp/git-practice$ git status
On branch master
Changes to be committed:
(use "git restore --staged <file>..." to unstage)
deleted: b.txt
deleted: c.txt
deleted: d.txt
Untracked files:
(use "git add <file>..." to include in what will be committed)
d.txt
/tmp/git-practice$ ls
d.txt e.txt f.txt test
此時(shí)d.txt狀態(tài)改為未追蹤吉执,本地工作目錄還是存在蟆湖,相當(dāng)于再暫存區(qū)刪除,回到最開(kāi)始的未追蹤狀態(tài)先蒋。
(7)使用git修改文件名
如果使用linux的rm周叮,相當(dāng)于在倉(cāng)庫(kù)將某文件改變狀態(tài)為delete辩撑,并且本地多了一個(gè)未追蹤的新命名的文件
/tmp/git-practice$ mv e.txt ee.txt
/tmp/git-practice$ git status
On branch master
Changes to be committed:
(use "git restore --staged <file>..." to unstage)
deleted: b.txt
deleted: c.txt
deleted: d.txt
Changes not staged for commit:
(use "git add/rm <file>..." to update what will be committed)
(use "git restore <file>..." to discard changes in working directory)
deleted: e.txt
Untracked files:
(use "git add <file>..." to include in what will be committed)
d.txt
ee.txt
此時(shí)出現(xiàn)三種狀態(tài)
-
Changes to be committed
:已經(jīng)在暫存區(qū)生成了最新的快照,等待被提交仿耽。由于之前刪除的三個(gè)文件都被手動(dòng)add或者git rm自動(dòng)add合冀,已經(jīng)加入到暫存區(qū) -
Changes not staged for commit
:暫存區(qū)生成過(guò)快照,但是有了新的修改项贺,沒(méi)有生成新的快照君躺,linux mv不會(huì)自動(dòng)將改動(dòng)加入到暫存區(qū) -
Untracked files
:未跟蹤的狀態(tài),暫存區(qū)中還沒(méi)有生成快照
需要手動(dòng)add加入到暫存區(qū)
/tmp/git-practice$ git add .
/tmp/git-practice$ git status
On branch master
Changes to be committed:
(use "git restore --staged <file>..." to unstage)
deleted: b.txt
deleted: c.txt
renamed: e.txt -> ee.txt
此時(shí)在暫存區(qū)中已經(jīng)加入rename操作开缎。
也可以直接使用git mv
/tmp/git-practice$ git mv ee.txt eee.txt
/tmp/git-practice$ git status
On branch master
Changes to be committed:
(use "git restore --staged <file>..." to unstage)
deleted: b.txt
deleted: c.txt
renamed: e.txt -> eee.txt
此操作一步到位棕叫,暫存區(qū)中加入改名操作。
文件的名稱對(duì)git來(lái)說(shuō)不重要奕删,git基于文件的內(nèi)容使用sha-1算法計(jì)算值生成blob文件在.git/objects目錄下俺泣,只更改文件名不會(huì)使得git重新生成blob對(duì)象。
(8)修改最后一次commit記錄(--amend)
使用git commit --amend
可以修改最后一次commit記錄,包括修改commit信息伏钠,修改文件等横漏,例如修改最后一次commit信息
/tmp/git-practice$ git commit --amend -m "alter commit"
[master 25bf556] alter commit
Date: Sat Aug 13 12:33:30 2022 +0800
1 file changed, 1 insertion(+)
/tmp/git-practice$ git log --oneline
25bf556 (HEAD -> master) alter commit
acaf3eb new commit
13a5a9e test
966f3e3 空文件
c6e5309 init commit
新生成了一個(gè)commit對(duì)象覆蓋了原來(lái)的。
同樣可以修改文件熟掂,比如追加一個(gè)文件到最后一次commit
/tmp/git-practice$ touch amend.txt
/tmp/git-practice$ git status
/tmp/git-practice$ git add .
/tmp/git-practice$ git commit --amend --no-edit
[master 6b74d34] alter commit
Date: Sat Aug 13 12:33:30 2022 +0800
2 files changed, 1 insertion(+)
create mode 100644 amend.txt
/tmp/git-practice$ git log --oneline
6b74d34 (HEAD -> master) alter commit
acaf3eb new commit
13a5a9e test
966f3e3 空文件
c6e5309 init commit
(9)忽略文件(.gitignore)
工作目錄下某些文件比如生產(chǎn)數(shù)據(jù)庫(kù)密碼配置缎浇,緩存文件等,不需要提交和git控制打掘,使用.gitignore
文件可以忽略华畏,.gitignore只對(duì)在其創(chuàng)建之后才有的文件有效
/tmp/git-practice$ touch .gitignore
/tmp/git-practice$ vim .gitignore
# vim .gitignore 輸入
config.yml # 忽略根目錄下的config.yml文件
# 每一行輸入一個(gè)文件
config/config.yml # 忽略config目錄下的config.yml
將.gitignore add到暫存區(qū),同時(shí)本地新建一個(gè)config.yml文件
/tmp/git-practice$ git add .
/tmp/git-practice$ touch config.yml
此時(shí)查看status尊蚁,沒(méi)有顯示config.yml任何信息
/tmp/git-practice$ git status
On branch master
Changes to be committed:
(use "git restore --staged <file>..." to unstage)
new file: .gitignore
如果在add的增加-f
就可以忽略.gitignore,也會(huì)把config.yml加入暫存區(qū)
/tmp/git-practice$ git add -f config.yml
可以使用git clean -fX
刪除本地工作目錄忽略的文件
/tmp/git-practice$ git clean -fX
Removing config.yml
(10)查看某個(gè)文件的提交記錄(log)
在git log后面加入文件名可以查看某個(gè)文件的提交記錄侣夷,先修改d.txt横朋,增加一些文本,在add和提交
/tmp/git-practice$ vim d.txt
/tmp/git-practice$ git add .
/tmp/git-practice$ git commit -m "new commit"
此時(shí)使用git log --oneline d.txt查看
/tmp/git-practice$ git log --oneline d.txt
acaf3eb (HEAD -> master) new commit
c6e5309 init commit
有兩條提交記錄百拓,使用-p
參數(shù)可以查看具體變更內(nèi)容
/tmp/git-practice$ git log --oneline -p d.txt
acaf3eb (HEAD -> master) new commit
diff --git a/d.txt b/d.txt
index e69de29..e61ef7b 100644
--- a/d.txt
+++ b/d.txt
@@ -0,0 +1 @@
+aa
c6e5309 init commit
diff --git a/d.txt b/d.txt
new file mode 100644
index 0000000..e69de29
大致看出new commit那次增加了aa一行文本琴锭,+
代表增加的內(nèi)容,-
表示刪除的內(nèi)容
(11)追蹤提交代碼內(nèi)容的作者(blame)
當(dāng)某個(gè)已經(jīng)提交的代碼文件需要排查某一行的作者衙传,通過(guò)git blame
可以查看
/tmp/git-practice$ git blame model.py
c5f030e7 (xxx 2022-08-13 19:21:59 +0800 1) import numpy as np
c5f030e7 (xxx 2022-08-13 19:21:59 +0800 2) import tensorflow as tf
c5f030e7 (xxx 2022-08-13 19:21:59 +0800 3)
c5f030e7 (xxx 2022-08-13 19:21:59 +0800 4)
c5f030e7 (xxx 2022-08-13 19:21:59 +0800 5) def glorot(shape, name=None):
54f067ef (xxx 2022-08-13 19:25:50 +0800 6) init_range = np.sqrt(9.0 / (shape[0] + shape[1]))
c5f030e7 (xxx 2022-08-13 19:21:59 +0800 7) initial = tf.random_uniform(shape, minval=-init_range, maxval=init_range, dtype=tf.float32)
c5f030e7 (xxx 2022-08-13 19:21:59 +0800 8) return tf.Variable(initial, name=name)
可以看到?jīng)]一行代碼的作者决帖,提交時(shí)間,commit號(hào)蓖捶,可見(jiàn)有c5f030e7和c5f030e7兩次提交地回,該窗口可以使用linux less中類似的跳到指定行的方法。
(12)恢復(fù)已經(jīng)刪除或者修改的文件或目錄(checkout俊鱼,回滾單個(gè)文件)
使用git checkout
方法指定一個(gè)已經(jīng)被刪除的文件就可以恢復(fù)到工作目錄
/tmp/git-practice$ rm -rf f.txt
/tmp/git-practice$ git checkout f.txt
也可以直接加.恢復(fù)所有刪除的文件
/tmp/git-practice$ git checkout .
git checkout + 文件名實(shí)際上是將要恢復(fù)的文件從.git目錄中復(fù)制一份到當(dāng)前的工作目錄刻像,在沒(méi)刪除之前這些文件被add到暫存區(qū),就是在.git/objects下創(chuàng)建了對(duì)應(yīng)的文件并闲。
注意
- 以上恢復(fù)僅對(duì)類似linux rm刪除的方法可以生效细睡,如果使用git rm或者普通rm刪除之后+add,無(wú)法使用git checkout恢復(fù)刪除的文件會(huì)報(bào)錯(cuò)
error: pathspec 'xxx' did not match any file(s) known to git
- git checkout恢復(fù)文件僅需要文件add即可帝火,沒(méi)有commit的文件也能被恢復(fù)
如果僅有一個(gè)或者多個(gè)腳本需要回滾到指定版本溜徙,而不是整個(gè)版本回滾,可以使用checkout
git checkout xxxxx(commit id) XXXX(文件名或者路徑)
例如將PredictSeq.java回滾到711c8b45這個(gè)版本的時(shí)候的樣子犀填,覆蓋現(xiàn)有工作目錄下的PredictSeq.java
git checkout 711c8b45 PredictSeq.java
在回滾之后文件只在工作目錄蠢壹,不再暫存區(qū),需要add宏浩。
(13)commit拆掉重做/回滾(reset)
使用git reset
使當(dāng)前的commit切換到之前某個(gè)commit狀態(tài)知残,例如因?yàn)楸敬蝐ommit要重做,要前往前一個(gè)commit的狀態(tài)
/tmp/git-practice$ git log --oneline
54f067e (HEAD -> master) 123
c5f030e gnn
6b74d34 alter commit
/tmp/git-practice$ git reset master^
Unstaged changes after reset:
M model.py
/tmp/git-practice$ git log --oneline
c5f030e (HEAD -> master) gnn
6b74d34 alter commit
54f067e這個(gè)commit已經(jīng)刪除回退到c5f030e,其中master/HEAD代表當(dāng)前的commit求妹,^代表前一次乏盐,另一種是直接指定要前往的commit號(hào)
/tmp/git-practice$ git reset 6b74d34
/tmp/git-practice$ git log --oneline
6b74d34 (HEAD -> master) alter commit
reset有三種模式
-
--mixed
(默認(rèn)),該種模式拆除的文件會(huì)留在工作目錄制恍,但會(huì)從暫存區(qū)刪除 -
--soft
父能,該種模式暫存區(qū)和工作目錄都不會(huì)被刪除,僅是HEAD移動(dòng) -
--hard
净神,該種模式不論是暫存區(qū)還是工作目錄都會(huì)被刪除
reset常用于本地倉(cāng)庫(kù)代碼回滾何吝,測(cè)試一下
# 修改追加一行print("回滾測(cè)試") 到hello.py,新建一個(gè)huigun.txt
# commit新增一條commit記錄
$ git commit -m "這是一個(gè)回滾測(cè)試版本"
[master 5ffc52a] 這是一個(gè)回滾測(cè)試版本
2 files changed, 4 insertions(+)
create mode 100644 huigun.txt
$ git log --oneline
5ffc52a (HEAD -> master) 這是一個(gè)回滾測(cè)試版本
db14c25 fourth commit
c6468b5 thrid commit
8cbf526 seconf commit
3dfd0b8 first commit
先測(cè)試--hard回滾鹃唯,本地倉(cāng)庫(kù)直接恢復(fù)到一個(gè)舊的版本爱榕,且從它之后的版本在樹(shù)中消失
$ git reset --hard db14c25
HEAD is now at db14c25 fourth commit
$ git log --oneline
db14c25 (HEAD -> master) fourth commit
c6468b5 thrid commit
8cbf526 seconf commit
3dfd0b8 first commit
回滾后本地工作目錄和上一次提交時(shí)一樣,文件修改的恢復(fù)原樣坡慌,新增的也被刪除黔酥。實(shí)際上5ffc52a 這個(gè)commit還在本地倉(cāng)庫(kù)中,可以再切過(guò)去洪橘,使用git reflog
查看出歷史所有對(duì)commit的操作
$ git reflog --oneline
db14c25 (HEAD -> master) HEAD@{0}: reset: moving to db14c25
5ffc52a HEAD@{1}: commit: 這是一個(gè)回滾測(cè)試版本
db14c25 (HEAD -> master) HEAD@{2}: commit: fourth commit
c6468b5 HEAD@{3}: commit: thrid commit
8cbf526 HEAD@{4}: commit: seconf commit
3dfd0b8 HEAD@{5}: commit (initial): first commit
再reset過(guò)去又恢復(fù)了
$ git reset --hard 5ffc52a
HEAD is now at 5ffc52a 這是一個(gè)回滾測(cè)試版本
$ ls
hello.py huigun.txt README.md test/ two.txt world..py
$ cat hello.py
print("hello")
print(1)
print("回滾測(cè)試")
$ cat huigun.txt
回滾測(cè)試
git log消失的commit也被恢復(fù)
$ git log --oneline
5ffc52a (HEAD -> master) 這是一個(gè)回滾測(cè)試版本
db14c25 fourth commit
c6468b5 thrid commit
8cbf526 seconf commit
3dfd0b8 first commit
從smartgit可視化來(lái)看樹(shù)圖
也可以使用^
符號(hào)回滾到指定版本的之前一個(gè)版本跪者,一個(gè)^
代表一個(gè)版本,兩個(gè)就^^
熄求,再多可以使用~n
n是前多少個(gè)版本
$ git log --oneline
db14c25 fourth commit
c6468b5 thrid commit
8cbf526 seconf commit
3dfd0b8 first commit
$ git reset --hard HEAD^
HEAD is now at db14c25 fourth commit
$ git log --oneline
db14c25 (HEAD -> master) fourth commit
c6468b5 thrid commit
8cbf526 seconf commit
3dfd0b8 first commit
$ git reset --hard HEAD~2
HEAD is now at 8cbf526 seconf commit
$ git log --oneline
8cbf526 (HEAD -> master) seconf commit
3dfd0b8 first commit
如果在HEAD下改動(dòng)了還沒(méi)有commit渣玲,想直接回滾重寫,可以直接回滾HEAD
$ git log --hard HEAD
再測(cè)試一下默認(rèn)的mixed模式弟晚,現(xiàn)在修改一個(gè)文件huigun.txt忘衍,新增一個(gè)文件new.py
$ echo "這是一個(gè)mixed測(cè)試" > huigun.txt
$ touch new.py
$ git add .
warning: LF will be replaced by CRLF in huigun.txt.
The file will have its original line endings in your working directory
$ git commit -m "這是一個(gè)mixed測(cè)試"
[master f3481aa] 這是一個(gè)mixed測(cè)試
2 files changed, 1 insertion(+), 1 deletion(-)
create mode 100644 new.py
$ git log --oneline
f3481aa (HEAD -> master) 這是一個(gè)mixed測(cè)試
5ffc52a 這是一個(gè)回滾測(cè)試版本
db14c25 fourth commit
c6468b5 thrid commit
8cbf526 seconf commit
3dfd0b8 first commit
使用默認(rèn)的mixed回滾到上一個(gè)版本,回滾后結(jié)果如下
$ git status
On branch master
Changes not staged for commit:
(use "git add <file>..." to update what will be committed)
(use "git restore <file>..." to discard changes in working directory)
modified: huigun.txt
Untracked files:
(use "git add <file>..." to include in what will be committed)
new.py
no changes added to commit (use "git add" and/or "git commit -a")
結(jié)果就是HEAD已經(jīng)回到上個(gè)版本指巡,但是本地工作目錄還是存在代碼改動(dòng)淑履,修改的腳本還是被修改了改為未add狀態(tài),新增的腳本還是被新增了改為未追蹤狀態(tài)藻雪。再測(cè)試一下soft模式
$ git reset --soft HEAD^
$ git log --oneline
5ffc52a (HEAD -> master) 這是一個(gè)回滾測(cè)試版本
db14c25 fourth commit
c6468b5 thrid commit
8cbf526 seconf commit
3dfd0b8 first commit
$ git status
On branch master
Changes to be committed:
(use "git restore --staged <file>..." to unstage)
modified: huigun.txt
new file: new.py
結(jié)果僅僅是HEAD移動(dòng)到上一個(gè)commit秘噪,但是工作目錄和暫存區(qū)都還保留了改動(dòng)∶阋總結(jié)一下hard指煎,soft,mixed都會(huì)把HEAD移動(dòng)到想要回滾的commit便斥,區(qū)別就是在工作目錄和暫存區(qū)至壤,hard會(huì)把整個(gè)工作目錄文件變動(dòng)全部還原和回滾之前一樣,mixed工作目錄變動(dòng)保留枢纠,但是變動(dòng)的文件在暫存區(qū)移除需要重新add像街,soft什么變動(dòng)都保留,僅僅是HEAD移動(dòng)
(14)git diff 比較文件差異
使用git diff
可以查看文件差異,具體可以查看工作區(qū)和緩存區(qū)的差異镰绎,以及緩存區(qū)和倉(cāng)庫(kù)(上一次提交)的差異脓斩。
git diff不加任何參數(shù)查看項(xiàng)目下,工作目錄和暫存區(qū)的差異
$ git diff
warning: LF will be replaced by CRLF in README.md.
The file will have its original line endings in your working directory
diff --git a/README.md b/README.md
index e69de29..7898192 100644
--- a/README.md
+++ b/README.md
@@ -0,0 +1 @@
+a
diff --git a/hello.py b/hello.py
index 4b74a69..0e56f06 100644
--- a/hello.py
+++ b/hello.py
@@ -1,3 +1,6 @@
print("hello")
print("hello world")
-print("hello world world")
\ No newline at end of file
+print("hello world world")
+
+
+print(1)
加上--stat可以只輸出摘要而非具體改動(dòng)內(nèi)容
$ git diff --stat
warning: LF will be replaced by CRLF in README.md.
The file will have its original line endings in your working directory
README.md | 1 +
hello.py | 5 ++++-
2 files changed, 5 insertions(+), 1 deletion(-)
摘要就是READ.md加了一行畴栖, hello.py加了4行刪了1行随静,一共動(dòng)了5行。如果一行代碼局部出現(xiàn)改動(dòng)吗讶,git按照刪除該行又新增一行處理燎猛。
git diff最后加上指定文件可以只看該文件的改動(dòng)而不是整個(gè)項(xiàng)目
$ git diff --stat hello.py
hello.py | 5 ++++-
1 file changed, 4 insertions(+), 1 deletion(-)
git diff加上--cached
可以比較已經(jīng)add的暫存區(qū)和倉(cāng)庫(kù)的文件差異
$ git status
On branch master
Changes to be committed:
(use "git restore --staged <file>..." to unstage)
modified: world..py
Changes not staged for commit:
(use "git add <file>..." to update what will be committed)
(use "git restore <file>..." to discard changes in working directory)
modified: README.md
modified: hello.py
修改過(guò)的world..py已經(jīng)再暫存區(qū)了,使用git diff --cached
查看他和上一次提交到倉(cāng)庫(kù)的差異
$ git diff --cached
diff --git a/world..py b/world..py
index 72943a1..97811de 100644
--- a/world..py
+++ b/world..py
@@ -1 +1,7 @@
-aaa
+aac
+
+
+
+ccd
+
+cd
參考上面的git rm --cached
將文件從暫存區(qū)移除回到未追蹤照皆,--cached
參數(shù)就是將git命令操作再暫存區(qū)的意思重绷。
另外git diff HEAD
能查看所有已經(jīng)再暫存區(qū)和還沒(méi)有到暫存區(qū)的文件和上一次提交的文件差異,因?yàn)?strong>HEAD總是指向你最近的一次提交記錄
$ git diff HEAD --stat
warning: LF will be replaced by CRLF in README.md.
The file will have its original line endings in your working directory
README.md | 1 +
hello.py | 5 ++++-
world..py | 1 -
3 files changed, 5 insertions(+), 2 deletions(-)
(15)HEAD是什么
HEAD是一個(gè)指標(biāo)指向一個(gè)分支膜毁,代表當(dāng)前所在分支论寨,在啊.git/HEAD中記錄了HEAD內(nèi)容,HEAD總是指向你最近的一次提交記錄
/tmp/git-practice/.git$ cat HEAD
ref: refs/heads/master
記錄了HEAD內(nèi)容的目錄追蹤進(jìn)去看
/tmp/git-practice/.git$ cd refs/
/tmp/git-practice/.git/refs$ cd heads/
/tmp/git-practice/.git/refs/heads$ cat master
6b74d34e6ed26227a9a017006c8574933134b7f8
實(shí)際上記錄一個(gè)commit號(hào)
/tmp/git-practice$ git log --oneline
6b74d34 (HEAD -> master) alter commit
acaf3eb new commit
13a5a9e test
966f3e3 空文件
c6e5309 init commit
該commit號(hào)和最新一次提交的commit號(hào)等同爽茴。
(16).git目錄簡(jiǎn)述
工作目錄下的.git目錄主要包含Tree對(duì)象,commit對(duì)象绰垂,Blob對(duì)象室奏,Tag對(duì)象,在使用git各種命令的時(shí)候?qū)嶋H上在不斷修改和寫入這個(gè).git下的文件
當(dāng)創(chuàng)建一個(gè)文件時(shí)處于非追蹤狀態(tài)劲装,此時(shí)add到暫存區(qū)就會(huì)在.git/objects下根據(jù)文件的內(nèi)容創(chuàng)建一個(gè)blob對(duì)象胧沫。git先根據(jù)sha-1計(jì)算出blob的的名字,例如
/tmp/git-practice$ touch testa.txt
/tmp/git-practice$ vim testa.txt
#插入abc
abc
/tmp/git-practice$ git add .
使用git命令查看文件的sha-1值
/tmp/git-practice$ echo "abc" | git hash-object --stdin
8baef1b4abc478178b004d62031cf7fe6db6f903
生成了一個(gè)40長(zhǎng)度的字符串占业,其中前2位作為文件夾的名字存在.git/objects下绒怨,后38位作為文件名,查看.git/objects
/tmp/git-practice/.git/objects/8b$ ls
aef1b4abc478178b004d62031cf7fe6db6f903
/tmp/git-practice/.git/objects/8b$ pwd
/tmp/git-practice/.git/objects/8b
這個(gè)aef1b4abc478178b004d62031cf7fe6db6f903文件是被壓縮的谦疾,文本編輯器查看不了南蹂,使用git cat-file
來(lái)查看
/tmp/git-practice$ git cat-file -t 8baef1b4abc478178b004d62031cf7fe6db6f903
blob
/tmp/git-practice$ git cat-file -p 8baef1b4abc478178b004d62031cf7fe6db6f903
abc
查看時(shí)候必須是完整的40長(zhǎng)度,其中-t表示查看對(duì)象的類型念恍,-p表示查看內(nèi)容六剥,這樣就可以從壓縮的文件查看原本的內(nèi)容》寤铮總結(jié)Blob的文件名是SHA-1決定的疗疟,Blob的內(nèi)容由壓縮算法決定的。git采用前兩位分文件夾也是避免一個(gè)目錄下文件太多降低效率瞳氓。
創(chuàng)建一個(gè)空的文件夾不會(huì)被git感知到策彤,git只對(duì)文件有感應(yīng),空的目錄無(wú)法被加入到git中。此時(shí)在空文件夾中創(chuàng)建一個(gè)空文件
/tmp/git-practice$ mkdir doc
/tmp/git-practice$ cd doc/
/tmp/git-practice/doc$ touch doc1.txt
/tmp/git-practice$ git status
On branch master
Changes to be committed:
(use "git restore --staged <file>..." to unstage)
new file: testa.txt
Untracked files:
(use "git add <file>..." to include in what will be committed)
doc/
此時(shí)在status中就能感應(yīng)得到店诗,雖然創(chuàng)建的文件為空內(nèi)容裹刮,但是也是有內(nèi)容的(空的內(nèi)容),此時(shí)git根據(jù)sha-1和壓縮算法創(chuàng)建blob對(duì)象必搞,如果項(xiàng)目已經(jīng)有過(guò)空腳本的add則不會(huì)再在.git/objects下再創(chuàng)建因?yàn)閮?nèi)容一樣必指。blob只關(guān)系所有文件的內(nèi)容是否一樣,一樣就不重復(fù)創(chuàng)建恕洲,不關(guān)系文件的名稱以及來(lái)自的文件夾塔橡,文件夾和文件名稱是Tree對(duì)象的范疇。
Tree對(duì)象也是生成在.git/objects下霜第,在commit之后生成
/tmp/git-practice$ git status
On branch master
Changes to be committed:
(use "git restore --staged <file>..." to unstage)
new file: doc/doc1.txt
new file: testa.txt
目前有兩個(gè)文件在暫存區(qū)已經(jīng)創(chuàng)建最新快照葛家,此時(shí)commit之后查看.git/objects下的26/26a0e8ab4a7de23e8578d64234f3d4ff509881a0
/tmp/git-practice/.git/objects/26$ git cat-file -p 26a0e8ab4a7de23e8578d64234f3d4ff509881a0
100644 blob 039f8c8423f854784f8eafd0b968e80a4dfd0a83 .gitignore
100644 blob e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 amend.txt
100644 blob e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 config.yml
100644 blob 6ad46738804ca6670e653cc8593f134e252f4ac1 d.txt
040000 tree 3aa0d85a4ec5bf18f43bc7e2bf8ff8e08942e57d doc
100644 blob e61ef7b965e17c62ca23b6ff5f0aaf09586e10e9 eee.txt
100644 blob e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 f.txt
040000 tree f13f51556efabe074d5b255eabcdd3ec33520c55 test
100644 blob 8baef1b4abc478178b004d62031cf7fe6db6f903 testa.txt
該對(duì)象自身是一個(gè)tree,他下面又指向很多tree和blob泌类,其中3aa0d85a4ec5bf18f43bc7e2bf8ff8e08942e57d這個(gè)tree對(duì)象就是doc目錄癞谒,跟蹤進(jìn)去
/tmp/git-practice/.git/objects/26$ git cat-file -p 3aa0d85a4ec5bf18f43bc7e2bf8ff8e08942e57d
100644 blob e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 doc1.txt
doc下面是一個(gè)doc1.txt壓縮之后的blob對(duì)象,由此可見(jiàn)這個(gè)tree->(blob刃榨,tree)的DAG有向無(wú)環(huán)圖把 目錄結(jié)構(gòu)
弹砚,文件夾名稱
,文件名
都串起來(lái)了枢希,首先整個(gè)工程是一個(gè)tree桌吃,然后他指向其他子tree或者blob。git想當(dāng)于用Tree對(duì)象單獨(dú)記錄了目錄結(jié)構(gòu)和文件/文件夾名稱苞轿,blob單獨(dú)記錄文件內(nèi)容茅诱,通過(guò)40位的blob號(hào)進(jìn)行關(guān)聯(lián)文件名和內(nèi)容的關(guān)系。
然后看另一個(gè)對(duì)象
/tmp/git-practice/.git/objects/ad$ git cat-file -t ad1acc43daf0d86d5ebec5b69f1d87220d564088
commit
此乃一個(gè)從哦秘密他對(duì)象搬卒,看內(nèi)容
/tmp/git-practice/.git/objects/ad$ git cat-file -p ad1acc43daf0d86d5ebec5b69f1d87220d564088
tree 26a0e8ab4a7de23e8578d64234f3d4ff509881a0
parent 6b74d34e6ed26227a9a017006c8574933134b7f8
author xxx <xxx@xxx.com> 1660395552 +0800
committer xxx <xxx@xxx.com> 1660395552 +0800
my commit
一切明了了瑟俭,這個(gè)commit對(duì)象包含一個(gè)tree對(duì)象,上一次的commit號(hào)契邀,作者摆寄,提交者,提交信息蹂安,相當(dāng)于commit是以一個(gè)tree對(duì)象為根基椭迎,附帶上一次commit號(hào),作者田盈,提交者畜号,提交日期,提交信息的對(duì)象允瞧。其中tree就是剛才那個(gè)tree對(duì)象简软,對(duì)象號(hào)一樣蛮拔,觀察一下上次提交號(hào)6b74d34e6ed26227a9a017006c8574933134b7f8
/tmp/git-practice/.git/objects/ad$ git cat-file -p 6b74d34e6ed26227a9a017006c8574933134b7f8
tree 12df76f32d24b906ac7274391f7c35d0cb7db5df
parent acaf3eb290728cc2cd2f5a2a75da79cdf6dff72a
author xxx <xxx@xxx.com> 1660365210 +0800
committer xxx <xxx@xxx.com> 1660389419 +0800
alter commit
上一次的提交信息是alter commit,看一下上一次的目錄結(jié)構(gòu)DAG
/tmp/git-practice/.git/objects/ad$ git cat-file -p 12df76f32d24b906ac7274391f7c35d0cb7db5df
100644 blob 039f8c8423f854784f8eafd0b968e80a4dfd0a83 .gitignore
100644 blob e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 amend.txt
100644 blob e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 config.yml
100644 blob 6ad46738804ca6670e653cc8593f134e252f4ac1 d.txt
100644 blob e61ef7b965e17c62ca23b6ff5f0aaf09586e10e9 eee.txt
100644 blob e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 f.txt
040000 tree f13f51556efabe074d5b255eabcdd3ec33520c55 test
在上次提交的DAG中沒(méi)有testa.txt和doc目錄痹升,符合之前的操作事實(shí)建炫。因此Tree,blob疼蛾,commit三個(gè)對(duì)象關(guān)系如圖
由此可見(jiàn)git是以一個(gè)commit為根察郁,拉起對(duì)應(yīng)的tree衍慎,以及下面的各個(gè)blob和tree,就如同拎葡萄一樣皮钠,作者給了一個(gè)圖很形象
對(duì)于歷史commit可以使用git checkout+commit號(hào)重新拎起來(lái)歷史提交版本稳捆,例如獲取上一次提交的整個(gè)tree
/tmp/git-practice$ git checkout 6b74d34e6ed26227a9a017006c8574933134b7f8
Note: switching to '6b74d34e6ed26227a9a017006c8574933134b7f8'.
You are in 'detached HEAD' state. You can look around, make experimental
changes and commit them, and you can discard any commits you make in this
state without impacting any branches by switching back to a branch.
If you want to create a new branch to retain commits you create, you may
do so (now or later) by using -c with the switch command. Example:
git switch -c <new-branch-name>
Or undo this operation with:
git switch -
Turn off this advice by setting config variable advice.detachedHead to false
HEAD is now at 6b74d34 alter commit
此時(shí)再看工作目錄,testa.txt和doc目錄已經(jīng)被刪除
/tmp/git-practice$ ls
amend.txt config.yml d.txt eee.txt f.txt test
使用分支
在多人開(kāi)發(fā)的時(shí)候麦轰,想以現(xiàn)有的版本之上增加新的功能或者修改Bug乔夯,或者嘗試新的做法可以另外做一個(gè)分支,鞥確認(rèn)沒(méi)問(wèn)題再合并回來(lái)款侵。
分支簡(jiǎn)單使用
(1)查看分支和默認(rèn)分支master
使用git branch
來(lái)查看現(xiàn)有的分支
$ git branch
* master
Git會(huì)默認(rèn)設(shè)置一個(gè)叫做master
的分支末荐,前面的*
代表現(xiàn)在正在這個(gè)分支上。
(2)創(chuàng)建新分支
如果要新增分支新锈,需要再git branch后面加上分支名稱
$ git branch cat
$ git branch
cat
* master
(3)分支修改名稱
git branch -m 原名稱 現(xiàn)名稱
來(lái)改名
$ git branch -m cat dog
$ git branch
dog
* master
master分支也可以改名字
$ git branch -m master new_master
$ git branch
dog
* new_master
(4)刪除分支
刪除分支加上-d
參數(shù)
$ git branch -d dog
Deleted branch dog (was ad5f2e2).
$ git branch
* new_master
當(dāng)前所在分支不能刪除鞠评,只能刪除其他分支
$ git branch cat
$ git branch
cat # 創(chuàng)建了新分支cat
* new_master
$ git branch -d new_master
error: Cannot delete branch 'new_master' checked out at 'D:/git-practice' # 直接刪除當(dāng)前分支報(bào)錯(cuò)
$ git checkout cat
Switched to branch 'cat' # 切換到cat分支
$ git branch
* cat
new_master
$ git branch -d new_master
Deleted branch new_master (was ad5f2e2).
$ git branch # new_master分支已經(jīng)被刪除
* cat
(5)切換分支
使用git checkout+分支名
# 切換到cat分支
$ git checkout cat
(6)切換分支的理解
先理解一下master和HEAD,master是git的默認(rèn)分支僅此而已沒(méi)有什么其他特殊壕鹉。在項(xiàng)目目錄下第一次commit之后git默認(rèn)分配一個(gè)master分支,使用git log
查看提交記錄聋涨,查看commit id
$ git log --oneline
787dad4 (HEAD -> master) first commit
787dad4 是commit id晾浴,HEAD表示當(dāng)前分支,master是一個(gè)分支名稱指向787dad4 這次commit牍白,三者關(guān)系如圖
master分支指向commit id茂腥,HEAD指向master分支表示master分支是當(dāng)前所在的分支狸涌。此時(shí)再在當(dāng)前分支下再提交一次commit,比如修改了a.txt
$ echo "1" > a.txt
$ git add .
warning: LF will be replaced by CRLF in a.txt.
The file will have its original line endings in your working directory
$ git commit -m "second commit"
[master b46bb64] second commit
1 file changed, 1 insertion(+)
$ git log --oneline
b46bb64 (HEAD -> master) second commit
787dad4 first commit
master指向了新提交的b46bb64 commit最岗,HEAD也跟著master再走帕胆,如圖
相當(dāng)于master是一個(gè)標(biāo)簽,指向最新一次提交的commit id般渡,HEAD也是一個(gè)標(biāo)簽懒豹,標(biāo)記哪一個(gè)分支是當(dāng)下的分支芙盘,當(dāng)下項(xiàng)目只有一個(gè)master分支因此肯定指向master,因此master和HEAD是兩個(gè)不同的作用脸秽,master就是一個(gè)分支名稱儒老,一個(gè)分支可以叫任意名稱,只不過(guò)master是git的默認(rèn)分支名记餐,HEAD的作用是標(biāo)記哪一個(gè)分支是當(dāng)下分支驮樊。master和HEAD就像貼紙一樣貼在某個(gè)commit id上。
引入多個(gè)分支理解一下片酝,新建一個(gè)分支cat囚衔,并且修改a.txt再提交一次
$ git branch cat
$ git branch
cat
* master
新建一個(gè)一個(gè)叫cat的分支,HEAD還是再master钠怯,現(xiàn)在切換分支
$ git checkout cat
Switched to branch 'cat'
$ git branch
* cat
master
*號(hào)走到cat佳魔,當(dāng)前分支為cat,看一下此時(shí)的提交日志
$ git log --oneline
b46bb64 (HEAD -> cat, master) second commit
787dad4 first commit
此時(shí)有兩個(gè)分支指向最新的提交commit號(hào)晦炊,分別是cat和master鞠鲜,此時(shí)的關(guān)系圖如下
此時(shí)修改一下a.txt,以cat分支的身份再提交一次
$ git ls-files -s
100644 d00491fd7e5bb6fa28c517a0bb32b8b506539d4d 0 a.txt
沒(méi)修改之前a.txt內(nèi)容是1断国,生成的blob號(hào)是d00491fd7e5bb6fa28c517a0bb32b8b506539d4d 贤姆,此時(shí)修改輸入2
$ echo "2" > a.txt
$ git ls-files -s
100644 0cfbf08886fca9a91cb753ec8734c84fcbe52c9f 0 a.txt
由于內(nèi)容修改因此重新生成一個(gè)blob,老的blob也存在不覆蓋稳衬,兩個(gè)都可以查看
$ git cat-file -p 0cfbf08886fca9a91cb753ec8734c84fcbe52c9f
2
$ git cat-file -p d00491fd7e5bb6fa28c517a0bb32b8b506539d4d
1
印證了之前說(shuō)的只要內(nèi)容有任何改動(dòng)霞捡,git就會(huì)新生成一個(gè)blob。此時(shí)以cat身份提交一次
$ git commit -m "cat commit"
[cat 3e92641] cat commit
1 file changed, 1 insertion(+), 1 deletion(-)
$ git log --oneline
3e92641 (HEAD -> cat) cat commit
b46bb64 (master) second commit
787dad4 first commit
此時(shí)cat跟著最新的commit走了薄疚,HEAD繼續(xù)跟著cat碧信,并且脫離master,示意圖如下
從圖上來(lái)看cat分支和master分支并不是獨(dú)立的街夭,而是同一個(gè)樹(shù)結(jié)構(gòu)砰碴,新的分支不復(fù)制原有分支,而是再原始樹(shù)上的子節(jié)點(diǎn)長(zhǎng)出新的節(jié)點(diǎn)板丽,已經(jīng)分支標(biāo)簽和HEAD標(biāo)簽的移動(dòng)呈枉,切換不同分支就是選擇不同的樹(shù)路徑,看一下如果切回master分支
$ git checkout master
Switched to branch 'master'
$ ls
a.txt
$ cat a.txt
1
此時(shí)a.txt又恢復(fù)為1了埃碱,原因是猖辫,master指向的是b46bb64,b46bb64指向的tree對(duì)象下的blob文件是d00491fd7e5bb6fa28c517a0bb32b8b506539d4d
(7)切換分支發(fā)生了什么
切換分支做了兩件事情
- (1)更性暫存區(qū)和工作目錄:切換后會(huì)使用那個(gè)分支指向的commit的內(nèi)容來(lái)更新暫存區(qū)和工作目錄砚殿,commit的內(nèi)容就是以commit為根拉起他所指向的tree和blob對(duì)象啃憎。
- (2)更改HEAD的位置:切換之后HEAD跟著新切換的分支走,底層會(huì)修改./git/HEAD文件似炎,實(shí)際上就是修改refs/heads/master的值為該分支指向的commit
另外如果切換到分支A后修改的內(nèi)容沒(méi)有commit荧飞,然后直接又切換了分支B凡人,則修改的內(nèi)容會(huì)隨著切換一起走,修改的內(nèi)容會(huì)到B下叹阔,就是說(shuō)未commit的文件會(huì)在切換時(shí)被攜帶挠轴,直到在哪個(gè)分支下commit位置才固定下來(lái),也就是說(shuō)可以在A分支下修改內(nèi)容耳幢,但是在B分支下提交算做B分支的改動(dòng)內(nèi)容岸晦。
(8)合并分支
新建分支修改和提交經(jīng)過(guò)驗(yàn)證無(wú)誤之后,需要合并回master分支睛藻,切換到master分支下启上,使用git merge
+ 需要合并的分支名稱,一個(gè)例子如下
/tmp/git-practice$ git branch cat
/tmp/git-practice$ git checkout cat
Switched to branch 'cat'
/tmp/git-practice$ vim a.txt
/tmp/git-practice$
/tmp/git-practice$ touch b.txt
/tmp/git-practice$ git add .
/tmp/git-practice$ git commit -m "cat commit"
[cat 4fe449c] cat commit
2 files changed, 1 insertion(+), 1 deletion(-)
create mode 100644 b.txt
以上內(nèi)容是在分支cat下新建了b.txt和修改a.txt店印,然后切回master合并
/tmp/git-practice$ git merge cat
Updating 65d44e4..4fe449c
Fast-forward
a.txt | 2 +-
b.txt | 0
2 files changed, 1 insertion(+), 1 deletion(-)
create mode 100644 b.txt
/tmp/git-practice$ ls
a.txt b.txt
/tmp/git-practice$ cat a.txt
b
合并之后b.txt同樣存在在工作目錄下冈在,且a.txt內(nèi)容也同步了在cat下的改動(dòng),再看一下提交日志
/tmp/git-practice$ git log --oneline
4fe449c (HEAD -> master, cat) cat commit
65d44e4 firse commit
此時(shí)master跟上來(lái)了按摘,本來(lái)master在65d44e4
(9)master合并快轉(zhuǎn)模式與非快轉(zhuǎn)模式
在剛才合并時(shí)打印了日志
/tmp/git-practice$ git merge cat
Updating 65d44e4..4fe449c
Fast-forward
a.txt | 2 +-
b.txt | 0
2 files changed, 1 insertion(+), 1 deletion(-)
create mode 100644 b.txt
其中Fast-forward
表示使用master合并其他分支時(shí)用了快轉(zhuǎn)模式包券,這是git默認(rèn)的模式,他是說(shuō)把master標(biāo)簽從老commit拿下來(lái)直接跟到cat指向的commit炫贤,并沒(méi)有創(chuàng)建一個(gè)額外的commit對(duì)象溅固,可以使用--no-ff
選擇非快轉(zhuǎn)模式,非快轉(zhuǎn)模式會(huì)做出一個(gè)額外的commit對(duì)象指向master的commit和擦頭的commit
/tmp/git-practice$ git merge cat --no-ff -m "merge cat to master"
Merge made by the 'recursive' strategy.
a.txt | 2 +-
b.txt | 0
2 files changed, 1 insertion(+), 1 deletion(-)
create mode 100644 b.txt
看一下提交日志
/tmp/git-practice$ git log --oneline
ae5c2e1 (HEAD -> master) merge cat to master
ab157bc (cat) cat commit
fc32b71 first commit
新生成了一個(gè)commit兰珍,master跟上侍郭,cat保持在原地,再看一下可視化工具smartgit是如何展示的
smartgit繞了一一個(gè)圈掠河,這種非快轉(zhuǎn)模式可以使得master分支更加干凈亮元,快進(jìn)式合并會(huì)把cat的提交歷史混入到master中,攪亂master的提交歷史唠摹。舉個(gè)例子這是原始模式在心分支下進(jìn)行了兩次新提交
A---B---C cat
/
D---E---F master
這是使用快轉(zhuǎn)模式合并的結(jié)果苹粟,cat的兩次歷史提交都也進(jìn)入master路徑
A---B---C cat
/ master
D---E---F
這是使用非快轉(zhuǎn)模式的提交結(jié)果,他新建了一個(gè)commit G跃闹,master不包含A,B兩次提交毛好,更加干凈
A---B---C cat
/ \
D---E---F-----------G master
(10)刪除分支和恢復(fù)刪除的分支
刪除分支使用git branch -d
+ 分支名望艺,如果一個(gè)分支還沒(méi)有被master合并會(huì)需要使用git branch -D
強(qiáng)制刪除
/tmp/git-practice$ git branch -d cat
Deleted branch cat (was ab157bc).
/tmp/git-practice$ git log --oneline
ae5c2e1 (HEAD -> master) merge cat to master
ab157bc cat commit
fc32b71 first commit
/tmp/git-practice$ git branch
* master
刪除之后git branch查看分支僅有master分支,但是查看log還是有cat指向的commit肌访,刪除分支僅僅是產(chǎn)出commit的分支標(biāo)簽找默,commit文件依舊存在,類似圖示效果
smartgit里面分支綠色陰影也消失吼驶,只有commit的提交信息
因此恢復(fù)分支就是給一個(gè)無(wú)頭蒼蠅的commit繼續(xù)打上一個(gè)分支標(biāo)簽惩激,使用如下語(yǔ)句
/tmp/git-practice$ git branch new_cat ab157bc
/tmp/git-practice$ git branch
* master
new_cat
/tmp/git-practice$ git log --oneline
ae5c2e1 (HEAD -> master) merge cat to master
ab157bc (new_cat) cat commit
fc32b71 first commit
這下又有了店煞,同樣smartgit也更新