Git入門
資料來源:https://www.runoob.com/git/git-tutorial.html、http://git-scm.com/docs
查看Git命令的幫助信息,git –help
1.Git 工作區(qū)、暫存區(qū)和版本庫(以本地舉例)、遠(yuǎn)程倉庫
- 工作區(qū):就是你在電腦里能看到的目錄。
- 暫存區(qū):英文叫 stage 或 index。一般存放在 .git 目錄下的 index 文件(.git/index)中,所以我們把暫存區(qū)有時(shí)也叫作索引(index)。
- 版本庫:工作區(qū)有一個(gè)隱藏目錄 .git,這個(gè)不算工作區(qū),而是 Git 的版本庫。
Git 工作區(qū)、暫存區(qū)和版本庫
- 圖中左側(cè)為工作區(qū),右側(cè)為版本庫。在版本庫中標(biāo)記為 “index” 的區(qū)域是暫存區(qū)(stage/index),標(biāo)記為 “master” 的是 master 分支所代表的目錄樹。
- 圖中我們可以看出此時(shí) “HEAD” 實(shí)際是指向 master 分支的一個(gè)”游標(biāo)”。所以圖示的命令中出現(xiàn) HEAD 的地方可以用 master 來替換。
- 圖中的 objects 標(biāo)識(shí)的區(qū)域?yàn)?Git 的對(duì)象庫,實(shí)際位于 “.git/objects” 目錄下,里面包含了創(chuàng)建的各種對(duì)象及內(nèi)容。
- 當(dāng)對(duì)工作區(qū)修改(或新增)的文件執(zhí)行 git add 命令時(shí),暫存區(qū)的目錄樹被更新,同時(shí)工作區(qū)修改(或新增)的文件內(nèi)容被寫入到對(duì)象庫中的一個(gè)新的對(duì)象中,而該對(duì)象的ID被記錄在暫存區(qū)的文件索引中。
- 當(dāng)執(zhí)行提交操作(git commit)時(shí),暫存區(qū)的目錄樹寫到版本庫(對(duì)象庫)中,master 分支會(huì)做相應(yīng)的更新。即 master 指向的目錄樹就是提交時(shí)暫存區(qū)的目錄樹。
- 當(dāng)執(zhí)行 git reset HEAD 命令時(shí),暫存區(qū)的目錄樹會(huì)被重寫,被 master 分支指向的目錄樹所替換,但是工作區(qū)不受影響。
- 當(dāng)執(zhí)行 git rm –cached 命令時(shí),會(huì)直接從暫存區(qū)刪除文件,工作區(qū)則不做出改變。
- 當(dāng)執(zhí)行 git checkout . 或者 git checkout — 命令時(shí),會(huì)用暫存區(qū)全部或指定的文件替換工作區(qū)的文件。這個(gè)操作很危險(xiǎn),會(huì)清除工作區(qū)中未添加到暫存區(qū)中的改動(dòng)。
- 當(dāng)執(zhí)行 git checkout HEAD . 或者 git checkout HEAD 命令時(shí),會(huì)用 HEAD 指向的 master 分支中的全部或者部分文件替換暫存區(qū)和以及工作區(qū)中的文件。這個(gè)命令也是極具危險(xiǎn)性的,因?yàn)椴坏珪?huì)清除工作區(qū)中未提交的改動(dòng),也會(huì)清除暫存區(qū)中未提交的改動(dòng)。
2.Git文件狀態(tài)
在Git中文件大概分為四種狀態(tài):已修改(modified)、已暫存(staged)、已提交(committed)、未追蹤(Untrack)
- .gitignore內(nèi)的文件,不會(huì)擁有任何一種狀態(tài),被git徹底無視。
- 處于ignore列表的文件,無法被add添加;但是可以強(qiáng)制添加
- 空目錄、以及子目錄全部是空目錄的目錄不會(huì)有Untrack狀態(tài),也無法通過add改變狀態(tài)(無效)
- 工作目錄新增文件時(shí),只要不處于ignore目錄,都會(huì)變成Untrack狀態(tài);
- 沒有add過的文件或者被restore(不帶–staged)的文件,處于Untrack狀態(tài);
- 初次add和被add后產(chǎn)生修改的文件,會(huì)處于modifed狀態(tài)。
- 處于modified狀態(tài)的文件,最開始可以進(jìn)行add和restore兩種操作,此時(shí)的add操作叫做 更新要提交的內(nèi)容,add后變?yōu)閟taged狀態(tài),restore(不加staged標(biāo)記)后變?yōu)閁ntrack;
- add后變?yōu)閟taged狀態(tài)的文件,可用restore –staged 變回modified狀態(tài);這個(gè)staged狀態(tài)的內(nèi)容可以用來恢復(fù)內(nèi)容。沒有被add的modified狀態(tài)文件內(nèi)容沒有被記錄(雖然有撤回,但是本質(zhì)不一樣);
- 處于staged狀態(tài)的文件,在沒有commit之前再次產(chǎn)生修改時(shí),會(huì)同時(shí)具有staged和modified兩個(gè)狀態(tài)(可以把statged狀態(tài)的內(nèi)容拉回來,覆蓋。);但是commit時(shí)會(huì)使用內(nèi)容最新的那個(gè)狀態(tài);
- commit會(huì)提交所有staged狀態(tài)的文件,所以commit可以理解有一個(gè)modified到staged狀態(tài)的過程(實(shí)際可能不存在,因?yàn)闀捍鎱^(qū)本來就有變動(dòng)的記錄);所以暫存狀態(tài)不能理解為處于暫存區(qū),應(yīng)當(dāng)指的是被納入下一次提交的文件;任何被追蹤的產(chǎn)生修改的文件都會(huì)在暫存區(qū)被記錄;成為下一次提交的一部分;
- 未被追蹤的文件被刪除時(shí),不會(huì)產(chǎn)生git狀態(tài)。處于modofy未add時(shí),會(huì)變成deleted狀態(tài);處于staged狀態(tài)會(huì)保持暫存狀態(tài);
- 已經(jīng)被刪除的(deleted狀態(tài))被追蹤的文件,恢復(fù)后會(huì)變成modified狀態(tài);
提示:add的作用是將文件添加到暫存區(qū),只有被add的文件才會(huì)被追蹤
暫存區(qū)
(1)所謂的暫存區(qū)只是一個(gè)簡(jiǎn)單的索引文件而已。(2)暫存區(qū)這個(gè)索引文件里面包含的是文件的目錄樹,像一個(gè)虛擬的工作區(qū),在這個(gè)虛擬工作區(qū)的目錄樹中,記錄了文件名、文件的時(shí)間戳、文件長(zhǎng)度、文件類型以及最重要的SHA-1值,文件的內(nèi)容并沒有存儲(chǔ)在其中,所以說 它像一個(gè)虛擬的工作區(qū)。(3)索引指向的是.Git/objects下的文件。(4)暫存區(qū)的作用:除非是繞過暫存區(qū)直接提交,否則Git想把修改提交上去,就必須將修改存入暫存區(qū)最后才能commit。每次提交的是暫存區(qū)所對(duì)應(yīng)的文件快照。
拓展:status提示信息
Changes not staged for commit: (use “git add …” to update what will be committed) (use “git checkout — …” to discard changes in working directory)
- 既然是Changes not staged for commit,就說明出現(xiàn)這個(gè)提示下的所有文件改動(dòng),都是存在于工作區(qū)的。stage是暫存區(qū)的意思,not stage說明都不在暫存區(qū),那么說明在工作區(qū)。
- (use “git add …” to update what will be committed)。執(zhí)行這條命令就可以工作區(qū)里面的改變加入到暫存區(qū)??梢詧?zhí)行g(shù)it add .把當(dāng)前目錄下所有改動(dòng)加入暫存區(qū)。
- (use “git checkout – …” to discard changes in working directory)。執(zhí)行這條命令將丟棄在工作區(qū)的改動(dòng)??梢詧?zhí)行g(shù)it checkout *把當(dāng)前目錄下所有工作區(qū)的改動(dòng)丟棄掉
Untracked files: (use “git add …” to include in what will be committed)
- Untracked files,就說明出現(xiàn)這個(gè)提示下的所有文件都是當(dāng)前HEAD沒有被加入過的文件。這種改動(dòng)也屬于工作區(qū)。
- (use “git add …” to include in what will be committed)。把Untracked files加入暫存區(qū)。
On branch masterYour branch is ahead of ‘origin/master’ by 1 commit. (use “git push” to publish your local commits)
- 當(dāng)前分支比遠(yuǎn)程分支多了一次commit
Your branch and ‘origin/master’ have perged, and have 1 and 1 different commits each, respectively
pull報(bào)錯(cuò)了,查看狀態(tài)顯示這個(gè),先留著待解決吧
3.HEAD是什么
HEAD是Git中非常重要的一個(gè)概念,你可以稱它為指針或者引用,它可以指向任意一個(gè)節(jié)點(diǎn),并且指向的節(jié)點(diǎn)始終為當(dāng)前工作目錄,換句話說就是當(dāng)前工作目錄(也就是你所看到的代碼)就是HEAD指向的節(jié)點(diǎn)。
4.git重命名檢測(cè)
Git 采用了不同的方法:它沒有選擇去存儲(chǔ)與文件移動(dòng)操作相關(guān)的信息,而是采用了重命名檢測(cè)算法。在該算法中,如果一個(gè)文件在某一次提交中消失了,它依然會(huì)存在于其前次提交,而如果某個(gè)擁有相同名字或相似內(nèi)容的文件出現(xiàn)在了另一個(gè)位置,Git 就會(huì)自動(dòng)檢測(cè)到。如果是這種情況,Git 就會(huì)假定該文件被移動(dòng)過了。
Git項(xiàng)目文件說明
Git init后主要有兩個(gè)重要的文件和目錄:.git目錄和.gitignore
1. .gitignore
.gitignore文件存在于根目錄(與.git同級(jí)的目錄)用于在將文件提交到git暫存區(qū)時(shí),指定將哪些文件排除;
有時(shí)候你想添加(git add)一個(gè)文件到Git,但發(fā)現(xiàn)添加不了,多半原因是這個(gè)文件被.gitignore忽略了
git add .不會(huì)添加被.gitignore忽視的文件,而git add -f . 強(qiáng)制添加所有文件,即使是.gitignore忽視的文件也添加。
當(dāng).gitignore文件不是你編寫的,但是它編寫的不符合實(shí)際需求,你可以使用git check-ignore命令進(jìn)行檢查,看是哪一個(gè)規(guī)則有問題了
#檢測(cè) git check-ignore -v App.class#結(jié)果.gitignore:3:*.classApp.class
.gitignore只能忽略那些原來沒有被track的文件,如果某些文件已經(jīng)被納入了版本管理中,則修改.gitignore是無效的。解決方法就是先把本地緩存刪除(改變成未track狀態(tài)),然后再提交。
git rm -r –cached .git add .git commit -m ‘update .gitignore’
也可以手動(dòng)指定一個(gè)文件作為git忽略文件
git config core.excludesfile ***
對(duì)于全局Git配置,可以使用如下命令對(duì)全部倉庫進(jìn)行配置。
git config –global core.excludesfile **/.gitignore(文件相對(duì)或絕對(duì)位置)
忽略規(guī)則如下:
各種項(xiàng)目的gitignore
參考地址:https://github.com/github/gitignore
2. .git目錄
任意文件夾中,用 git init 命令初始化倉庫,即可在此文件夾下創(chuàng)建 .git 文件夾(.打頭為隱藏文件夾,所以平時(shí)可能看不到)。這個(gè)文件夾之外的部分叫做工作區(qū)(Working Directory),.git 文件夾我們稱做 Git倉庫 (Git Repository)。 通常會(huì)有7個(gè)文件5個(gè)目錄,常見目錄如下:
COMMIT_EDITMSGHEADORIG_HEADFETCH_HEADconfigdescriptionindexhooks/info/logs/objects/refs/
1. 文件 COMMIT_EDITMSG
此文件是一個(gè)臨時(shí)文件,存儲(chǔ)最后一次提交的信息內(nèi)容,git commit 命令之后打開的編輯器就是在編輯此文件,而你退出編輯器后,git 會(huì)把此文件內(nèi)容寫入 commit 記錄。
實(shí)際應(yīng)用: git pull 遠(yuǎn)程倉庫后,新增了很多提交,淹沒了本地提交記錄,直接 cat .git/COMMIT_EDITMSG 就可以弄清楚最后工作的位置了。
2. HEAD
此文件永遠(yuǎn)存儲(chǔ)當(dāng)前位置指針,就像 linux 中的 $PWD 變量和命令提示符的箭頭一樣,永遠(yuǎn)指向當(dāng)前位置,表明當(dāng)前的工作位置。在 git 中 HEAD 永遠(yuǎn)指向當(dāng)前正在工作的那個(gè) commit。(孤立HEAD?????)
HEAD 存儲(chǔ)一個(gè)分支的 ref,Linux中運(yùn)行:cat .git/HEAD 通常會(huì)顯示:
ref: refs/heads/master
這說明你目前正在 master 分支工作。此時(shí)你的任何 commit,默認(rèn)自動(dòng)附加到 master 分支之上
git cat-file -p HEAD, 顯示詳細(xì)的提交信息:
tree 4cbb261560348e1727b5137f3ab6eceae8e1f34dparent 22c457fe24f737505356edfb8696c7e50fd9d971author Evan You 1654857613 +0800committer Evan You 1654857613 +0800chore: test pass
孤立head,不指向任何commit
3. ORIG_HEAD
正因?yàn)?HEAD 比較重要,此文件會(huì)在你進(jìn)行危險(xiǎn)操作時(shí)備份 HEAD,如以下操作時(shí)會(huì)觸發(fā)備份
git resetgit mergegit rebasegit pull
此文件應(yīng)用示例
# 回滾到上一次的狀態(tài)(慎用!!!)git reset –hard ORIG_HEAD
4. FETCH_HEAD
這個(gè)文件作用在于追蹤遠(yuǎn)程分支的拉取與合并,與其相關(guān)的命令有 git pull/fetch/merge,而git pull 命令相當(dāng)于執(zhí)行以下兩條命令:
$ git fetch$ git merge FETCH_HEAD# 顯示如下>>>From https://github.com/xxx/xxxx* branch master -> FETCH_HEADUpdating f785638..59db1b2
此時(shí)會(huì)默默備份 HEAD 到 ORIG_HEAD
5. config
此文件存儲(chǔ)項(xiàng)目本地的 git 設(shè)置,典型內(nèi)容如下:
[core] repositoryformatversion = 0 filemode = true bare = false logallrefupdates = true ignorecase = true[remote “origin”] url = git@gitlab.xxxx.com/xxx.git fetch = +refs/heads/*:refs/remotes/origin/*[branch “master”] remote = origin merge = refs/heads/master[branch “v2.6.0”] remote = origin merge = refs/heads/v2.6.0[branch “v2.8.0”] remote = origin merge = refs/heads/v2.8.0
[core] 段的內(nèi)容跟 git config 命令對(duì)應(yīng)
執(zhí)行以下命令:
git config user.name abcgit config user.email abc@abc.com
會(huì)在 config 文件中追加以下內(nèi)容:
… …[user]name = abcemail = abc@abc.com
git config –global 影響的則是全局配置文件 ~/.gitconfig。
[remote] 段表示遠(yuǎn)程倉庫配置
[branch] 段表示分支同步設(shè)置
假設(shè)當(dāng)前在 master 分支,執(zhí)行 git pull 若出現(xiàn)以下提示:
There is no tracking information for the current branch.Please specify which branch you want to merge with.See git-pull(1) for details.
git pull 就說明 .git/config 文件缺少對(duì)應(yīng)的 [branch “master”] 字段。
解決方案為:
git branch -u origin/master master# 或者執(zhí)行一次 pushgit push -u origin master
會(huì)出現(xiàn)提示:
Branch ‘master’ set up to track remote branch ‘master’ from ‘origin’.
其實(shí)就是生成以下內(nèi)容在 .git/config中:
[branch “master”]remote = originmerge = refs/heads/master
手動(dòng)編輯 .git/config,效果一樣。這就是 upstream 的真正含義,即生成 config 中的這段配置。
6. description
說明這個(gè)文件主要用于 GitWeb 的描述,如果要啟動(dòng) GitWeb 可用如下命令:
# 確保lighttpd已安裝: brew install lighttpd$ git instaweb –start
默認(rèn)會(huì)啟動(dòng) lighttpd 服務(wù)并打開瀏覽器 http://127.0.0.1:1234 (試著改成對(duì)外IP并分享給別人?)
以下顯示當(dāng)前的 git 倉庫名稱以及描述,默認(rèn)的描述如下:
默認(rèn)描述
上面這段話就是默認(rèn)的 description 文件的內(nèi)容,編輯這個(gè)文件來讓你 GitWeb 描述更友好。
7. hooks/目錄
存放 git hooks,用于在 git 命令前后做檢查或做些自定義動(dòng)作。運(yùn)行 ls -F1 .git/hooks
prepare-commit-msg.sample # git commit 之前,編輯器啟動(dòng)之前觸發(fā),傳入 COMMIT_FILE,COMMIT_SOURCE,SHA1commit-msg.sample # git commit 之前,編輯器退出后觸發(fā),傳入 COMMIT_EDITMSG 文件名pre-commit.sample # git commit 之前,commit-msg 通過后觸發(fā),譬如校驗(yàn)文件名是否含中文pre-push.sample # git push 之前觸發(fā)pre-receive.sample # git push 之后,服務(wù)端更新 ref 前觸發(fā)update.sample # git push 之后,服務(wù)端更新每一個(gè) ref 時(shí)觸發(fā),用于針對(duì)每個(gè) ref 作校驗(yàn)等post-update.sample # git push 之后,服務(wù)端更新 ref 后觸發(fā)pre-rebase.sample # git rebase 之前觸發(fā),傳入 rebase 分支作參數(shù)applypatch-msg.sample # 用于 git am 命令提交信息校驗(yàn)pre-applypatch.sample # 用于 git am 命令執(zhí)行前動(dòng)作fsmonitor-watchman.sample # 配合 core.fsmonitor 設(shè)置來更好監(jiān)測(cè)文件變化
參考
https://git-scm.com/docs/githooks
如果要啟用某個(gè) hook,只需把 .sample 刪除即可,然后編輯其內(nèi)容來實(shí)現(xiàn)相應(yīng)的邏輯。
比如要校驗(yàn)每個(gè) commit message 至少要包含兩個(gè)單詞,否則就提示并拒絕提交,將 commit-msg.sample 改為 commit-msg 后,編輯如下:
#!/bin/shgrep -q ‘Ss+S’ $1 || { echo ‘提交信息至少為兩個(gè)單詞’ && exit 1; }
這樣當(dāng)提交一個(gè) commit 時(shí),會(huì)執(zhí)行 bash 命令: .git/hooks/commit-msg .git/COMMIT_EDITMSG,退出值不為 0,就拒絕提交。
8. info/目錄
此文件夾基本就有兩個(gè)文件:
9. logs/目錄
記錄了操作信息,git reflog 命令以及像 HEAD@{1} 形式的路徑會(huì)用到。如果刪除此文件夾(危險(xiǎn)?。?,那么依賴于 reflog 的命令就會(huì)報(bào)錯(cuò)。
文件夾 objects/
此文件夾簡(jiǎn)單說,就是 git的數(shù)據(jù)庫,運(yùn)行 tree .git/objects,可以看到目錄結(jié)構(gòu):
.git/objects/|– 0c| `– d370696b581c38ee01e62b148a759f80facc2d|– 59| `– 3d5b490556791212acd5a516a37bbfa05d44dd|– 61| `– be44eedde61d723e5761577a2b420ba0fc2794|– 64| `– c0aed8ddcbb546bdcec2848938fc82348db227|– d4| `– 9904676ce8ddde276bdbfa9bbec313e90e0f50|– info`– pack |– pack-75e3f2aa378752ec93a8e9f375f01204d498605b.idx `– pack-75e3f2aa378752ec93a8e9f375f01204d498605b.pack
這些文件分兩種形式:pack壓縮包 形式放在 pack/ 目錄下,除此之外都是 hash文件 形式,被叫做 loost objects。
這個(gè)文件夾以及相應(yīng)的算法,我沒找到獨(dú)立的名稱,就叫它 hash-object 體系吧。因?yàn)榇_實(shí)有個(gè) git hash-object 命令存在,是一個(gè)底層的負(fù)責(zé)生成這些 loost objects 文件,如果要看到這些文件各自的含義,執(zhí)行以下命令:
git cat-file –batch-check –batch-all-objects
可以看到
04c87c65f142f33945f2f5951cf7801a32dfa240 commit 194098217953a6ca169bed33d2be8a07d584fcdaf30 tree 310cd370696b581c38ee01e62b148a759f80facc2d commit 2452a810017bfc85d7db2627f4aabdaa1583212bda3 blob 193920a07c1d5694df6b8658592b0939241d70e9e5 tree 93593d5b490556791212acd5a516a37bbfa05d44dd tag 14861be44eedde61d723e5761577a2b420ba0fc2794 tree 154… …
但你會(huì)發(fā)現(xiàn)這個(gè)列表里有些值在文件夾中并不存在,因?yàn)槌?loost objects 它還匯總了 pack 文件中的內(nèi)容。
hash文件
又稱為 loose object,文件名稱共由40字符的 SHA-1 hash 值組成,其中前兩個(gè)字符為文件夾分桶,后38個(gè)字符為文件名稱。
按文件內(nèi)容可分為四種類型:commit, tree, blob, tag,若執(zhí)行以下命令會(huì)生成所有四種類型:
echo -en ‘xx’ > xx # 共 3 個(gè)字符git add .git commit -m ‘update xx’git tag -a ‘v1.0’ -m ‘release: 1.0.0’
經(jīng)過以上操作后,對(duì)比一下文件樹,發(fā)現(xiàn)多了四個(gè) hash文件:
|– 0c| `– d370696b581c38ee01e62b148a759f80facc2d|– 18| `– 143661f96845f11e0b4ab7312bdc0f356834ce|– 30| `– 20feea86d222d83218eb3eb5aa9f58f73df04d|– 59| `– 3d5b490556791212acd5a516a37bbfa05d44dd|– 61| `– be44eedde61d723e5761577a2b420ba0fc2794|– 64| `– c0aed8ddcbb546bdcec2848938fc82348db227|– ad| `– f4c9afac7afae3ff3e95e6c4eefe009d547f00|– cc| `– c9bd67dc5c467859102d53d54c5ce851273bdd|– d4| `– 9904676ce8ddde276bdbfa9bbec313e90e0f50|– info`– pack|– pack-75e3f2aa378752ec93a8e9f375f01204d498605b.idx`– pack-75e3f2aa378752ec93a8e9f375f01204d498605b.pack
這四個(gè) hash文件 分別是:
cc/c9bd67dc5c467859102d53d54c5ce851273bdd # blob30/20feea86d222d83218eb3eb5aa9f58f73df04d # commitad/f4c9afac7afae3ff3e95e6c4eefe009d547f00 # tree18/143661f96845f11e0b4ab7312bdc0f356834ce # tag
其實(shí)這些文件都經(jīng)過了壓縮,壓縮形式為 zlib。先安裝一下解壓工具 macOS 版 brew install pigz 或 windows 版 pigz,后執(zhí)行:
$ pigz -d >>>(注意xx后有個(gè))blob 3xx$pigz -d >>>commit 248tree adf4c9afac7afae3ff3e95e6c4eefe009d547f00parent 0cd370696b581c38ee01e62b148a759f80facc2dauthor jamesyang.yjm 1562044880 +0800committer jamesyang.yjm 1562044880 +0800update xx$ pigz -d >>>tree 154100644 abc*???]}?bJ??X2??100644 asdf???CK?)?wZ???S?100644 iou???CK?)?wZ???S?100644 xx??g?FxY-S?L?Q’;?100644 yy???CK?)?wZ???S?$ pigz -d >>>tag 155object 3020feea86d222d83218eb3eb5aa9f58f73df04dtype committag v1.0tagger jamesyang.yjm 1562045942 +0800release: 1.0.0
會(huì)發(fā)現(xiàn),顯示結(jié)果都是 type size+內(nèi)容 形式,這就是 object 文件的存儲(chǔ)格式:
[type] [size][NULL][content]
type 可選值:commit, tree, blob, tag,NULL 就是C語言里的字符結(jié)束符:,size 就是 NULL后內(nèi)容的字節(jié)長(zhǎng)度。
type 的幾種類型可以使用 git cat-file -t hash 看到,內(nèi)容可以用 git cat-file -p hash 看到。
git cat-file -t ccc9bd67dc5c467859102d53d54c5ce851273bdd# 顯示結(jié)果為>>>>blobgit cat-file -p ccc9bd67dc5c467859102d53d54c5ce851273bdd# 顯示結(jié)果為>>>>xx
所以 blob 文件就是對(duì)原文件內(nèi)容的全量拷貝,同時(shí)前面加了 blob size,而文件名稱的 hash 值計(jì)算是計(jì)算整體字符的 SHA-1 值:
echo -en ‘blob 3xx’ | shasum# 顯示結(jié)果為>>>>ccc9bd67dc5c467859102d53d54c5ce851273bdd –
知道原理后,其它類型格式請(qǐng)自行參考 斯坦福 Ben Lynn 所著的 GitMagic。
所以,當(dāng)我們 git show 3020feea86d222d83218eb3eb5aa9f58f73df04d 時(shí),會(huì)發(fā)生些什么?
找到 3020feea86d222d83218eb3eb5aa9f58f73df04d 這個(gè) commit,顯示出來找到此 commit 關(guān)聯(lián)的 tree object: adf4c9afac7afae3ff3e95e6c4eefe009d547f00,拉取相應(yīng)的 blob 文件,并與當(dāng)前工作區(qū)內(nèi)的文件做 diff,然后顯示出來
這就是 objects/ 文件夾作為 git數(shù)據(jù)庫 被使用的真實(shí)例子。
pack文件
為什么會(huì)有 .pack 文件?
由于每次 commit 都會(huì)生成許多 hash文件,并且由于 blob 文件都是全量存儲(chǔ)的,導(dǎo)致 git 效率下降,于是有了 pack-format,優(yōu)勢(shì):
對(duì)于大倉庫存儲(chǔ)效率高利于網(wǎng)絡(luò)傳輸,便于備份增量存儲(chǔ),優(yōu)化磁盤空間將 .git/objects 下的部分文件打包成 pack格式
$ tree .git/objects/ | wc -l311$ git gcEnumerating objects: 288, done.Counting objects: 100% (288/288), done.Delta compression using up to 4 threadsCompressing objects: 100% (287/287), done.Writing objects: 100% (288/288), done.Total 288 (delta 131), reused 90 (delta 0)$ tree .git/objects/ | wc -l12
可以看到文件數(shù)量減小了不少,其中大部分文件被打到一個(gè) .pack 包中,并且是增量存儲(chǔ),有部分變更的文件只存儲(chǔ) 基礎(chǔ)hash 變更內(nèi)容,磁盤空間優(yōu)化很明顯。
git gc 其實(shí)運(yùn)行了兩條命令:git repack 用來打包 和 git prune-packed 用來移除已打包的 hash文件;
11.文件夾refs
refs 可以理解成文件系統(tǒng)中的 symbol link,看下結(jié)構(gòu):
$ tree .git/refs/.git/refs|– heads| `– master`– tags`– v1.0$ cat .git/refs/heads/master5978c2c79cd3a4711fb8edd3166c9f9f5c8c97f5$ cat .git/refs/tags/v1.05978c2c79cd3a4711fb8edd3166c9f9f5c8c97f5$ git cat-file -t 5978c2c79cd3a4711fb8edd3166c9f9f5c8c97f5commit
可以看到 master 和 v1.0 都指向 5978c2c79cd3a4711fb8edd3166c9f9f5c8c97f5 這個(gè) commit。
refs/heads/ 文件夾內(nèi)的 ref 一般通過 git branch 生成。git show-ref –heads 可以查看。
refs/tags/ 文件夾內(nèi)的 ref 一般通過 git tag 生成。git show-ref –tags 可以查看。
如下:
$ git branch abc$ tree .git/refs/.git/refs/|– heads| |– abc| `– master`– tags`– v1.0$ cat .git/refs/heads/abc5978c2c79cd3a4711fb8edd3166c9f9f5c8c97f5
說明新建分支其實(shí)就是生成了一個(gè)指向某個(gè) commit 的 symbol link,當(dāng)然在這里叫做 ref。
而 git tag 命令本質(zhì)與 git branch 相同,只生成一個(gè) ref 放在 tags 目錄下,所以被稱為 lightweight tag。
而 git tag -a xx 命令會(huì)首先生成一個(gè)類型為 tag 的 hash文件 放到 objects/ 目錄,然后生成 ref 放到 tags 目錄下指向那個(gè)文件。這就叫做 annotated tag,好處是可包含一些元信息如 tagger 和 message,被 git 的 hash-object 算法管理,可被 GPG 簽名等,所以更穩(wěn)定,更安全。
使用以下命令來拿到 refs 文件夾存儲(chǔ)的信息:
$ git show-ref –head –dereference5978c2c79cd3a4711fb8edd3166c9f9f5c8c97f5 HEAD5978c2c79cd3a4711fb8edd3166c9f9f5c8c97f5 refs/heads/abc5978c2c79cd3a4711fb8edd3166c9f9f5c8c97f5 refs/heads/master5978c2c79cd3a4711fb8edd3166c9f9f5c8c97f5 refs/tags/v1.05e84371048faa20412f5492e6af264a7e1edfec1 refs/tags/xx5978c2c79cd3a4711fb8edd3166c9f9f5c8c97f5 refs/tags/xx^{}
我們來看這些信息如何變化的:
$ touch new_file && git add . && git commit -m ‘add new_file'[master 44b0d05] add new_file1 file changed, 0 insertions(+), 0 deletions(-)create mode 100644 new_file$ git show-ref –head –dereference44b0d05ddadaaa8d2cc40d6647cc474b26f5d8d3 HEAD5978c2c79cd3a4711fb8edd3166c9f9f5c8c97f5 refs/heads/abc44b0d05ddadaaa8d2cc40d6647cc474b26f5d8d3 refs/heads/master5978c2c79cd3a4711fb8edd3166c9f9f5c8c97f5 refs/tags/v1.05e84371048faa20412f5492e6af264a7e1edfec1 refs/tags/xx5978c2c79cd3a4711fb8edd3166c9f9f5c8c97f5 refs/tags/xx^{}
diff 一下可以看到:
5978c2c79cd3a4711fb8edd3166c9f9f5c8c97f5 HEAD5978c2c79cd3a4711fb8edd3166c9f9f5c8c97f5 refs/heads/master
這兩行發(fā)生了變化。也就是每次 commit 時(shí),HEAD 與 heads 都會(huì)自動(dòng)更新。
12. index文件
文件保存成二進(jìn)制對(duì)象以后,還需要通知 Git 哪些文件發(fā)生了變動(dòng)。所有變動(dòng)的文件,Git 都記錄在一個(gè)區(qū)域,叫做”暫存區(qū)”(英文叫做 index 或者 stage)。等到變動(dòng)告一段落,再統(tǒng)一把暫存區(qū)里面的文件寫入正式的版本歷史。
git update-index命令用于在暫存區(qū)記錄一個(gè)發(fā)生變動(dòng)的文件。
$ git update-index –add –cacheinfo 100644 3b18e512dba79e4c8300dd08aeb37f8e728b8dad test.txt
上面命令向暫存區(qū)寫入文件名test.txt、二進(jìn)制對(duì)象名(哈希值)和文件權(quán)限。
git ls-files命令可以顯示暫存區(qū)當(dāng)前的內(nèi)容。
$ git ls-files –stage100644 3b18e512dba79e4c8300dd08aeb37f8e728b8dad 0 test.txt
上面代碼表示,暫存區(qū)現(xiàn)在只有一個(gè)文件test.txt,以及它的二進(jìn)制對(duì)象名和權(quán)限。知道了二進(jìn)制對(duì)象名,就可以在.git/objects子目錄里面讀出這個(gè)文件的內(nèi)容。
git status命令會(huì)產(chǎn)生更可讀的結(jié)果。
$ git status要提交的變更: 新文件: test.txt
上面代碼表示,暫存區(qū)里面只有一個(gè)新文件test.txt,等待寫入歷史。
資料來源
參考:https://developer.aliyun.com/article/716483
Git遠(yuǎn)程倉庫
Git 并不像 SVN 那樣有個(gè)中心服務(wù)器。目前我們使用到的 Git 命令都是在本地執(zhí)行,如果你想通過 Git 分享你的代碼或者與其他開發(fā)人員合作。 你就需要將數(shù)據(jù)放到一臺(tái)其他開發(fā)人員能夠連接的服務(wù)器上。
1.添加遠(yuǎn)程倉庫
git remote add [shortname] [url] #添加遠(yuǎn)程倉庫git remote rm name # 刪除遠(yuǎn)程倉庫git remote rename old_name new_name # 修改倉庫名
2.查看遠(yuǎn)端倉庫
$ git remoteorigin$ git remote -vorigin git@github.com:tianqixin/runoob-git-test.git (fetch)origin git@github.com:tianqixin/runoob-git-test.git (push)
3.獲取遠(yuǎn)端倉庫代碼 git fetch
# 只能fetch到一個(gè)空白的分支,然后可以手動(dòng)merge$ git fetch :
不填的話都是默認(rèn)
4.拉取 git pull
git pull :# 允許合并不相關(guān)的分支 $ git pull –allow-unrelated-histories
git pull操作其實(shí)是git fetch 與 git merge 兩個(gè)命令的集合。 git fetch 和 git merge FETCH_HEAD 的簡(jiǎn)寫。
相關(guān)文檔:https://www.runoob.com/git/git-remote-repo.html
5.推送 git push
# 基本$ git push :# 強(qiáng)制推送$ git push –force origin master# 刪除遠(yuǎn)程分支$ git push origin –delete master# 允許合并不相關(guān)的分支$ git push –allow-unrelated-histories
提示
如果另一個(gè)開發(fā)者在我們之前已經(jīng)做過一次 push 操作,此次 push 命令就會(huì)被拒絕傳送提交。這時(shí)候,我們必須要先做一次 pull 操作,將其他人新上載的更新取回,并本地合并。
如果本地分支名與遠(yuǎn)程分支名相同,則可以省略冒號(hào),帶上-u 參數(shù)相當(dāng)于記錄了push到遠(yuǎn)端分支的默認(rèn)值,這樣當(dāng)下次我們還想要繼續(xù)push的這個(gè)遠(yuǎn)端分支的時(shí)候推送命令就可以簡(jiǎn)寫成git push即可。
Git 分支
1.創(chuàng)建分支命令 git branch
# 創(chuàng)建分支$ git branch # 創(chuàng)建分支并跟蹤遠(yuǎn)程分支$ git branch -u o/master foo
2.切換分支命令 git checkout
第一作用是切換分支,第二個(gè)是撤銷修改。
# 切換指定分支$ git checkout ||# 創(chuàng)建一個(gè)的分支,它跟蹤遠(yuǎn)程分支$ git checkout -b 本地分支名x origin/遠(yuǎn)程分支名x# 從暫存區(qū)恢復(fù)到工作區(qū)$ git checkout .
提示
當(dāng)你切換分支的時(shí)候,Git 會(huì)用該分支的最后提交的快照替換你的工作目錄的內(nèi)容, 所以多個(gè)分支不需要多個(gè)目錄。
實(shí)際測(cè)試:
假設(shè)分支1 a文件未提交,分支2 a文件已提交。切換到到分支2時(shí)會(huì)替換 分支1的a文件。1切換到2時(shí)也會(huì)替換a文件;
兩個(gè)分支都已經(jīng)提交的 切換時(shí)會(huì)互相替換。一個(gè)提交一個(gè)沒提交時(shí),從a到b,b會(huì)保持a的暫存區(qū)和工作區(qū)
3.合并分支命令 git merge
# 合并指定分支到當(dāng)前分支$ git merge
4.刪除分支 git branch -d
# 刪除指定分支$ git branch -d
5.分支列表 git branch
# 列出所有分支$ git branch# 查看遠(yuǎn)程所有分支$ git branch -r# 查看本地和遠(yuǎn)程所有分支$ git branch -a
列出分支時(shí),帶*號(hào)的分支為當(dāng)前活動(dòng)分支
5.重命名分支 git branch -M
# 重命名指定分支# 不填old默認(rèn)重命名當(dāng)前分支$ git branch -m old new# 強(qiáng)制重命名指定分支$ git branch -M old new
git rebase 變基
1.介紹
Git rebase,通常被稱作變基或衍合, 可以理解為另外一種合并的方式,與merge 會(huì)保留分支結(jié)構(gòu)和原始提交記錄不同,rebase 是在公共祖先的基礎(chǔ)上,把新的提交鏈截取下來,在目標(biāo)分支上進(jìn)行重放,逐個(gè)應(yīng)用選中的提交來完成合并。
不同公司,不同情況有不同使用場(chǎng)景,不過大部分情況推薦如下:
自己?jiǎn)螜C(jī)的時(shí)候,拉公共分支最新代碼的時(shí)候使用rebase,也就是git pull -r或git pull –rebase。這樣的好處很明顯,提交記錄會(huì)比較簡(jiǎn)潔。但有個(gè)缺點(diǎn)就是rebase以后我就不知道我的當(dāng)前分支最早是從哪個(gè)分支拉出來的了,因?yàn)榛鬃兞寺?,所以看個(gè)人需求了。
往公共分支上合代碼的時(shí)候,使用merge。如果使用rebase,那么其他開發(fā)人員想看主分支的歷史,就不是原來的歷史了,歷史已經(jīng)被你篡改了。舉個(gè)例子解釋下,比如張三和李四從共同的節(jié)點(diǎn)拉出來開發(fā),張三先開發(fā)完提交了兩次然后merge上去了,李四后來開發(fā)完如果rebase上去(注意李四需要切換到自己本地的主分支,假設(shè)先pull了張三的最新改動(dòng)下來,然后執(zhí)行,然后再git push到遠(yuǎn)端),則李四的新提交變成了張三的新提交的新基底,本來李四的提交是最新的,結(jié)果最新的提交顯示反而是張三的,就亂套了。
正因如此,大部分公司其實(shí)會(huì)禁用rebase,不管是拉代碼還是push代碼統(tǒng)一都使用merge,雖然會(huì)多出無意義的一條提交記錄“Merge … to …”,但至少能清楚地知道主線上誰合了的代碼以及他們合代碼的時(shí)間先后順序
2.原理
變基操作的工作原理很簡(jiǎn)單:Git 會(huì)讓我們想要移動(dòng)的提交序列在目標(biāo)分支上按照相同的順序重新再現(xiàn)一遍。這就相當(dāng)于我們?yōu)楦鱾€(gè)原提交做了個(gè)副本,它們擁有相同的修改集、同一作者、日期以及注釋信息。
3.命令
# 可以是commit 版本號(hào)、分支名稱,合并多個(gè)提交到一個(gè)版本$ git rebase -i [startpoint] [endpoint] # 變基發(fā)生沖突時(shí),解決后繼續(xù)變基$ git rebase –continue# 無視沖突,繼續(xù)變基操作$ git rebase –skip # 發(fā)生沖突時(shí)中斷變基$ git rebase –abort”
-i的意思是–interactive,即彈出交互式的界面讓用戶編輯完成合并操作,[startpoint] [endpoint]則指定了一個(gè)編輯區(qū)間,如果不指定[endpoint],則該區(qū)間的終點(diǎn)默認(rèn)是當(dāng)前分支HEAD所指向的commit(注:該區(qū)間指定的是一個(gè)前開后閉的區(qū)間)。
Git 標(biāo)簽
Git 中的tag指向一次commit的id,通常用來給開發(fā)分支做一個(gè)標(biāo)記,如標(biāo)記一個(gè)版本號(hào)。
1.添加標(biāo)簽
git tag -a version -m “note”
注解:git tag 是打標(biāo)簽的命令,-a 是添加標(biāo)簽,其后要跟新標(biāo)簽號(hào),-m 及后面的字符串是對(duì)該標(biāo)簽的注釋。
2.提交標(biāo)簽到遠(yuǎn)程倉庫
git push origin -tags
注解:就像git push origin master 把本地修改提交到遠(yuǎn)程倉庫一樣,-tags可以把本地的打的標(biāo)簽全部提交到遠(yuǎn)程倉庫。
3.刪除標(biāo)簽
git tag -d version
注解:-d 表示刪除,后面跟要?jiǎng)h除的tag名字
4.刪除遠(yuǎn)程標(biāo)簽
git push origin :refs/tags/version
注解:就像git push origin :branch_1 可以刪除遠(yuǎn)程倉庫的分支branch_1一樣, 冒號(hào)前為空表示刪除遠(yuǎn)程倉庫的tag。
5.查看標(biāo)簽
git tag或者git tag -l
Git存儲(chǔ)
git stash將本地未提交代碼作為一個(gè)本地緩存。作用范圍為本地工作區(qū)以及本地暫存區(qū)。
1.使用場(chǎng)景
- 當(dāng)正在dev分支上開發(fā)某個(gè)項(xiàng)目,這時(shí)項(xiàng)目中出現(xiàn)一個(gè)bug,需要緊急修復(fù),但是正在開發(fā)的內(nèi)容只是完成一半,還不想提交,這時(shí)可以用git stash命令將修改的內(nèi)容保存至堆棧區(qū),然后順利切換到hotfix分支進(jìn)行bug修復(fù),修復(fù)完成后,再次切回到dev分支,從堆棧中恢復(fù)剛剛保存的內(nèi)容。
- 由于疏忽,本應(yīng)該在dev分支開發(fā)的內(nèi)容,卻在master上進(jìn)行了開發(fā),需要重新切回到dev分支上進(jìn)行開發(fā),可以用git stash將內(nèi)容保存至堆棧中,切回到dev分支后,再次恢復(fù)內(nèi)容即可。
2.git stash
# 保存當(dāng)前工作區(qū)、暫存區(qū)的所有未提交代碼# 執(zhí)行存儲(chǔ)時(shí),添加備注,方便查找# git stash
只有g(shù)it stash 也是可以的,但查找時(shí)不方便識(shí)別。
3.查看stash列表
# 查看stash了哪些存儲(chǔ)$ git stash list
4.恢復(fù)緩存
git stash pop
命令恢復(fù)之前緩存的工作目錄,將緩存堆棧中的對(duì)應(yīng)stash刪除,并將對(duì)應(yīng)修改應(yīng)用到當(dāng)前的工作目錄下,默認(rèn)為第一個(gè)stash,即stash@{0},如果要應(yīng)用并刪除其他stash,命令:
git stash pop stash@{$num}
比如應(yīng)用并刪除第二個(gè):
git stash pop stash@{1}
5.git apply
使用apply命令恢復(fù),stash列表中的信息是會(huì)繼續(xù)保留的,而使用pop命令進(jìn)行恢復(fù),會(huì)將stash列表中的信息進(jìn)行刪除。參數(shù)同pop
6.刪除緩存
git stash drop stash@{num}
刪除某個(gè)保存,num是可選項(xiàng),通過git stash list可查看具體值
7.刪除所有緩存
# 刪除所有緩存的stash $ git stash clear
Git常用命令,基于使用步驟
使用步驟
提示:正常步驟應(yīng)該是:先commit 然后pull 再 push ;
1.git config,配置Git
用于查看和修改Git配置信息,當(dāng)安裝Git后首先要做的事情是設(shè)置用戶名稱和email地址。這是非常重要的,因?yàn)槊看蜧it提交都會(huì)使用該用戶信息
#設(shè)置用戶信息git config –global user.name “未進(jìn)化的程序猿”git config –global user.email “486566947@qq.com”#讀取配置信息git config –listgit config user.name
- /etc/gitconfig 文件:系統(tǒng)中對(duì)所有用戶都普遍適用的配置。若使用 git config 時(shí)用 –system 選項(xiàng),讀寫的就是這個(gè)文件。
- ~/.gitconfig 文件:用戶目錄下的配置文件只適用于該用戶。若使用 git config 時(shí)用 –global 選項(xiàng),讀寫的就是這個(gè)文件。
- 當(dāng)前項(xiàng)目的 Git 目錄中的配置文件(也就是工作目錄中的 .git/config 文件):這里的配置僅僅針對(duì)當(dāng)前項(xiàng)目有效。每一個(gè)級(jí)別的配置都會(huì)覆蓋上層的相同配置,所以 .git/config 里的配置會(huì)覆蓋 /etc/gitconfig 中的同名變量。
提示
PHPstorm中使用Git時(shí)的賬號(hào)密碼同樣是Git配置中使用的賬號(hào)密碼
2.初始化倉庫,git init
Git 使用 git init 命令來初始化一個(gè) Git 倉庫,Git 的很多命令都需要在 Git 的倉庫中運(yùn)行,所以 git init 是使用 Git 的第一個(gè)命令。
在執(zhí)行完成 git init 命令后,Git 倉庫會(huì)生成一個(gè) .git 目錄,該目錄包含了資源的所有元數(shù)據(jù),其他的項(xiàng)目目錄保持不變。
使用當(dāng)前目錄作為 Git 倉庫,我們只需使它初始化。
git init
該命令執(zhí)行完后會(huì)在當(dāng)前目錄生成一個(gè) .git 目錄。
使用我們指定目錄作為Git倉庫。
git init newrepo
初始化后,會(huì)在 newrepo 目錄下會(huì)出現(xiàn)一個(gè)名為 .git 的目錄,所有 Git 需要的數(shù)據(jù)和資源都存放在這個(gè)目錄中。
3.將文件納入版本控制 ,git add
如果當(dāng)前目錄下有幾個(gè)文件想要納入版本控制,需要先用 git add 命令告訴 Git 開始對(duì)這些文件進(jìn)行跟蹤,然后提交:
# 當(dāng)前目錄下.c結(jié)尾的所有文件(通配符)$ git add *.c # 指定README等多個(gè)文件文件$ git add README README.md# .git add all無論在哪個(gè)目錄執(zhí)行都會(huì)提交相應(yīng)文件。$ git add –all # .git add .只能夠提交當(dāng)前目錄或者它后代目錄下相應(yīng)文件。$ git add .
4. 提交文件 ,git commit
注意
注: 在 Linux 系統(tǒng)中,commit 信息使用單引號(hào) ‘,Windows 系統(tǒng),commit 信息使用雙引號(hào) “。
所以在 git bash 中 git commit -m ‘提交說明’ 這樣是可以的,在 Windows 命令行中就要使用雙引號(hào) git commit -m “提交說明”。
暫存區(qū)保留本次變動(dòng)的文件信息,等到修改了差不多了,就要把這些信息寫入歷史,這就相當(dāng)于生成了當(dāng)前項(xiàng)目的一個(gè)快照(snapshot)。
項(xiàng)目的歷史就是由不同時(shí)點(diǎn)的快照構(gòu)成。Git 可以將項(xiàng)目恢復(fù)到任意一個(gè)快照??煺赵?Git 里面有一個(gè)專門名詞,叫做 commit,生成快照又稱為完成一次提交。
提示
每一次commit都會(huì)產(chǎn)生一個(gè)新的版本。產(chǎn)生一個(gè)代表版本號(hào)的散列值
git commit -m ‘初始化項(xiàng)目版本,記錄提交信息’git commit -am “新增提交說明”
設(shè)置了用戶名和 Email,保存快照的時(shí)候,會(huì)記錄是誰提交的。
Git文件生命周期
5. 克隆倉庫,git clone
我們使用 git clone 從現(xiàn)有 Git 倉庫中拷貝項(xiàng)目,Git倉庫一般是一個(gè)遠(yuǎn)程連接
git clone
- repo:Git 倉庫。
- directory:本地目錄。
6. 當(dāng)前提交狀態(tài),git status
git status 命令用于查看在你上次提交之后是否有對(duì)文件進(jìn)行再次修改。
$ git statusOn branch masterInitial commitChanges to be committed: (use “git rm –cached …” to unstage) new file: README new file: hello.php
通常我們使用 -s 參數(shù)來獲得簡(jiǎn)短的輸出結(jié)果:
$ git status -sAM READMEA hello.php
AM 狀態(tài)的意思是這個(gè)文件在我們將它添加到緩存之后又有改動(dòng)。
7. git diff,比較差異
git diff 命令比較文件的不同,即比較文件在暫存區(qū)和工作區(qū)的差異。(-c 上下文格式的diff、-u 合并格式diff)
git diff 命令顯示已寫入暫存區(qū)和已經(jīng)被修改但尚未寫入暫存區(qū)文件的區(qū)別。
git diff 有兩個(gè)主要的應(yīng)用場(chǎng)景。
- 尚未緩存的改動(dòng):git diff
- 查看已緩存的改動(dòng): git diff –cached
- 查看已緩存的與未緩存的所有改動(dòng):git diff HEAD
- 顯示摘要而非整個(gè) diff:git diff –stat
顯示暫存區(qū)和工作區(qū)的差異:
$ git diff [file]
顯示兩次提交之間的差異:
$ git diff –cached [file]或$ git diff –staged [file]
顯示暫存區(qū)和上一次提交(commit)的差異:
$ git diff [first-branch]…[second-branch]
分支比較
# 直接將兩個(gè)分支上最新的提交做diff$ git diff topic master$ #或 $ git diff topic..master# 輸出自topic和master分別開發(fā)以來,master分支上的變更。$ git diff topic…master# 查看簡(jiǎn)單的diff結(jié)果,可以加上–stat參數(shù)# –name-only輸出時(shí)只顯示文件名
8. 回退版本,git reset
git reset 命令用于回退版本,可以指定退回某一次提交的版本。只對(duì)本地分支有效,對(duì)遠(yuǎn)程分支無效
git reset [–soft | –mixed | –hard] [HEAD]
–mixed 為默認(rèn),可以不用帶該參數(shù),用于重置暫存區(qū)的文件與上一次的提交(commit)保持一致,工作區(qū)文件內(nèi)容保持不變。
git reset [HEAD]
git reset HEAD
執(zhí)行 git reset HEAD 以取消之前 git add 添加,但不希望包含在下一提交快照中的緩存。
實(shí)例:
$ git reset HEAD^ # 回退所有內(nèi)容到上一個(gè)版本$ git reset HEAD^ hello.php # 回退 hello.php 文件的版本到上一個(gè)版本$ git reset 052e # 回退到指定版本
–soft 參數(shù)用于回退到某個(gè)版本:
$ git reset –soft HEAD~3 # 回退上上上一個(gè)版本
–hard 參數(shù)撤銷工作區(qū)中所有未提交的修改內(nèi)容,將暫存區(qū)與工作區(qū)都回到上一次版本,并刪除之前的所有信息提交:
$ git reset –hard HEAD~3 # 回退上上上一個(gè)版本 $ git reset –hard bae128 # 回退到某個(gè)版本回退點(diǎn)之前的所有信息。 $ git reset –hard origin/master # 將本地的狀態(tài)回退到和遠(yuǎn)程的一樣
- HEAD 表示當(dāng)前版本
- HEAD^ 上一個(gè)版本
- HEAD^^ 上上一個(gè)版本
- HEAD^^^ 上上上一個(gè)版本HEAD~0 表示當(dāng)前版本
- HEAD~1 上一個(gè)版本
- HEAD^2 上上一個(gè)版本
- HEAD^3 上上上一個(gè)版本
9.刪除文件,git rm
將文件從暫存區(qū)和工作區(qū)中刪除:
git rm
如果想把文件從暫存區(qū)域移除,但仍然希望保留在當(dāng)前工作目錄中,換句話說,僅是從跟蹤清單中刪除,使用 –cached 選項(xiàng)即可:
git rm –cached
可以遞歸刪除,即如果后面跟的是一個(gè)目錄做為參數(shù),則會(huì)遞歸刪除整個(gè)目錄中的所有子目錄和文件:
git rm –r *
10. 移動(dòng)目錄、文件,git mv
git mv 命令用于移動(dòng)或重命名一個(gè)文件、目錄或軟連接。
git mv [file] [newfile]
如果新文件名已經(jīng)存在,但還是要重命名它,可以使用 -f 參數(shù):
git mv -f [file] [newfile]
11.查看提交歷史git log、git blame
# 所有歷史記錄$ git log # 簡(jiǎn)潔版$ git log –oneline# 拓?fù)鋱D版$ git log –graph# 反向顯示$ git log –reverse # 指定提交者$ git log –author=lius# 時(shí)間范圍$ git log –before={3.weeks.ago} –after={2010-04-18}# 反向顯示$ git log –reverse # 指定文件# git blame
以上多種參數(shù)可以組合使用
12. git show
# 查看分支最后一次提交或版本在代碼層面的改動(dòng)$ git show |
13. git reflog
reflog是reference log的縮寫,含義是引用日志,它會(huì)記錄下HEAD節(jié)點(diǎn)和分支引用所指向的歷史。可以使用git reflog命令來查看引用日志
14. git gc
gc 命令(gc 指的是垃圾回收)可用于清理版本庫,移除所有不屬于當(dāng)前分支的提交對(duì)象。
15. git ls-files
查看一下git跟蹤了哪些文件,此命令就可以列出所有g(shù)it正在跟蹤的文件
12. 撤銷文件 git restore
# 將在 工作區(qū) 但是 不在暫存區(qū) 的文件撤銷更改 $ git restore # 作用是將 暫存區(qū)的文件從暫存區(qū)撤出,但不會(huì)更改文件 $ git restore –staged
問題記錄
1.push和commit的區(qū)別
git commit操作的是本地庫,git push操作的是遠(yuǎn)程庫。
git commit是將本地修改過的文件提交到本地庫中。git push是將本地庫中的最新信息發(fā)送給遠(yuǎn)程庫。
如果本地不commit的話,修改的紀(jì)錄可能會(huì)丟失,而有些修改當(dāng)前是不需要同步至服務(wù)器的,所以什么時(shí)候同步過去由用戶自己選擇。什么時(shí)候需要同步再push到服務(wù)器
2. pull requests 和 merge requests
github可以對(duì)不同的用戶賦予不同的分支權(quán)限,例如Gitlab中的:
- Guest:可以創(chuàng)建issue、發(fā)表評(píng)論,不能讀寫版本庫
- Reporter:可以克隆代碼,不能提交,QA、PM可以賦予這個(gè)權(quán)限
- Developer:可以克隆代碼、開發(fā)、提交、push,RD可以賦予這個(gè)權(quán)限
- Master:可以創(chuàng)建項(xiàng)目、添加tag、保護(hù)分支、添加項(xiàng)目成員、編輯項(xiàng)目,核心RD負(fù)責(zé)人可以賦予這個(gè)權(quán)限
- Owner:可以設(shè)置項(xiàng)目訪問權(quán)限 – Visibility Level、刪除項(xiàng)目、遷移項(xiàng)目、管理組成員,開發(fā)組leader可以賦予這個(gè)權(quán)限
github基于fork的模式下,PR用于請(qǐng)求分支管理員 合并自己提交的代碼(理解為請(qǐng)求拉取自己的代碼),merge requests同理;
3. fetch和pull的區(qū)別
git在本地會(huì)保存兩個(gè)版本的倉庫,分為本地倉庫和遠(yuǎn)程倉庫。
fetch 只能更新遠(yuǎn)程倉庫的代碼為最新的,本地倉庫的代碼還未被更新,我們需要通過 git merge origin/master 來合并這兩個(gè)版本,你可以把它理解為合并分支一樣的。
pull 操作是將本地倉庫和遠(yuǎn)程倉庫(本地的)更新到遠(yuǎn)程的最新版本。fetch+merge,自動(dòng)進(jìn)行合并
4.checkout和reset時(shí)的變化
簽出切換分支、版本、標(biāo)簽時(shí)文件的變化
- 當(dāng)執(zhí)行 git reset HEAD 命令時(shí),暫存區(qū)的目錄樹會(huì)被重寫,被 master 分支指向的目錄樹所替換,但是工作區(qū)不受影響。加上–hard時(shí)會(huì)強(qiáng)制替換工作區(qū)、暫存區(qū)的內(nèi)容;
- git restore會(huì)清除暫存區(qū)的修改內(nèi)容,例如修改了test.vue ,會(huì)變?yōu)槲葱薷臅r(shí)的內(nèi)容;
- 當(dāng)執(zhí)行 git rm –cached 命令時(shí),會(huì)直接從暫存區(qū)刪除文件,工作區(qū)則不做出改變。
5. 保存遠(yuǎn)程時(shí)輸入的賬號(hào)密碼
在 git bash 里輸入命令:
git config –global credential.helper store
然后執(zhí)行 git 操作,輸入一遍密碼后就會(huì)記錄密碼,以后就不用輸入了。
要更改記錄的用戶名和密碼,只需要更改用戶目錄下的 .git-credentials 文件即可。
6.移除文件的版本控制
7.Git問題解決記錄
8.Github常規(guī)目錄說明:
設(shè)置某個(gè)分支的權(quán)限(保護(hù)分支)
1. 管理員身份登錄GitHub,找到項(xiàng)目2. Settings–>Branches–>Protected branches—>Choose a branch… ,選擇需要保護(hù)的分支,然后點(diǎn)擊edit按鈕,3. Branch protection for 所選的分支名 –> 勾選Restrict who can push to this branch People and teams with push access若不選擇任何人,則任何人都沒有push代碼到該分支的權(quán)限。
9. git做不到一個(gè)文件一個(gè)分支有一個(gè)分支沒有
例如一個(gè)數(shù)據(jù)庫配置文件,本地和線索不一樣,把它從暫存區(qū)拉出來,取消追蹤變成deleted狀態(tài),本地文件實(shí)際還存在,同步到分支,遠(yuǎn)程分支當(dāng)前版本已經(jīng)沒有這個(gè)文件了。
10. Git拉取指定分支
git clone 倉庫地址 //默認(rèn)master分支git clone -b 分支名 倉庫地址 //指定分支
11. git 常見的輸出內(nèi)容
# 已經(jīng)提交的改變$ Changes to be committed: # 暫未提交的改變$ Changes not staged for commit:# 常見狀態(tài)deleted、modified
12.報(bào)錯(cuò)non-fast-forward
non-fast-forward
Dealing with “non-fast-forward” errors:(From time to time you may encounter this error while pushing)To prevent you from losing history, non-fast-forward updates were rejected. Merge the remote changes before pushing again. See the ‘non-fast forward’ section of ‘git push –help’ for details.This error can be a bit overwhelming at first, do not fear. Simply put, git cannot make the change on the remote without losing commits, so it refuses the push. Usually this is caused by another user pushing to the same branch. You can remedy this by fetching and merging the remote branch, or using pull to perform both at once.In other cases this error is a result of destructive changes made locally by using commands like git commit –amend or git rebase. While you can override the remote by adding –force to the push command, you should only do so if you are absolutely certain this is what you want to do. Force-pushes can cause issues for other users that have fetched the remote branch, and is considered bad practice. When in doubt, don’t force-push.
以上時(shí)較為官方的解釋,簡(jiǎn)單說就是push之前需要先同步遠(yuǎn)程版本。pull會(huì)自動(dòng)合并,所以要改為fetch手動(dòng)合并;
問題分析
可以這樣理解這個(gè)問題就是:別人上傳到遠(yuǎn)程倉庫后,你沒有及時(shí)的同步(、拉?。┑奖镜?,但是你同時(shí)又添加了一些內(nèi)容(提交),以致于你在提交時(shí),它會(huì)檢測(cè)到你之前從遠(yuǎn)程倉庫拉取的時(shí)候的倉庫狀態(tài)和現(xiàn)在的不一樣。于是,它為了安全起見拒絕了你的提交(然后就報(bào)了這個(gè)錯(cuò)誤)。
“不能快速前進(jìn)”的原因是因?yàn)槁凡灰粯恿?,變得不好走了;體現(xiàn)在git里面就是提交歷史出現(xiàn)分叉,主線不再是一條直線,而是在前端出現(xiàn)了分叉,git不知道該如何前進(jìn),所以報(bào)錯(cuò)了,讓你來覺得走哪條路!說的簡(jiǎn)單點(diǎn),就是遠(yuǎn)程倉庫和本地倉庫不同步了
解決問題
1.先合并之前的歷史,再進(jìn)行提交——提倡使用
先把git的東西fetch到你本地然后merge后再push,(如果有沖突就要解決沖突后再合并,沖突問題比較復(fù)雜,這里就不詳細(xì)說了),這樣就可以使遠(yuǎn)程倉庫和你本地倉庫一致了,然后就可以提交修改了。
$ git fetch origin master$ git merge origin FETCH_HEAD# 提示:這2句命令等價(jià)于,但是使用git fetch + git merge 更加安全。$ git pull origin master# 然后執(zhí)行,重新定基,可以使得歷史更加統(tǒng)一,即提交歷史趨向于一條直線。 $ git pull –rebase origin master
2.丟棄之前的歷史,強(qiáng)推(謹(jǐn)慎使用)
利用強(qiáng)覆蓋方式用你本地的代碼替代git倉庫內(nèi)的內(nèi)容,遠(yuǎn)程倉庫的東西會(huì)被本地倉庫覆蓋?。。?/p>
$ git push -f 或者 $ git push –force
官方文檔提示:This flag disables these checks, and can cause the remote repository to lose commits; use it with care.(即:此標(biāo)志禁用這些檢查,并可能導(dǎo)致遠(yuǎn)程存儲(chǔ)庫丟失提交;小心使用。)
不僅在此處,在平時(shí)使用時(shí),也要非常注意,除非你真的是想覆蓋遠(yuǎn)程倉庫,不然最好不要強(qiáng)制執(zhí)行。
13.徹底清理歷史版本
先創(chuàng)建一個(gè)分支,添加所有文件,刪除其他所有分支。
14.回退遠(yuǎn)程倉庫的版本
先把遠(yuǎn)程倉庫指定分支拉下來,手動(dòng)回退,然后再強(qiáng)制推送上去(拉回來的遠(yuǎn)程版本庫同時(shí)帶了這個(gè)分支的所有歷史版本);
Gitee和Github
1.查看fork的子倉
- github點(diǎn)擊 Insights -> fork
- gitee點(diǎn)擊fork邊上的數(shù)字
2.保護(hù)分支
- github默認(rèn)只有分支創(chuàng)建者和倉庫管理員有push的權(quán)限,其他人可以提交PR
- gitee需要自己設(shè)置分支為保護(hù)分支,并在保護(hù)分支的設(shè)置內(nèi)設(shè)置相應(yīng)的保護(hù)級(jí)別和權(quán)限。
- github可以邀請(qǐng)別人成為協(xié)作者,個(gè)人倉庫的協(xié)作者可以拉?。ㄗx?。﹤}庫的內(nèi)容并向倉庫推送(寫入)更改。