本人陸陸續續學了很多次git,但是多次又忘了。到最後還是隻會在idea上,上傳和推送自己的修改。最近又看了一個講git講的還不錯的,至少自己隔了好多天還沒忘,所以專門記錄一下。
前傳
先說點廢話(只是想學命令的,建議從中傳開始看起)
Linus在1991年創建了開源的Linux,從此,Linux系統不斷髮展,已經成爲最大的服務器系統軟件了。
Linus雖然創建了Linux,但Linux的壯大是靠全世界熱心的志願者參與的,這麼多人在世界各地爲Linux編寫代碼,那Linux的代碼是如何管理的呢?
在2002年以前,世界各地的志願者把源代碼文件通過diff的方式發給Linus,然後由Linus本人通過手工方式合併代碼!
你也許會想,爲什麼Linus不把Linux代碼放到版本控制系統裏呢?不是有CVS、SVN這些免費的版本控制系統嗎?因爲Linus堅定地反對CVS和SVN,這些集中式的版本控制系統不但速度慢,而且必須聯網才能使用。有一些商用的版本控制系統,雖然比CVS、SVN好用,但那是付費的,和Linux的開源精神不符。
不過,到了2002年,Linux系統已經發展了十年了,代碼庫之大讓Linus很難繼續通過手工方式管理了,社區的弟兄們也對這種方式表達了強烈不滿,於是Linus選擇了一個商業的版本控制系統BitKeeper,BitKeeper的東家BitMover公司出於人道主義精神,授權Linux社區免費使用這個版本控制系統。
安定團結的大好局面在2005年就被打破了,原因是Linux社區牛人聚集,不免沾染了一些梁山好漢的江湖習氣。開發Samba的Andrew試圖破解BitKeeper的協議(這麼幹的其實也不只他一個),被BitMover公司發現了(監控工作做得不錯!),於是BitMover公司怒了,要收回Linux社區的免費使用權。
Linus可以向BitMover公司道個歉,保證以後嚴格管教弟兄們,嗯,這是不可能的。實際情況是這樣的:
Linus花了兩週時間自己用C寫了一個分佈式版本控制系統,這就是!一個月之內,Linux系統的源碼已經由Git管理了!牛是怎麼定義的呢?大家可以體會一下。
Git迅速成爲最流行的分佈式版本控制系統,尤其是2008年,GitHub網站上線了,它爲開源項目免費提供Git存儲,無數開源項目開始遷移至GitHub,包括jQuery,PHP,Ruby等等。
歷史就是這麼偶然,如果不是當年BitMover公司威脅Linux社區,可能現在我們就沒有免費而超級好用的Git了。
原理1. git原理
首先,git基於版本快照工作。
Git 更像是把數據看作是對小型文件系統的一組快照。 每次你提交更新,或在 Git 中保存項目狀態時,它主要對當時的全部文件製作一個快照並保存這個快照的索引。 爲了高效,如果文件沒有修改,Git 不再重新存儲該文件,而是隻保留一個鏈接指向之前存儲的文件。 Git 對待數據更像是一個快照流。
原理2. git保證數據完整性
Git 中所有數據在存儲前都計算校驗和,然後以校驗和來引用。這個功能建構在 Git 底層,是構成 Git 哲學不可或缺的部分。 若你在傳送過程中丟失信息或損壞文件,Git 就能發現。
Git 用以計算校驗和的機制叫做 SHA-1 散列(hash,哈希)。 這是一個由 40 個十六進制字符(0-9 和 a-f)組成字符串,基於 Git 中文件的內容或目錄結構計算出來。 SHA-1 哈希看起來是這樣:
24b9da6552252987aa493b52f8696cd6d3b00373
Git 中使用這種哈希值的情況很多,你將經常看到這種哈希值。 實際上,Git 數據庫中保存的信息都是以文件內容的哈希值來索引,而不是文件名。
原理3. 版本丟失
你執行的 Git 操作,幾乎只往 Git 數據庫中增加數據。 很難讓 Git 執行任何不可逆操作,或者讓它以任何方式清除數據。 同別的 版本控制工具一樣,未提交更新時有可能丟失或弄亂修改的內容;但是一旦你提交快照到 Git 中,就難以再丟失數據,特別是如果你定期的推送數據庫到其它倉庫的話。
這使得我們使用 Git 成爲一個安心愉悅的過程,因爲我們深知可以盡情做各種嘗試,而沒有把事情弄糟的危險。
中傳 – 很重要的概念: 版本庫(倉庫),工作區,暫存區,分支區
1.版本庫(倉庫)
版本庫又名倉庫,英文名repository,你可以簡單理解成一個目錄,這個目錄裏面的所有文件都可以被Git管理起來,每個文件的修改、刪除,Git都能跟蹤,以便任何時刻都可以追蹤歷史,或者在將來某個時刻可以“還原”。
2.工作區
存放要管理的文件的位置
3.暫存區
版本庫中 包含暫存區 git add命令 或 git rm 命令加入的操作 被記錄在暫存區內
4.分支區
版本庫中 包含分支區 是最終版本信息保存的位置 git commit命令將暫存區內記錄的操作 提交到分支中
可以配置多個分支,如果不指定則默認爲master分支,並有一個head指針指向master分支的最新位置
1. 創建版本庫
首先,如果你想讓你的文件被git管理,你要把你的文件夾聲明成一個git文件夾。使用的命令是:
git init
命令執行後,該目錄下會多出一個.git的目錄,這個目錄是Git用來跟蹤管理版本庫的,請勿手動修改。
git是一個很多人都可以用的版本管理工具,那麼如何讓其他人知道你是你呢?我們可以做一些配置
首先設置user.name , 使用 git config --global suer.name “你想要設置的名字”即可
git config --global user.name "zhangsan"
名字有時候會重複,所以還得設置郵箱。使用git config --global user.email 郵箱 即可。
git config --global user.email zhangsan@qq.com
因爲本次不是對git相關配置做討論,所以僅僅簡單介紹一下。
查看自己的配置信息使用 git config --list
git config --list
2. 工作區和倉庫
可以將git管理的文件夾分爲三個區域:工作區、暫存區、分支區。
工作區可以理解爲修改文件的區域。
分支區可以理解爲:提交了的文件(commit成功的文件)的區域。
暫存區介於二者之間,可以理解成二者的過渡區。既能提交到分支區,也可以撤銷到工作區。
3. 新增文件以及add和commit
首先,我們隨意寫一個文件,就叫text01.txt。我們可以使用一個命令來查看一下git管理的文件夾下的變化。
git status
這是對輸出的內容進行的一個解釋。
因爲我們新增了text01.txt文件,所以git提示我們有一個文件沒有追蹤(即這個文件無法被提交到分支區)。我們可以使用git add 文件名 命令追蹤文件,追蹤的同時也會將文件提交到暫存區。
git add text01.txt
同時,我們再次查看一下git的狀態
git status
下面的圖片上的第二行文字寫錯了,應該是 沒有未提交的內容
我們已經新增完了文件,同時也提交到了暫存區,接下來只要把他提交到分支區即可。
使用 git commit -m 相關信息 即可。 相關信息爲這次提交的備註。
-m後面輸入的是本次提交的說明,可以輸入任意內容
本例中只經歷了一個修改就提交了,其實完全可以 多個修改後一次提交
git commit -m "add file test.txt"
再次查看版本庫狀態
git status
畫圖的話,現在版本庫變成了如下狀態:
4. 修改並比較文件
首先對test01.txt進行修改。修改完畢後查看版本庫狀態
git status
畫圖的版本庫狀態如下:
此時,我們可以查看文件的修改信息。使用git diff 文件名稱 命令。
git diff test.txt
我的電腦上,顯示如下
將文件添加到暫存區。
git add test01.txt
查看狀態
git status
圖片的版本庫狀態如下:
此時,提交修改到分支。
git commit -m "change Git to git"
再次查看狀態
git status
圖片的版本庫狀態如下
5. 查看歷史版本
再次修改test01.txt。修改完後增加文件到暫存區。
git add text01.txt
然後再提交文件到分支。
git commit -m "...some msg..."
此時已提交了多次文件到分支區,可以使用 git log 命令查看歷史版本
git log
6. 回滾版本
有一天,正在修改的時候,突然發現自己修改錯了,需要回到以前某次提交的時候,這個操作叫回滾。可以使用如下命令:
git reset --hard HEAD^
在Git中,用HEAD表示當前版本,上一個版本就是HEAD^, 上上一個版本就是HEAD^^,當然往上100個版本寫100個^比較容易數不過來,所以寫成HEAD~100。
如果懶得數往上第幾個版本,也可以回滾到指定的版本號。
git reset --hard 5527510751b4303390bb4f321bfa8b7f997cbfd0
懶得敲也可以敲前幾個就行。
git reset --hard 55275
回滾到以前版本,再次使用git log查看歷史版本。
git log
使用該命令會發現,只會顯示當前版本及當前版本以前的版本;當前版本之後的版本查看不到。
那麼是不是說我們只能往前回滾不能往後回滾呢?
當然不是的。
使用git reflog命令可以查看所有的版本。
git reflog
配合git reset 命令可以方便的回滾到任意版本。
7. 撤銷修改
回到原來。我們再次修改文件,但是發現修改錯了,想撤銷掉這次修改。可以使用 git checkout – 文件名。該命令會將工作區中的文件恢復到最近一次add 或 commit之前的狀態。
git checkout -- test.txt
我們繼續修改文件,並將文件add到暫存區(還沒有提交到分支區)。突然,我們發現又寫錯了…單獨使用git checkout命令就不好使了。這時候,我們可以配合其他命令使用。
首先,我們可以回滾到當前版本(回滾到其他版本時,暫存區中的文件將會消失,這樣就清除了暫存區中的文件)
git reset HEAD test.txt
然後再使用git checkout命令進行撤銷。
git checkout -- test.txt
這次,我們繼續修改文件,將文件add並commit。然後發現又寫錯了…可以通過回滾的方式撤銷掉本次修改。但是,即使回滾,使用git reflog仍然可以看到這次提交。所以,只要提交到了git的分支區,就不可能刪除。
8. 刪除文件
我們發現某個文件無用,就刪了它。
rm test01.txt
當然也可以手動刪除。刪除過後查看版本庫狀態
git status
將刪除的文件增加到暫存區。使用git rm 文件名 命令。
git rm test01.txt
當然,也可以使用git add命令。查看版本庫。
git status
將其提交到分支。
9. git管理和跟蹤的是什麼
git跟蹤並管理的是修改,而並非是文件。
舉個例子,首先,我們在test01.txt中新增一行文字:111111。
然後將文件添加到暫存區中。
再在test01.txt中新增遺憾文件:222222。
然後提交到分支。
然後查看版本庫狀態,會發現, 222222並未提交。
10. 命令總結
11. 通過tag指定版本標籤名
git中的版本都有版本編號,對代碼的提交和回滾都是基於版本編號進行的,但是git中的版本編號是一串隨機串,不好記憶,此時可以使用標籤機制爲某個提交增加標籤,方便以後查找。大了標籤之後所有需要用到版本編號的位置都可以用對應的標籤替代。標籤也可以理解成別名。
首先,在當前分支的當前提交上打標籤。
git tag v1.0
也可以查看所有標籤
git tag
也可以將標籤打到以前提交的版本上
git tag v0.9 f52c663
還可以創建帶有說明的標籤。用-a指定標籤名,用-m指定說明文字。
git tag -a v0.1 -m "version 0.1 released " 1094adb
可以通過如下命令查看一個tag信息:
git show v0.1
如果標籤打錯了,也可以刪除
git tag -d v0.1
如果要推送標籤到某個遠程,使用git push origin tagName。
git push origin v1.0
也可以一次性將所有尚未推送到遠程的本地標籤全部推送過去。
git push origin --tags
但是,如果標籤已經推送到了遠程,刪除的時候就會麻煩了。首先要刪除本地標籤
git tag -d v0.9
然後再從遠程刪除
git push origin :refs/tags/v0.9
12. 指定git忽略部分內容
可以在倉庫中配置.gitIgnore文件,在其中配置哪些文件是不需要git管理,則git在處理此倉庫時,會自動自動忽略聲明的內容。
.gitIgnore文件格式:
1)空格不匹配任意文件,可作爲分隔符,可用反斜槓轉義
2)以“#”開頭的行都會被 Git 忽略。即#開頭的文件標識註釋,可以使用反斜槓進行轉義。
3)可以使用標準的glob模式匹配。所謂的glob模式是指shell所使用的簡化了的正則表達式。
4)以斜槓"/“開頭表示目錄;”/“結束的模式只匹配文件夾以及在該文件夾路徑下的內容,但是不匹配該文件;”/“開始的模式匹配項目跟目錄;如果一個模式不包含斜槓,則它匹配相對於當前 .gitignore 文件路徑的內容,如果該模式不在 .gitignore 文件中,則相對於項目根目錄。
5)以星號”“通配多個字符,即匹配多個任意字符;使用兩個星號”**" 表示匹配任意中間目錄,比如a/**/z
可以匹配 a/z, a/b/z 或 a/b/c/z等。
6)以問號"?“通配單個字符,即匹配一個任意字符;
7)以方括號”[]“包含單個字符的匹配列表,即匹配任何一個列在方括號中的字符。比如[abc]表示要麼匹配一個a,要麼匹配一個b,要麼匹配一個c;如果在方括號中使用短劃線分隔兩個字符,表示所有在這兩個字符範圍內的都可以匹配。比如[0-9]表示匹配所有0到9的數字,[a-z]表示匹配任意的小寫字母)。
8)以歎號”!“表示不忽略(跟蹤)匹配到的文件或目錄,即要忽略指定模式以外的文件或目錄,可以在模式前加上驚歎號(!)取反。需要特別注意的是:如果文件的父目錄已經被前面的規則排除掉了,那麼對這個文件用”!“規則是不起作用的。也就是說”!“開頭的模式表示否定,該文件將會再次被包含,如果排除了該文件的父級目錄,則使用”!"也不會再次被包含。可以使用反斜槓進行轉義。
需要謹記:git對於.ignore配置文件是按行從上到下進行規則匹配的,意味着如果前面的規則匹配的範圍更大,則後面的規則將不會生效;
因爲markdown語法問題,有些符號可能會顯示錯誤,故在下面附上圖片版:
示例:
# 表示此爲註釋,將被Git忽略
.a 表示忽略所有 .a 結尾的文件
!lib.a 表示但lib.a除外
/TODO 表示僅僅忽略項目根目錄下的 TODO 文件,不包括 subdir/TODO
build/ 表示忽略 build/目錄下的所有文件,過濾整個build文件夾;
doc/.txt 表示會忽略doc/notes.txt但不包括 doc/server/arch.txt
bin/: 表示忽略當前路徑下的bin文件夾,該文件夾下的所有內容都會被忽略,不忽略 bin 文件
/bin: 表示忽略根目錄下的bin文件
/.c: 表示忽略cat.c,不忽略 build/cat.c
debug/.obj: 表示忽略debug/io.obj,不忽略 debug/common/io.obj和tools/debug/io.obj
/foo: 表示忽略/foo,a/foo,a/b/foo等
a//b: 表示忽略a/b, a/x/b,a/x/y/b等
!/bin/run.sh 表示不忽略bin目錄下的run.sh文件
*.log: 表示忽略所有 .log 文件
config.php: 表示忽略當前路徑的 config.php 文件
/mtk/ 表示過濾整個文件夾
*.zip 表示過濾所有.zip文件
/mtk/do.c 表示過濾某個具體文件
被過濾掉的文件就不會出現在git倉庫中(gitlab或github)了,當然本地庫中還有,只是push的時候不會上傳。
需要注意的是,gitignore還可以指定要將哪些文件添加到版本管理中,如下:
!.zip
!/mtk/one.txt
唯一的區別就是規則開頭多了一個感嘆號,Git會將滿足這類規則的文件添加到版本管理中。爲什麼要有兩種規則呢?
想象一個場景:假如我們只需要管理/mtk/目錄中的one.txt文件,這個目錄中的其他文件都不需要管理,那麼.gitignore規則應寫爲::
/mtk/
!/mtk/one.txt
假設我們只有過濾規則,而沒有添加規則,那麼我們就需要把/mtk/目錄下除了one.txt以外的所有文件都寫出來!
注意上面的/mtk/*不能寫爲/mtk/,否則父目錄被前面的規則排除掉了,one.txt文件雖然加了!過濾規則,也不會生效!
還有一些規則如下:
fd1/*
說明:忽略目錄 fd1 下的全部內容;注意,不管是根目錄下的 /fd1/ 目錄,還是某個子目錄 /child/fd1/ 目錄,都會被忽略;
/fd1/*
說明:忽略根目錄下的/fd1/目錄的全部內容
/*
!.gitignore
!/fw/
/fw/*
!/fw/bin/
!/fw/sf/
說明:忽略全部內容,但是不忽略 .gitignore文件、根目錄下的/fw/bin/和/fw/sf/目錄;主要要對bin/的父目錄使用!規則,使其不被排除。
因爲markdown語法問題,有些符號可能會顯示錯誤,故在下面附上圖片版:
13. 總結
git本地管理到此結束,本地管理的話,這些命令也基本夠用
1. 分清工作區、暫存區和分支區
2. add是提交到工作區,commit是提交到分支區。一旦提交到分支區,永遠不可能刪除。即使回滾到以前的版本,仍然可以找到提交的信息。
3. git log 查看當前版本的以前所有版本的信息,git reflog查看所有的版本的信息。
4. git commit時,一定要好好寫 -m 後面的信息
5. 其實前面忘說了,如果覺得麻煩,可以使用 git add * 提交所有文件,也可以 git add /src 提交根目錄src下所有文件。add的命令還是很靈活的。
6. git指定忽略內容沒必要背,知道有這麼個東西,用的時候現查就行了。