Git
Git 起源
Git 源自 Linux 內(nèi)核開源項(xiàng)目拓售。
Linux 內(nèi)核開源項(xiàng)目組在 2002 年開始使用 BitKeeper 來(lái)管理和維護(hù)代碼异雁,到了 2005 年华临,開發(fā) BitKeeper 的商業(yè)公司和 Linux 內(nèi)核開源社區(qū)的合作關(guān)系結(jié)束窘行,Linux 內(nèi)核開源社區(qū)就不能免費(fèi)使用 BitKeeper饥追,這個(gè)就迫使項(xiàng)目組自己開發(fā)一個(gè)項(xiàng)目管理系統(tǒng)也就是 Git。項(xiàng)目組在開發(fā) Git 時(shí)罐盔,吸取了 BitKeeper 的經(jīng)驗(yàn)教訓(xùn)但绕,做出的非常大的改進(jìn),對(duì)這個(gè)新系統(tǒng)制定了若干目標(biāo):
- 速度
- 簡(jiǎn)單的設(shè)計(jì)
- 對(duì)非線性開發(fā)模式的強(qiáng)力支持(允許成千上萬(wàn)個(gè)并行開發(fā)分支)
- 完全分布式
- 有能力高效管理類似 Linux 內(nèi)核一樣的超大規(guī)模項(xiàng)目
Git 與其他控制系統(tǒng)的區(qū)別和特點(diǎn)
主要區(qū)別
Git 和其他的版本控制系統(tǒng)的主要區(qū)別在于 Git 對(duì)待數(shù)據(jù)的方法。
其他大部分的系統(tǒng)(例如:svn)以文件變更列表的方式存儲(chǔ)信息捏顺,將保存的信息看作是一組基本文件和每個(gè)文件隨時(shí)間逐步累積的差異六孵。
Git 像是把數(shù)據(jù)看作是對(duì)小型文件系統(tǒng)的一組快照欠母。每次提交更新疫鹊,或在 Git 中保存項(xiàng)目狀態(tài)時(shí)底扳,它主要對(duì)當(dāng)時(shí)的全部文件制作一個(gè)快照并保存這個(gè)快照的索引拿撩。為了高效肌访,如果文件沒有修改妙同,Git 不再重新存儲(chǔ)該文件坝咐,而是只保留一個(gè)鏈接指向之前的文件唧垦。Git 對(duì)待數(shù)據(jù)更像是一個(gè)快照流懂拾。
Git 的特點(diǎn)
- 絕大多數(shù)的操作都是本地執(zhí)行
我們?cè)诳寺№?xiàng)目時(shí)煤禽,會(huì)將遠(yuǎn)程倉(cāng)庫(kù)中幾乎所有的數(shù)據(jù)都克隆下來(lái),而不是一個(gè)版本一個(gè)版本的克隆出來(lái)岖赋,這樣就能在本地查詢對(duì)比檬果,只有在上傳、克隆等少數(shù)操作時(shí)唐断,才需要聯(lián)網(wǎng) - 保證完整性
Git 所有數(shù)據(jù)在存儲(chǔ)前都計(jì)算校驗(yàn)和选脊,然后以檢驗(yàn)和來(lái)引用。校驗(yàn)和是基于 Git 中的文件的內(nèi)容或目錄結(jié)構(gòu)計(jì)算出來(lái)的 40 個(gè)十六進(jìn)制字符脸甘。所以不可能在 Git 不知情更改任何內(nèi)容恳啥。 - 一般只添加數(shù)據(jù)
在執(zhí)行 Git 操作時(shí),幾乎只能往 Git 數(shù)據(jù)庫(kù)中增加數(shù)據(jù)丹诀,很難讓 Git 執(zhí)行任何不可逆操作钝的。
Git 文件狀態(tài)
一:已跟蹤(已納入版本控制)
- 已提交(數(shù)據(jù)已經(jīng)提交到本地?cái)?shù)據(jù)庫(kù)中)
- 已修改(文件修改了,但是還沒有提交到數(shù)據(jù)庫(kù)中)
- 已暫存(已暫存表示對(duì)一個(gè)已修改文件的當(dāng)前版本做了標(biāo)記铆遭,使之包含在下次提交的快照中)
二:未跟蹤(未納入版本控制)
由此也引出了 Git 項(xiàng)目的三個(gè)工作區(qū)域的概念:Git 倉(cāng)庫(kù)硝桩、工作目錄以及暫存區(qū)域。
Git 倉(cāng)庫(kù)目錄是 Git 用來(lái)保存項(xiàng)目的源數(shù)據(jù)和對(duì)象數(shù)據(jù)庫(kù)的地方枚荣。這是 Git 中最重要的部分碗脊,從其它計(jì)算機(jī)克隆倉(cāng)庫(kù)時(shí),拷貝的就是這里的數(shù)據(jù)橄妆。
工作目錄是對(duì)項(xiàng)目的某個(gè)版本獨(dú)立拉取出來(lái)的內(nèi)容衙伶。這些從 Git 倉(cāng)庫(kù)的壓縮數(shù)據(jù)庫(kù)中提取出來(lái)的文件,放在磁盤上供使用和修改呼畸。
暫存區(qū)域是一個(gè)文件痕支,保存了下次將要提交的文件列表信息,一般在 Git 倉(cāng)庫(kù)目錄中蛮原。有時(shí)候也稱作“索引”。
基本的 Git 工作流程如下:
- 在工作目錄中修改文件
- 暫存文件另绩,將文件的快照放入暫存區(qū)域
- 提交更新儒陨,找到暫存區(qū)域的文件花嘶,將快照永久性存儲(chǔ)到 Git 倉(cāng)庫(kù)目錄。
初次 Git 配置
首次運(yùn)行 git 時(shí)需要設(shè)置用戶信息(如果沒有設(shè)置在提交時(shí)會(huì)提醒設(shè)置)蹦漠,
$ git config --global user.name "NAME"
$ git config --global user.email "EMAIL"
# 配置默認(rèn)編輯器
$ git config --global core.editor xxx
# 查看配置列表
$ git config --list
--global 命令是針對(duì)全局的椭员。如果你想針對(duì)特定項(xiàng)目使用不同的用戶名和郵箱時(shí),那就可以在項(xiàng)目的根目錄下運(yùn)行沒有--global 的命令
忽略文件
有一些文件我們不需要納入版本控制笛园,也不希望它們出現(xiàn)在未跟蹤文件列表中隘击。比如: /node_modules 整個(gè)文件夾,以及 package-lock.json 這種自動(dòng)生成的文件研铆,還有一些打包時(shí)的錯(cuò)誤日志文件埋同。在這種情況下我們可以創(chuàng)建一個(gè)名為.gitignore 的文件,列出要忽略的文件模式棵红。
*.[oa]
*~
第一行告訴 Git 忽略所有的以 .o 或 .a 為結(jié)尾的文件
第二行告訴 Git 忽略所有以~結(jié)尾的文件
.gitignore 格式規(guī)范:
- 所有的空行或者以#開頭的行都會(huì)被 git 忽略
- 可以使用標(biāo)準(zhǔn)的 glob 模式[1]匹配
- 匹配模式可以以(/)開頭防止遞歸
- 匹配模式可以以(/)結(jié)尾指定目錄
- 要忽略指定模式以外的文件或目錄凶赁,可以在模式前加上感嘆號(hào)(!)取反。
再看一個(gè)例子:
# 排除 .a 文件
*.a
# 對(duì)lib.a 文件進(jìn)行跟蹤逆甜,即使上面已經(jīng)排除了 .a 文件
!lib.a
# 只排除當(dāng)前目錄下的TODO文件虱肄,但是不排除其他文件夾下的TODO文件
/TODO
# 排除build文件夾下的所有文件
build/
# 只排除doc根目錄下的 .txt文件,非根目錄下的 .txt文件不排除
doc/*.txt
# 排除 doc 目錄下 所有的 .pdf文件
doc/**/*.pdf
Git 常用命令
# 克隆一個(gè)遠(yuǎn)程倉(cāng)庫(kù)
$ git clone URL
# 克隆一個(gè)遠(yuǎn)程倉(cāng)庫(kù) 并自定義本地倉(cāng)庫(kù)名
$ git clone URL NEW_NAME
# 檢查當(dāng)前文件狀態(tài)
$ git status
# 緊湊展示當(dāng)前文件狀態(tài)
# 新添加未跟蹤 ??
# 新添加到暫存區(qū) A
# M修改了交煞,但是還未放入暫存區(qū)
# M 修改了并已放入暫存區(qū)
$ git status -s || --short
# 開始跟蹤某個(gè)文件/暫存某個(gè)文件
# 如果xxx是一個(gè)路徑咏窿,將遞歸跟蹤該路徑下所有文件
$ git add xxx
# 暫存所有文件
$ git add .
# 本地提交
$ git commit
# -m 是直接寫提交信息, 如果
$ git commit -m "提交信息"
# 推送到遠(yuǎn)程服務(wù)器
$ git push
git diff 對(duì)比
使用 git status 命令我們能夠知道各個(gè)文件的狀態(tài)素征,但是不能夠知道每個(gè)文件具體修改了哪些地方集嵌?這個(gè)時(shí)候就有了 git diff 這條命令。
git diff 命令只能查看尚未暫存的文件修改了哪些部分稚茅,它比較的是文件目錄當(dāng)前文件與暫存區(qū)域快照的差異纸淮。如果想要查看已暫存的將要添加到下次提交里的內(nèi)容,可以用 git diff --cached 或者 git diff --staged
git commit 提交
提交命令亚享,提交暫存區(qū)域的數(shù)據(jù)咽块。在執(zhí)行該命令之前一定要確定所有的文件都已經(jīng) git add 過了,不然提交時(shí)欺税,是不會(huì)記錄這些沒有暫存的文件侈沪。這些沒有暫存的文件只會(huì)保留在本地磁盤。執(zhí)行該命令后晚凿,Git 會(huì)啟動(dòng)文本編輯器亭罪,一遍輸入本次提交說明(如果沒有配置編輯器就會(huì)默認(rèn)打開 git 的 nano 編輯器)。
如果不想打開編輯器填寫提交說明也可以執(zhí)行 git commit -m "提交信息" 提交歼秽。
還有一種提交方法是 git commit -a -m "提交信息" 這種提交方式可以直接跳過暫存步驟应役,將所有已跟蹤的文件暫存起來(lái),然后一并提交。
我們提交的文件的時(shí)候箩祥,有可能忘了提交某些文件院崇、或者提交信息寫錯(cuò)了
$ git commit --amend
git rm 刪除
如果我們想從項(xiàng)目中刪除某個(gè)文件,就必須要從已跟蹤的文件清單(暫存區(qū))中移除袍祖,然后再提交底瓣,這樣才能徹底從項(xiàng)目中刪除該文件。此時(shí)我們就要使用 git rm 命令蕉陋。
$ git rm yy.md
rm 'yy.md'
下一次提交的時(shí)候捐凭,該文件就不會(huì)被提交。如果凳鬓,在刪除之前文件已經(jīng)放到了暫存區(qū)域的話茁肠,就需要使用強(qiáng)制刪除 -f 。
還有一種情況是村视,忘記在 .gitignore 文件中添加忽略文件官套,不小心把某個(gè)日志文件或編譯后的文件提交到暫存區(qū)時(shí),就需要用到 --cached :
$ git rm --cached node_modules
git mv 重命名
# 將 a 改名為 b
$ git mv a b
# 等同于
$ mv a.js b.js
$ git rm a.js
$ git add b.js
查看歷史
查看所有的歷史紀(jì)錄可以直接使用 git log 蚁孔,不帶任何參數(shù)時(shí)奶赔,會(huì)按照提交時(shí)間列出所有的更新,包括每一個(gè)提交的 SHA-1 校驗(yàn)和杠氢、作者站刑、郵箱、時(shí)間鼻百、提交信息绞旅。
- -p 用來(lái)顯示每次提交的內(nèi)容差異, 也可以加上 -2 來(lái)僅顯示最近兩次的提交
$ git log -p -2
- --stat 查看提交的簡(jiǎn)略統(tǒng)計(jì)信息
$ git log --stat
- --pretty 可以指定不同于默認(rèn)的格式展示提交歷史
$ git log --pretty=oneline
內(nèi)建的子選項(xiàng)
子選項(xiàng) | 功能 |
---|---|
oneline | 將每個(gè)提交放在一行顯示 |
short | 和默認(rèn) git log 相似温艇,但不顯示時(shí)間 |
full | 比默認(rèn)輸出少了時(shí)間因悲,多了一個(gè) Commit |
fuller | 比 full 多了 AuthorDate 和 CommitDate |
--name-only | 僅在提交信息后顯示已修改文件清單 |
--name-status | 顯示新增、修改勺爱、刪除文件清單 |
--abbrev-commit | 僅顯示 SHA-1 的前幾個(gè)字符 |
--relative-date | 使用較短的相對(duì)時(shí)間顯示 晃琳,多久以前的方式 |
--graph | 使用 ASCII 圖形表示分支合并歷史 |
--pretty | 定制要顯示的格式 |
-(n) | 僅顯示最近 n 條提交 |
--since,--after | 僅顯示指定時(shí)間之后的提交 |
--until,--before | 僅顯示指定時(shí)間之前的提交 |
--author | 僅顯示指定作者提交的提交 |
--committer | 僅顯示指定提交者相關(guān)的提交 |
--no-merges | 僅顯示未合并的提交 |
--grep | 僅顯示指定關(guān)鍵字的提交 |
-S | 僅顯示添加或移除了某個(gè)關(guān)鍵字的提交 |
一個(gè)完整的例子:
查找 2019 年 8 月份 blithe6317 提交的關(guān)于 函數(shù) test 的提交內(nèi)容
$ git log --since="2019-8-01" --before="2019-9-01" --author="blithe6317" -S"function test"
fomat
$ git log --pretty=format:"%h - %an, %ar : %s "
format 常用選項(xiàng)說明
選項(xiàng) | 說明 |
---|---|
%H | 提交對(duì)象的完整哈希字串 |
%h | 提交對(duì)象的簡(jiǎn)短哈希字串 |
%T | 樹對(duì)象的完整哈希字串 |
%t | 樹對(duì)象的簡(jiǎn)短哈希字串 |
%P | 父對(duì)象的完整哈希字串 |
%p | 父對(duì)象的簡(jiǎn)短哈希字串 |
%an | 作者名字 |
%ae | 作者郵箱 |
%ad | 作者修改日期 |
%ar | 作者修改日期,按多久以前顯示 |
%cn | 提交者 |
%ce | 提交者郵箱 |
%cd | 提交日期 |
%cr | 提交日期琐鲁,按多久以前顯示 |
%s | 提交說明 |
作者和提交者的區(qū)別是:作者是實(shí)際作處修改的人卫旱,提交者是最后將此工作成果提交到倉(cāng)庫(kù)的人
撤銷操作
在任何一個(gè)階段我們都有可能想要做某些撤銷操作,這里介紹幾個(gè)常見的撤銷操作围段。
取消暫存的文件
我們?cè)诓僮髦杏袝r(shí)候修改了兩個(gè)文件顾翼,但是我們只想暫存其中一個(gè)文件,在執(zhí)行時(shí)奈泪,卻不小心輸入了 git add . 适贸。此時(shí)如何取消其中一個(gè)暫存呢灸芳?
輸入 git status 時(shí),也有提示:use "git reset HEAD <file>..." to unstage
$ git reset HEAD a.js
這個(gè)命令只是取消了文件的暫存取逾,并不會(huì)修改文件耗绿,如何撤銷對(duì)文件的修改呢苹支?
撤銷對(duì)文件的修改
執(zhí)行了上面的命令之后砾隅,現(xiàn)在 a.js 文件是處于未暫存狀態(tài),執(zhí)行 git status ,會(huì)有兩個(gè)提示:
- use "git add <file> ..." to update what will be committed // 更新將要提交的內(nèi)容
- use "git checkout -- <file>..." to discard changes in working directory // 放棄工作目錄中的修改
git checkout -- <file>...
該命令是一個(gè)比較危險(xiǎn)的命令债蜜,它會(huì)拋棄你上一次暫存時(shí)到現(xiàn)在的所有更改晴埂。除非你是真的不想要這些修改了。對(duì)于 Git 而言大多數(shù)的內(nèi)容都是可以恢復(fù)的寻定,但是儒洛,沒有提交的內(nèi)容被刪除了很有可能再也找不到了。
遠(yuǎn)程倉(cāng)庫(kù)的使用
# 添加遠(yuǎn)程倉(cāng)庫(kù) shortname 倉(cāng)庫(kù)名稱簡(jiǎn)寫 url 地址
# 當(dāng)你本地寫了一個(gè)項(xiàng)目狼速,想把它提交到遠(yuǎn)程倉(cāng)庫(kù)琅锻, 先在遠(yuǎn)程倉(cāng)庫(kù)中新建一個(gè)項(xiàng)目,用新建項(xiàng)目的git url來(lái)執(zhí)行當(dāng)前命令
$ git remote add <shortname> <url>
# 拉取本地沒有的數(shù)據(jù)
# git fetch 不會(huì)自動(dòng)合并代碼向胡,需要自己手動(dòng)合并 git pull 拉取數(shù)據(jù)后會(huì)嘗試合并恼蓬,無(wú)法自動(dòng)合并時(shí)才會(huì)提示需要手動(dòng)合并
$ git fetch [remote-name]
$ git pull
# 推送數(shù)據(jù)
$ git push [remote-name] [branch-name]
# 查看遠(yuǎn)程倉(cāng)庫(kù)
$ git remote show [remote-name]
# 遠(yuǎn)程倉(cāng)庫(kù)重命名
$ git remote rename <curr-name> <new-name>
# 遠(yuǎn)程倉(cāng)庫(kù)移除
$ git remote rm <remove-name>
-
所謂的 glob 模式是指 shell 所使用的簡(jiǎn)化了的正則表達(dá)式。 星號(hào)(*)匹配零個(gè)或多個(gè)任意字符僵芹;[abc] 匹配任何一個(gè)列在方括號(hào)中的字符(這個(gè)例子要么匹配一個(gè) a处硬,要么匹配一個(gè) b,要么匹配一個(gè) c)拇派;問號(hào)(?)只匹配一個(gè)任意字符荷辕;如果在方括號(hào)中使用短劃線分隔兩個(gè)字符,表示所有在這兩個(gè)字符范圍內(nèi)的都可以匹配(比如 [0-9] 表示匹配所有 0 到 9 的數(shù)字)件豌。 使用兩個(gè)星號(hào)(*) 表示匹配任意中間目錄疮方,比如 a/**/z 可以匹配 a/z , a/b/z 或 a/b/c/z 等。 ?