Git 與Github
1、簡(jiǎn)單介紹
很多人都知道,Linus在1991年創(chuàng)建了開源的Linux,從此,Linux系統(tǒng)不斷發(fā)展,已經(jīng)成為最大的服務(wù)器系統(tǒng)軟件了。
Linus雖然創(chuàng)建了Linux,但Linux的壯大是靠全世界熱心的志愿者參與的,這么多人在世界各地為L(zhǎng)inux編寫代碼,那Linux的代碼是如何管理的呢?
事實(shí)是,在2002年以前,世界各地的志愿者把源代碼文件通過diff的方式發(fā)給Linus,然后由Linus本人通過手工方式合并代碼!
你也許會(huì)想,為什么Linus不把Linux代碼放到版本控制系統(tǒng)里呢?不是有CVS、SVN這些免費(fèi)的版本控制系統(tǒng)嗎?因?yàn)長(zhǎng)inus堅(jiān)定地反對(duì)CVS和SVN,這些集中式的版本控制系統(tǒng)不但速度慢,而且必須聯(lián)網(wǎng)才能使用。有一些商用的版本控制系統(tǒng),雖然比CVS、SVN好用,但那是付費(fèi)的,和Linux的開源精神不符。
不過,到了2002年,Linux系統(tǒng)已經(jīng)發(fā)展了十年了,代碼庫(kù)之大讓Linus很難繼續(xù)通過手工方式管理了,社區(qū)的弟兄們也對(duì)這種方式表達(dá)了強(qiáng)烈不滿,于是Linus選擇了一個(gè)商業(yè)的版本控制系統(tǒng)BitKeeper,BitKeeper的東家BitMover公司出于人道主義精神,授權(quán)Linux社區(qū)免費(fèi)使用這個(gè)版本控制系統(tǒng)。
安定團(tuán)結(jié)的大好局面在2005年就被打破了,原因是Linux社區(qū)牛人聚集,不免沾染了一些梁山好漢的江湖習(xí)氣。開發(fā)Samba的Andrew試圖破解BitKeeper的協(xié)議(這么干的其實(shí)也不只他一個(gè)),被BitMover公司發(fā)現(xiàn)了(監(jiān)控工作做得不錯(cuò)!),于是BitMover公司怒了,要收回Linux社區(qū)的免費(fèi)使用權(quán)。
Linus可以向BitMover公司道個(gè)歉,保證以后嚴(yán)格管教弟兄們,嗯,這是不可能的。實(shí)際情況是這樣的:
Linus花了兩周時(shí)間自己用C寫了一個(gè)分布式版本控制系統(tǒng),這就是Git!一個(gè)月之內(nèi),Linux系統(tǒng)的源碼已經(jīng)由Git管理了!牛是怎么定義的呢?大家可以體會(huì)一下。
Git迅速成為最流行的分布式版本控制系統(tǒng),尤其是2008年,GitHub網(wǎng)站上線了,它為開源項(xiàng)目免費(fèi)提供Git存儲(chǔ),無數(shù)開源項(xiàng)目開始遷移至GitHub,包括jQuery,PHP,Ruby等等。
歷史就是這么偶然,如果不是當(dāng)年BitMover公司威脅Linux社區(qū),可能現(xiàn)在我們就沒有免費(fèi)而超級(jí)好用的Git了。
2、基本概念
安裝git
yum install git #centos系統(tǒng)默認(rèn)安裝好的
初始化版本庫(kù)
git init #會(huì)生成一個(gè).git的隱藏目錄
- 重點(diǎn)理解:工作區(qū)、暫存區(qū)和master分支
工作區(qū):用于平時(shí)的開發(fā),編輯之用,在你創(chuàng)建的倉(cāng)庫(kù)目錄下,就是工作區(qū)
暫存區(qū):用來暫時(shí)存放準(zhǔn)備提交到倉(cāng)庫(kù)的文檔的地方,在.git目錄下
master分區(qū):真正用來存放和發(fā)布已經(jīng)完成代碼的地方
關(guān)系圖
-
最初文件在工作區(qū)
image -
git add readme.txt 后,文件被添加到暫存區(qū),此時(shí)工作區(qū)的文件和暫存區(qū)的文件一致。
image -
git commit -m "new file readme.txt" 后,在暫存區(qū)的所有文件和目錄都將后被提交(移動(dòng))到分支 master。
image
3、一個(gè)文件被提交到版本庫(kù)的基本流程
- 初始化一個(gè)倉(cāng)庫(kù)
mkdir my_project #建立一個(gè)目錄
cd my_project #進(jìn)入這個(gè)目錄
git init #在當(dāng)前目錄初始化一個(gè)倉(cāng)庫(kù)
- 提交版本
touch/mkdir… #在工作區(qū)創(chuàng)建/修改文件
git add file #將文件添加到暫存區(qū)
git commit -m "描述" #將暫存區(qū)的內(nèi)容提交到master分支
可以多次添加,一次提交
- 暫存區(qū)是Git非常重要的概念,弄明白了暫存區(qū),就弄明白了Git的很多操作到底干了什么。
4、更多git操作
時(shí)空穿越
git 支持版本的回滾操作,并且,你可以在之前任何一個(gè)版本和當(dāng)前最新有效提交后的版本之間來回切換。
將代碼提交之后,突然有不要提交的代碼了,這種情況的話,就要用到回滾了
-
git log
命令可以查看提交的版本歷史
git log 常用參數(shù)
某一個(gè)人的提交記錄:
git log --author=name
一個(gè)壓縮后的每一條提交記錄只占一行的輸出:
git log --pretty=oneline
或者你想通過 ASCII 藝術(shù)的樹形結(jié)構(gòu)來展示所有的分支, 每個(gè)分支都標(biāo)示了他的名字和標(biāo)簽:
git log --graph --oneline --decorate --all
看看哪些文件改變了:
git log --name-status
更多的信息,參考:
git log --help
最前面的一串字符,例如
da197f...7955
是commit id(版本號(hào)),是一個(gè)SHA1計(jì)算出來的一個(gè)非常大的數(shù)字,用十六進(jìn)制表示。
每提交一個(gè)新版本,實(shí)際上Git就會(huì)把它們自動(dòng)串成一條時(shí)間線
-
開始版本回退
命令用法:
git reset --hard 版本號(hào)
版本號(hào)的表示形式:
1. 可以是十六進(jìn)制的形式
2. 也可以是 Git 內(nèi)部變量的形式。 上一個(gè)版本就是HEAD^
上上一個(gè)版本就是HEAD^^
100 個(gè)版本寫成 HEAD~100
-
回到未來
git reflog
命令會(huì)記錄每一次導(dǎo)致版本變化的命令以及涉及到的版本號(hào)
bash-3.2$ git reflog
a34d237 (HEAD -> master) HEAD@{0}: reset: moving to HEAD^
da197f4 HEAD@{1}: commit: add GLP for readme.txt
a34d237 (HEAD -> master) HEAD@{2}: commit: add distributed
63e4ecd HEAD@{3}: commit (initial): crete a readme file
(END)
利用命令進(jìn)行版本切換
bash-3.2$ git reset --hard da197f4
-
深入理解 add 和 commit
再次強(qiáng)調(diào)一下,commit 提交的是暫存區(qū)的所有文件
,而不是工作區(qū)的被修改的文件;每次修改,如果不add到暫存區(qū),那就不會(huì)加入到commit中。
-
改變未來(撤銷修改)
你在工作區(qū),對(duì) readme.txt 文件添加了一行新的內(nèi)容 : "這一行內(nèi)容我不能添加",當(dāng)你想要撤銷掉這行內(nèi)容時(shí)有兩種方法:
1、一種是你記得,在這之前你記得你的所有修改。這里我是記得的,因?yàn)槲抑惶砑恿艘恍袃?nèi)容而已,這樣的情況下,你重新編輯這個(gè)文件,刪除你不要的這一行即可。
2、另一種是,修改的太多了,一片混亂,使你無法繼續(xù)進(jìn)行下去。你只想會(huì)到?jīng)]有修改之前,就是恢復(fù)到最近一次提交到 master 之后的狀態(tài)。那就需要用git checkout -- readme.txt
命令
git checkout -- readme.txt
意思就是,readme.txt
文件在被添加到暫存區(qū)之前,對(duì)在工作區(qū)對(duì)此文件的修改全部撤銷
(2)這里有兩種情況:
1、一種是,在工作區(qū)對(duì) readme.txt 進(jìn)行修改之后,還沒有被放到暫存區(qū),暫存區(qū)為空;現(xiàn)在,撤銷修改就回到和版本庫(kù)一模一樣的狀態(tài);
2、一種是,在工作區(qū)對(duì) readme.txt 進(jìn)行修改之后,并且已經(jīng)添加到暫存區(qū)了,接著又在工作區(qū)對(duì)文件作了修改。此時(shí),現(xiàn)在 readme.txt 的狀態(tài)是:在工作區(qū)是一種最新修改后的狀態(tài)
在暫存區(qū)是另一種 add 后的狀態(tài)
在 master 是版本庫(kù)最新的狀態(tài)
此時(shí),撤銷修改,工作區(qū)的文件就會(huì)和暫存區(qū)文件的狀態(tài)保持一致,master 的文件狀態(tài)不變。
bash-3.2$ git checkout -- study/readme.txt
- 注意:
git checkout -- readme.txt
只能使工作區(qū)和緩存區(qū)內(nèi)容保持一致
想要把緩存區(qū)的內(nèi)容也去掉,只能回退版本再統(tǒng)一工作區(qū)和緩存區(qū)。
這樣工作區(qū)就回到了master的狀態(tài)
撤銷總結(jié)
1、當(dāng)你改亂了工作區(qū)某個(gè)文件的內(nèi)容,想直接丟棄工作區(qū)的修改時(shí),用命令git checkout -- file。
2、當(dāng)你不但改亂了工作區(qū)某個(gè)文件的內(nèi)容,還添加到了暫存區(qū)時(shí),想丟棄修改,分兩步,第一步用命令git reset HEAD file,就回到了 1,第二步按 1 操作。
3、已經(jīng)提交了不合適的修改到版本庫(kù)時(shí),想要撤銷本次提交,參考版本回退一節(jié),不過前提是沒有推送到遠(yuǎn)程庫(kù)。
-
自毀前程(刪除)
在Git中,刪除也是一個(gè)修改操作,分兩種情況:
1、一種是,在工作區(qū)刪除的文件,已經(jīng)被添加到了暫存區(qū),但是沒有提交。
當(dāng)你在工作區(qū)刪除一個(gè)你認(rèn)為沒用的文件時(shí),但是這個(gè)文件被已經(jīng)添加了暫存區(qū),這樣 Git 會(huì)知道你刪除了這個(gè)文件,因?yàn)榇藭r(shí),工作區(qū)和版本庫(kù)就不一致了,git status命令會(huì)立刻告訴你哪些文件被刪除了
- 此時(shí),你有兩種選擇:
1.真的要?jiǎng)h除這個(gè)文件
可以用git rm
刪除在暫存區(qū)的文件
bash-3.2$ git rm useless.txt
rm 'useless.txt'
bash-3.2$ git status
On branch master
nothing to commit, working tree clean
2.刪錯(cuò)了,需要把文件恢復(fù)到工作區(qū)
利用git checkout -- file
把工作區(qū)和緩存區(qū)統(tǒng)一
2、另一種是,在工作區(qū)刪除的文件,添加到了暫存區(qū),并且提交了。
- 此時(shí),你也有兩種選擇:
1.真的要?jiǎng)h除這個(gè)文件
rm useless.txt //刪除這個(gè)文件
git rm useless.txt //從版本庫(kù)刪除這個(gè)文件
git commit -m "del file useless.txt" //并且提交
此時(shí),文件就從版本庫(kù)中被刪除了,一般情況下它再也會(huì)不到你身邊了
2.刪錯(cuò)了,需要把文件恢復(fù)到工作區(qū)
bash-3.2$ ls useless.txt
ls: useless.txt: No such file or directory
bash-3.2$ git checkout -- useless.txt
bash-3.2$ ls useless.txt
useless.txt
bash-3.2$ git status
On branch master
nothing to commit, working tree clean
總結(jié)
git checkout其實(shí)是用<mark style="box-sizing: border-box;">版本庫(kù)</mark>里的版本替換工作區(qū)的版本,無論工作區(qū)是修改還是刪除,都可以“一鍵還原”。
版本庫(kù): 包括 暫存區(qū) 和 分支
5、分支管理
分支就像科幻電影里的平行宇宙,當(dāng)你正在電腦前努力學(xué)習(xí)Git的時(shí)候,另一個(gè)你正在另一個(gè)平行宇宙里努力學(xué)習(xí)SVN。
如果兩個(gè)平行宇宙互不干擾,那對(duì)現(xiàn)在的你也沒啥影響。不過,在某個(gè)時(shí)間點(diǎn),兩個(gè)平行宇宙合并了,結(jié)果,你既學(xué)會(huì)了Git又學(xué)會(huì)了SVN!
Git的分支是與眾不同的,無論創(chuàng)建、切換和刪除分支,Git在1秒鐘之內(nèi)就能完成!無論你的版本庫(kù)是1個(gè)文件還是1萬個(gè)文件。
-
創(chuàng)建與合并分支
在版本回退里,你已經(jīng)知道,每次提交,Git 都把它們串成一條時(shí)間線,這條時(shí)間線就是一個(gè)分支。截止到目前,只有一條時(shí)間線,在 Git 里,這個(gè)分支叫主分支,即master 分支。HEAD 嚴(yán)格來說不是指向提交,而是指向 master, master 才是指向提交的,所以HEAD 指向的就是當(dāng)前分支。
一開始的時(shí)候,master 分支是一條線,Git 用 master 指向最新的提交點(diǎn),再用HEAD 指向 master,就能確定當(dāng)前分支,以及當(dāng)前分支的提交點(diǎn):
每次提交,master 分支都會(huì)向前移動(dòng)一步,這樣,隨著你不斷提交,master分支的線也越來越長(zhǎng)
當(dāng)我們創(chuàng)建新的分支,例如 bac 時(shí),Git 會(huì)新建一個(gè)指針叫 bac,指向 master 相同的提交點(diǎn),再把HEAD指向 bac,就表示當(dāng)前分支在 bac 上:
- Git創(chuàng)建一個(gè)分支很快,因?yàn)榇藭r(shí),只是增加一個(gè) bac 指針,然后改改 HEAD 的指向即可,工作區(qū)的文件都沒有任何變化!
從現(xiàn)在開始,對(duì)工作區(qū)的修改和提交就是針對(duì) bac 分支了,比如新提交一次后,bac 指針往前移動(dòng)一步,而master 指針不變,HEAD 指針同樣不變:
假如我們?cè)赿ev上的工作完成了,就可以把 bac 合并到 master 上。Git 怎么合并呢?很簡(jiǎn)單,先切換到 master 分支,此時(shí) HEAD 指針就會(huì)指向 master 指針,之后就是直接把master 指向 bac 的當(dāng)前提交點(diǎn),就完成了合并:
- 所以Git合并分支也很快!就改改指針,工作區(qū)內(nèi)容也不需要變!
合并完分支后,你覺得 bac 分支沒什么用了,甚至可以刪除 bac 分支。刪除 bac 分支就是把 bac 指針給刪掉,刪掉后,我們就剩下了一條 master 分支:
實(shí)戰(zhàn)
1、創(chuàng)建分支 bac
bash-3.2$ git branch bac
2、切換到分支 bac
bash-3.2$ git checkout bac
Switched to branch 'bac'
3、創(chuàng)建并切換分支
上面的兩條命令可以合并為一條
bash-3.2$ git checkout -b bac
4、查看分支
bash-3.2$ git branch
* bac
master
// 星號(hào)代表當(dāng)前所在的分支
5、在分支 bac 上修改文件,并創(chuàng)建一個(gè)新文件 bac_new.txt,最后正常添加、提交。
bash-3.2$ echo "changes on the branch of bac" >> study/readme.txt
bash-3.2$ touch bac_new.txt
bash-3.2$ git add .
bash-3.2$ git commit -m "added a new line in readme.txt,create a file bac_new.txt"
[bac 096a515] added a new line in readme.txt,create a file bac_new.txt
2 files changed, 1 insertion(+)
create mode 100644 bac_new.txt
bash-3.2$ tail -3 study/readme.txt
Git 管理的是修改
Git 管理的不是文件
changes on the branch of bac
bash-3.2$ ls
bac_new.txt newdir study
// 此時(shí)會(huì)被提交到 bac 分支,工作區(qū)當(dāng)然也是屬于 bac 分支的
6、切換到 master 分支,并觀察文件的變化
bash-3.2$ git checkout master
Switched to branch 'master'
bash-3.2$ tail -3 study/readme.txt
Git is free software distributed under the GPL.
Git 管理的是修改
Git 管理的不是文件
bash-3.2$ ls
newdir study
- 切換到 master 分支后, HEAD 指針也就會(huì)指向 master 所指向的提交點(diǎn),工作區(qū)也就屬于 master,自然,你看不到在 bac 分支對(duì)文件做的任何修改
7、把分支 bac 合并到 master分支
bash-3.2$ git branch # 確定一下你現(xiàn)在所在的分支是 mster
bac
* master
bash-3.2$ git merge bac # 把 bac 分支合并到 master
Updating 6b0e1ca..096a515
Fast-forward
bac_new.txt | 0
study/readme.txt | 1 +
2 files changed, 1 insertion(+)
create mode 100644 bac_new.txt
bash-3.2$ ls # 確認(rèn)工作區(qū)的文件
bac_new.txt newdir study
-
把 bac 分支合并到 master 分支后的文件變化:
image
8、合并完成后,刪除分支 bac,并查看分支
bash-3.2$ git branch -d bac
Deleted branch bac (was 096a515).
bash-3.2$ git branch
* master
bash-3.2$
- 刪除分支 bac 就變成下圖的樣子:
image
總結(jié)
* 查看分支:git branch
* 創(chuàng)建分支:git branch <name>
* 切換分支:git checkout <name>
* 創(chuàng)建+切換分支:git checkout -b <name>
* 合并某分支到當(dāng)前分支:git merge <name>
* 刪除分支:git branch -d <name>
6、HEAD 指針
HEAD 指向哪個(gè)版本,當(dāng)前就是哪個(gè)版本;當(dāng)你來回切換版本的時(shí)候,Git 只是把 HEAD 指向你要切換的版本,順便把工作區(qū)的文件更新一下,見下圖:
-
處于最新提交后的指針指向:
image -
版本回退后的指針指向:
image
7、標(biāo)簽管理
標(biāo)簽是來為某個(gè)版本取個(gè)標(biāo)簽名
1、創(chuàng)建標(biāo)簽
git tag tagname #為最新版本打標(biāo)簽
注意: 標(biāo)簽名是為最近提交的一次打的,代表了最近提交的那個(gè)版本
git tag #查看所有標(biāo)簽
git show tagname #查看標(biāo)簽信息
2、刪除標(biāo)簽
git tag -d tagname #刪除某個(gè)標(biāo)簽
3、小結(jié)
git tag -a tagname -m 'comment' #添加標(biāo)簽并且指定描述信息
8、遠(yuǎn)程倉(cāng)庫(kù)
設(shè)置全局用戶名和郵箱
git config --global user.name "anan"
git config --global user.email "283728@qq.com"
克隆遠(yuǎn)程倉(cāng)庫(kù)到本地
本地沒有倉(cāng)庫(kù)的時(shí)候,創(chuàng)建一個(gè)本地目錄,并于遠(yuǎn)程的GitHub倉(cāng)庫(kù)建立聯(lián)系
git clone git@gitlab.com:alice/test.git #將遠(yuǎn)程倉(cāng)庫(kù)克隆到本地
cd test
touch file
git add .
git commit -m "new file"
git push -u origin master #將這個(gè)倉(cāng)庫(kù)的master分支上傳到遠(yuǎn)程倉(cāng)庫(kù)
注意: 只有第一次上傳到遠(yuǎn)程需要-u
在本地目錄創(chuàng)建倉(cāng)庫(kù),建立聯(lián)系,上傳遠(yuǎn)程
cd floder
git init #將目錄變成倉(cāng)庫(kù)
git remote add git@gitlab.com:alice/test.git #與遠(yuǎn)程倉(cāng)庫(kù)建立聯(lián)系
git add .
git commit -m ''
git push -u origin master
將本地已有的倉(cāng)庫(kù)上傳到遠(yuǎn)程
cd floder
git remote rename origin old-origin
git remote add origin git@gitlab.com:alice/test.git
git push -u origin --all
git push -u origin --tags