GIT入門-基本概念與操作
首先必須說明的是, 這篇文章不是闡述GIT原理性和比較深入的文章。只是對于日常開發(fā)中比較常用的需求的總結(jié)和GIT 這些命令大體的原理解釋。所以掌握這個只能說能夠應(yīng)付一定的開發(fā)需求。但是如果你是個追求極值和完美的人。應(yīng)該 再去了解下GIT具體的模型和實現(xiàn)細(xì)節(jié)。需要說明的是, 技術(shù)性東西,得先入門再深入理論,這很重要,入門可以讓你不斷 的進(jìn)行實踐,加深理解,而不是紙上談兵,看著理論無從下手。GIT的應(yīng)用我們主要掌握GIT中團(tuán)隊開發(fā)協(xié)助的常用命令和 場景。在這之前,我們需要先介紹一些必備概念。
A、基本概念
一、Repository
repository, 使用過SVN的應(yīng)該都知道,這是版本庫。何為版本庫,簡單理解就是用來存儲和檢索數(shù)據(jù)的一個倉庫,只不過我們 用它來存儲代碼,來實現(xiàn)團(tuán)隊開發(fā)中的代碼共享,以此來實現(xiàn)協(xié)同工作。簡單的所就是用來保證,一個軟件項目中的代碼同步。
在GIT中的版本庫一般有兩個, 一個是本地版本庫,一個是服務(wù)器版本庫(共享版本庫)。這是因為GIT本身就是設(shè)計為非集中式(分布式) 版本控制器,其優(yōu)點就是,當(dāng)你沒有網(wǎng)絡(luò)的時候,你不用依賴于服務(wù)器版本庫(共享版本庫)。想一下,你使用SVN的時候, 可以不用聯(lián)網(wǎng)嗎?如果希望一個項目交由GIT來管理,那么應(yīng)該再該項目所在的同級目錄中有一個.GIT文件夾,該文件夾里存放的就是 本地版本庫,這時候,當(dāng)你沒有網(wǎng)絡(luò)的時候,可以在本地進(jìn)行版本管理。再有網(wǎng)絡(luò)連接時候, 再將本地版本庫與共享版本庫進(jìn)行同步。
在GIT中,我們使用git init命令來創(chuàng)建初始化一個版本庫。其會自動生產(chǎn).GIT文件夾和對應(yīng)的文件,這時候就能用GIT進(jìn)行版本管理。 git init --bare 用在(共享版本庫中),因為在共享版本庫中, 我們不需要工程文件夾(即工作空間), 因為共享版本庫,相當(dāng)于一個存放 代碼的服務(wù)器,不需要工作空間。
總而言之
- git init用在我們本地工作空間中需要進(jìn)行管理的項目中。
- git init --bare用在創(chuàng)建共享版本庫,用來于本地版本庫進(jìn)行同步,實現(xiàn)多人開發(fā)的版本庫。
二、工作區(qū)
所謂的工作區(qū), 就是你項目所在的文件夾里,都可以統(tǒng)稱為工作區(qū)。
三、暫存區(qū)(stage、index)
該區(qū)域是用來保存要提交到本地版本庫中的所有文件, 稱為stage或index。當(dāng)執(zhí)行g(shù)it commit指令時候,會一次性將該區(qū)域類的文件提交到 本地版本庫??梢岳斫鉃橄喈?dāng)于一個緩存區(qū),用來緩存要交給本地版本庫管理的文件。而要將文件加入暫存區(qū)域,需要 使用git add指令進(jìn)行添加操作。也就是說要提交到本地版本庫需要兩步操作: git add + git commit。執(zhí)行完這兩條只是提交到了本地版本庫, 只能自己來使用,要是在團(tuán)隊開發(fā)中,要需要執(zhí)行g(shù)it push提交到共享版本庫.
四、HEAD指針
簡而言之,這里的HEAD指針就是一個用來標(biāo)識當(dāng)前所在的版本。也就是我們是通過HEAD所指向的版本來確定 當(dāng)前所在的版本,所以當(dāng)我們進(jìn)行版本切換的時候,進(jìn)行的就是改變HEAD指針的指向。
B、基本操作
一、增
由GIT的模型我們知道,要交給本地倉庫管理,我們必須先將其提交到stage中。再又stage提交到本地版本庫中,我將這步操作歸納為增。以此來 于數(shù)據(jù)庫中的增概念類比,方便學(xué)習(xí)。所以我們這里所謂的增加細(xì)分而來個分為向stage中增加,和向本地版本庫中增加。需要明確的是本地版本庫 的增, 是依賴于stage中的。具體的例子演示。
由于我們要模擬多人開發(fā),所以先完成以下的準(zhǔn)備工作。
準(zhǔn)備工作
1.創(chuàng)建一個SWPTest文件夾,我們準(zhǔn)備在該文件夾中創(chuàng)建A用戶的工作空間,和共享代碼庫。
執(zhí)行如下操作
- mkdir SWPTest
- cd SWPTest/
- mkdir AWorker sharedRep
2.創(chuàng)建共享版本庫(模擬遠(yuǎn)程版本庫)
- cd sharedRep
- git init --bare
3.用戶A將服務(wù)器上版本庫下載下來,準(zhǔn)備工作。并加入.gitigonre忽略文件(用來過濾掉項目中不提交給 版本庫進(jìn)行管理的文件)。(記得在SWPTest根目錄位置執(zhí)行命令)
- cd AWorder
- git clone ../sharedRep/
- touch .gitignore // 在工作區(qū)創(chuàng)建了.gitignore文件
- open .gitignore
- git add .gitignore // 執(zhí)行完這步, 將工作區(qū)的.gitignore文件提交到了暫存區(qū)(緩存區(qū))
- git status -s // 查詢的是工作區(qū)與暫存區(qū)的文件狀態(tài)(文件狀態(tài)的理解是重點)
- git commit -m "添加gitignore文件" // 執(zhí)行完之后,一次性將暫存區(qū)的內(nèi)容提交到本地版本庫。此時暫存區(qū)清空。
然后向.gitignore文件中粘貼object-c的忽略信息。(Github上搜索gitignore)
總結(jié):以上三小步,分別為服務(wù)器端的版本庫的創(chuàng)建(1,2), 與客戶端(3,)clone到本地(如果該工程沒有g(shù)itignore文件, 最好自己加上, 并添加到本地版本庫和服務(wù)端版本庫(共享版本庫))。
增總結(jié)(三種不同的添加)
- 1.向stage中增加(提交): git add <file> // <file>值的是要交給git管理的文件,上面第三步的最后一小步,就是stage添加
- 2.向本地版本庫種增加(提交): git commit // 基于stage緩存
- 3.向共享版本庫增加(提交): git push origin master // 將當(dāng)前的暫存區(qū)的所有數(shù)據(jù)提交到共享版本庫(遠(yuǎn)程版本庫)
- 在執(zhí)行完上面3個小步驟時候,你也可以立即將該文件同步到共享代碼庫,方便其它同事的使用。
- git push origin master // 提交到主分支
二、刪
在進(jìn)行刪除模擬前,我們先做如下準(zhǔn)備工作,創(chuàng)建a.m, b.m, c.m三個源文件。并準(zhǔn)備將其交給本地版本庫管理。 所以進(jìn)行向stage添加操作。具體如下
- suweipeng:sharedRep sixleaves$ touch a.m b.m c.m
- suweipeng:sharedRep sixleaves$ git add *.m
- suweipeng:sharedRep sixleaves$ git status -s
- A a.m
- A b.m
- A c.m
- suweipeng:sharedRep sixleaves$
當(dāng)查詢當(dāng)前暫存區(qū)的文件狀態(tài)的時候,會發(fā)現(xiàn),已經(jīng)變成A(第一欄表示暫存區(qū),第二欄表示工作區(qū)),表示已經(jīng)添加到暫存區(qū)域, 狀態(tài)為A(add)。接著我們開始研究刪除。 通過模型,我們可以知道,git中又有三塊存儲文件的地方,分別是工作區(qū)、暫存區(qū)(stage)、分支。所以,我們應(yīng)該 在學(xué)習(xí)之前就問自己一個問題,如果有刪除操作,是針對哪塊區(qū)域的。 我們通過具體操作來理解,如下(針對的緩存區(qū)和工作區(qū)一起刪除)
- suweipeng:sharedRep sixleaves$ git rm a.m
- error: the following file has changes staged in the index:
- a.m
- (use --cached to keep the file, or -f to force removal)
- suweipeng:sharedRep sixleaves$ git rm -f a.m
- rm 'a.m'
- suweipeng:sharedRep sixleaves$ ls -al
- total 8
- drwxr-xr-x 6 sixleaves staff 204 7 21 16:43 .
- drwxr-xr-x 3 sixleaves staff 102 7 21 11:34 ..
- drwxr-xr-x 13 sixleaves staff 442 7 21 16:43 .git
- -rw-r--r--@ 1 sixleaves staff 836 7 21 15:59 .gitignore
- -rw-r--r-- 1 sixleaves staff 0 7 21 16:27 b.m
- -rw-r--r-- 1 sixleaves staff 0 7 21 16:27 c.m
- suweipeng:sharedRep sixleaves$
第一行的意思是將a.m這個文件從工作區(qū),并在暫存區(qū)響應(yīng)的文件中標(biāo)記刪除操作。但是提示出錯, 根據(jù)提示,我們可以使用-f來強(qiáng)制執(zhí)行。
在Git 2.0中已經(jīng)支持一種更加容易理解的方式來進(jìn)行文件的刪除。
新方法:
現(xiàn)在我們直接使用系統(tǒng)自帶的rm程序來刪除,而不使用git rm。 步驟如下
- 使用rm刪除工作區(qū)的文件。
- 使用git add <刪除的文件名>將工作區(qū)的刪除添加到暫存區(qū)。(刪除工作區(qū)、暫存區(qū)中指定的文件)
- 使用git commit -m "刪除文件b"。將文件從本地倉庫中刪除。(刪除工作區(qū)、暫存區(qū)中件、本地版本庫指定的文)
- 使用git push origin master. 則這時候,不僅可以將本地版本庫中的文件刪除,同時也將共享版本庫中的文件刪除。((刪除工作區(qū)、暫存區(qū)中件、本地版本庫、共享版本庫指定的文件)。
也許你會想問,那么如果是已經(jīng)提交到本地版本庫中的文件呢?只是想將其從本地版本庫中刪除,是不能用這種新方法的,因為這種方法的前提你要刪除工作區(qū)間的文件,并且讓版本庫中的也刪除。那么如何應(yīng)對這種需求,請看下面。
git rm剖析: 此時我們來驗證git rm的作用區(qū)域,在驗證的時候,我這邊只剩下一個e.m文件,并且已經(jīng)交給git管理。 此時再重新創(chuàng)建b.m與c.m并將其交給git管理。
具體分析: 首先怎么驗證所git rm -f a.m刪除的是這兩個區(qū)域的呢?如果一個文件已經(jīng)提交到版本庫中,那么此時在使用該操作會不會影響版本庫呢?答案是否定的,它只影響暫存區(qū)和工作區(qū),也就是所只刪除這兩個區(qū)域的元素。驗證思路很簡單,我們創(chuàng)建一個文件z.m,然后將其提交給本地版本庫進(jìn)行管理,在提交的后,暫存區(qū)就清空了,所以此時只剩下版本庫和工作區(qū)又這個文件,此時我們執(zhí)行g(shù)it rm,接著在同步到共享版本庫,再另外一個員工的工作目錄中clone下共享版本庫,查看是否存在z.m。如果存在z.m,那么也就是所git rm是不會影響版本庫的中已經(jīng)存在的文件。
在AWorker的工作空間中,此時AWork的暫存區(qū)存在兩個文件,將其提交到本地版本庫中進(jìn)行管理。并同步到共享版本庫中。接著我們刪除git rm刪除其中b.m這個文件,由于此時暫存區(qū)沒有存在該文件,工作區(qū)有所以git rm不會提示 錯誤。這時候假設(shè)git rm對本地版本庫有影響,則我們再本地版本庫同步到共享版本庫,如果有影響,則此時的共享版本庫就不會存在該文件。于是我們又在BWorker中進(jìn)行g(shù)it pull指令來驗證。
AWorker
- suweipeng:sharedRep sixleaves$ git status -s
- A b.m
- A c.m
- suweipeng:sharedRep sixleaves$ git commit -m "添加b、c文件"
- [master b8e8cc9] 添加b、c文件
- 2 files changed, 0 insertions(+), 0 deletions(-)
- create mode 100644 b.m
- create mode 100644 c.m
- suweipeng:sharedRep sixleaves$ git push origin master
- Counting objects: 2, done.
- Delta compression using up to 4 threads.
- Compressing objects: 100% (2/2), done.
- Writing objects: 100% (2/2), 288 bytes | 0 bytes/s, done.
- Total 2 (delta 0), reused 0 (delta 0)
- To /Users/sixleaves/SWPTest/AWorker/../sharedRep/
- 97b6aa0..b8e8cc9 master -> master
- suweipeng:sharedRep sixleaves$ git rm b.m
- rm 'b.m'
- suweipeng:sharedRep sixleaves$ ls -l
- total 0
- -rw-r--r-- 1 sixleaves staff 0 7 21 17:27 c.m
- -rw-r--r-- 1 sixleaves staff 0 7 21 17:16 e.m
- suweipeng:sharedRep sixleaves$ git push origin master
- Everything up-to-date
- suweipeng:sharedRep sixleaves$
在BWorker中進(jìn)行g(shù)it pull指令,具體細(xì)節(jié)如下,可以看到BWorker工作區(qū)間在pull之后,存在b.m c.m e.m 所以我們可以得出結(jié)論git rm針對的只是工作區(qū)和暫存區(qū)域,當(dāng)兩個區(qū)域同時存在該文件的時候,進(jìn)行刪除 操作會出錯,我們需要詳細(xì)說明是不是要保存工作區(qū)的文件。
BWorker
- suweipeng:sharedRep sixleaves$ pwd
- /Users/sixleaves/SWPTest/BWorker/sharedRep
- suweipeng:sharedRep sixleaves$ git pull
- remote: Counting objects: 2, done.
- remote: Compressing objects: 100% (2/2), done.
- remote: Total 2 (delta 0), reused 0 (delta 0)
- Unpacking objects: 100% (2/2), done.
- From /Users/sixleaves/SWPTest/BWorker/../sharedRep
- 97b6aa0..b8e8cc9 master -> origin/master
- Updating 97b6aa0..b8e8cc9
- Fast-forward
- b.m | 0
- c.m | 0
- 2 files changed, 0 insertions(+), 0 deletions(-)
- create mode 100644 b.m
- create mode 100644 c.m
- suweipeng:sharedRep sixleaves$ ls -al
- total 8
- drwxr-xr-x 7 sixleaves staff 238 7 21 19:00 .
- drwxr-xr-x 3 sixleaves staff 102 7 21 17:18 ..
- drwxr-xr-x 15 sixleaves staff 510 7 21 19:00 .git
- -rw-r--r-- 1 sixleaves staff 836 7 21 17:18 .gitignore
- -rw-r--r-- 1 sixleaves staff 0 7 21 19:00 b.m
- -rw-r--r-- 1 sixleaves staff 0 7 21 19:00 c.m
- -rw-r--r-- 1 sixleaves staff 0 7 21 17:18 e.m
- suweipeng:sharedRep sixleaves$
最后分別針對這三個區(qū)域總結(jié),如下
- 針對工作區(qū)域的刪除: (如果文件是已經(jīng)交給git管理)這種情況,也必須同步版本庫,所以其實就是新方法的完整流程。如果是還沒提交給git管理,此時直接是用rm刪除即可。
- 針對暫存區(qū)域的刪除(而不刪除工作區(qū)): git rm <文件名> --cached
- 針對分支(版本庫)的刪除: 所謂的針對分支的刪除其實是沒必要的,因為每次提交的時候都會產(chǎn)生一個新版本。 如果出錯,我們可以直接使用git reset 進(jìn)行版本回退。所以如果你想做到針對版本庫的刪除效果,就只能使用版本回退。其它:
- 針對同時刪除工作區(qū)域和暫存區(qū)域的: git rm -f <file>
三、改
需要明確的一點是,當(dāng)你把文件交給git管理的時候,一旦你一修改一個文件(無論是換行還是空格都算),git就會追蹤到此時文件發(fā)生了變化。也就是所此時你必須再每次改完后,add到暫存區(qū),然后進(jìn)行commit??傄氖乱。?!每次修改完都必須add到暫存區(qū),保持暫存區(qū)該份文件的最新狀態(tài),這樣在commit之后,版本庫中保存的才是我們修改的文件!??!。覺得改也就這個注意點,是比較簡單的,所以具體的操作,可以常見官方文檔。
所以我把改分為兩種,一種是針對自己編程任務(wù)進(jìn)行編碼的"改"。一種是你需要從共享版本庫種更新下來最新 代碼的"改"。前者不在復(fù)述,后者簡單說一下。
在git中,我們可以使用git pull來更新一個已經(jīng)和共享版本庫關(guān)聯(lián)起來的本地版本庫,和相應(yīng)的工作區(qū)間。嚴(yán)謹(jǐn)點來說git pull = git fetch + merge。即一步是取數(shù)據(jù),一步是合并數(shù)據(jù),所以git pull可能會在合并時候產(chǎn)生數(shù)據(jù)沖突,后面會詳細(xì)介紹產(chǎn)生沖突的解決方案。這邊暫且掌握簡單的更新操作即可。
四、查
所謂的查有兩種,一種是查看版本記錄。一種是查看暫緩區(qū)和工作區(qū)的中的文件差別,以此來判斷時候要更新暫緩區(qū)的文件。
查看版本記錄: 就是進(jìn)行版本的查看(git log、git reflog)。前者是當(dāng)前存在的可追溯的版本,后者是歷史記錄中存在的版本。一般如果我們要回退版本的話,使用git reflog會更方便,應(yīng)該它可以查看到所有的版本的歷史記錄。
suweipeng:sharedRep sixleaves$ git log
commit 7f0486eda9bfcaaae56ec8382be580d446d854d3
Author: sixleaves <18649772243@163.com>
Date: Tue Jul 21 20:09:53 2015 +0800
添加了main.m
commit 97b6aa090388d0fc1b73eaeb615d86faf83807f7
Author: sixleaves <18649772243@163.com>
Date: Tue Jul 21 17:20:05 2015 +0800
刪除文件b
commit ef1169ef156294cfd68ac10b568781ca9ab4a15c
Author: sixleaves <18649772243@163.com>
Date: Tue Jul 21 17:16:46 2015 +0800
添加了e文件
commit 4eb100209198d1f5c7bf914bdf9776795f7753f1
Author: sixleaves <18649772243@163.com>
Date: Tue Jul 21 17:15:00 2015 +0800
添加了b文件
suweipeng:sharedRep sixleaves$ git reflog
7f0486e HEAD@{0}: commit: 添加了main.m
97b6aa0 HEAD@{1}: reset: moving to 97b6aa0
b8e8cc9 HEAD@{2}: commit: 添加b、c文件
97b6aa0 HEAD@{3}: commit: 刪除文件b
ef1169e HEAD@{4}: commit: 添加了e文件
4eb1002 HEAD@{5}: commit: 添加了b文件
a18151c HEAD@{6}: commit (initial): add gitignore file
suweipeng:sharedRep sixleaves$
查看工作區(qū)的文件狀態(tài):
git status 指令會去暫緩區(qū)中與工作區(qū)進(jìn)行比較,如果是暫緩區(qū)不存在,而工作區(qū)存在的,則會顯示兩個?,表示還沒提交給git進(jìn)行管理。此時可以執(zhí)行g(shù)it add命令將其提交到暫緩區(qū),此時暫緩區(qū)的文件狀態(tài),就變成了A,表示已經(jīng)加入暫緩區(qū),此時若在提交到本地版本庫,則git就可以管理該文件了,所以此時再使用git status查詢是沒有結(jié)果的,因為此時暫緩區(qū)已經(jīng)清空(在執(zhí)行g(shù)it commit后清空),而工作區(qū)中的文件已經(jīng)交給git管理,并且沒有發(fā)送變化。
suweipeng:sharedRep sixleaves$ git status -s ?? 1.m suweipeng:sharedRep sixleaves$ git add 1.m suweipeng:sharedRep sixleaves$ git status -s A 1.m suweipeng:sharedRep sixleaves$ vim 1.m suweipeng:sharedRep sixleaves$ git status -s A 1.m suweipeng:sharedRep sixleaves$ vim 1.m suweipeng:sharedRep sixleaves$ git status -s AM 1.m suweipeng:sharedRep sixleaves$ git add 1.m suweipeng:sharedRep sixleaves$ git status -s A 1.m suweipeng:sharedRep sixleaves$ git commit -m "添加1.m" [master f6378aa] 添加1.m 1 file changed, 1 insertion(+) create mode 100644 1.m suweipeng:sharedRep sixleaves$ git status -s suweipeng:sharedRep sixleaves$
如果修改了main.m文件,再進(jìn)行查看此時,第二欄目就變成了紅色的M,表示已經(jīng)修改工作區(qū)的文件,而此時git中版本庫里的文件和該文件不同,所以我們要將其同步,必須得先進(jìn)行g(shù)it add,此時文件狀態(tài)由工作區(qū)的M變成了暫緩區(qū)的M,并且變成綠色,而工作區(qū)的M會消失。
此時可以使用git diff查看工作區(qū)和暫緩區(qū)中具體的變化。
suweipeng:sharedRep sixleaves$ touch 2.m suweipeng:sharedRep sixleaves$ git add 2.m suweipeng:sharedRep sixleaves$ vim 2.m suweipeng:sharedRep sixleaves$ git status -s AM 2.m suweipeng:sharedRep sixleaves$ git diff diff --git a/2.m b/2.m index e69de29..abe4786 100644 --- a/2.m +++ b/2.m @@ -0,0 +1 @@ +ddddd suweipeng:sharedRep sixleaves$
上圖的意思是在變動后的文件種,新增加了一行ddddd
查總結(jié)
1.查詢版本記錄使用git log或者git reflog。
前者記錄的是當(dāng)前版本號的迭代順序(可能會因為回退而覆蓋)。
后者記錄的是所有的歷史版本記錄。(后者包含前者)。
2.使用git status -s就可知道暫存區(qū)的數(shù)據(jù)是否是最新的,是否有文件沒有添加進(jìn)暫存取。
3.如果暫存區(qū)數(shù)據(jù)不是最新的,可以使用git diff必將查看變化的數(shù)據(jù)。
git diff中的格式說明
---: 表示變動前的文件(工作區(qū)文件)。
+++: 表示變動后的文件(暫存區(qū)文件)。
沒有減號或加號的行表示沒有變動的行。
-號的行表示第一個文件中刪除的行,+號表示第二個文件中增加的行。
@@ -1,7 +1,7 @@這句話中的減號和加號分別表示第一個文件,和第二個文件。
文件狀態(tài)總結(jié):
補(bǔ)充: 暫存區(qū)的D和工作區(qū)的D的區(qū)別:
1.第一欄目的,也就是暫存區(qū)顯示的是綠色的D,則表示該文件已經(jīng)從工作空間
刪除,刪除標(biāo)志提交到了暫存區(qū),此時若再執(zhí)行g(shù)it commit就會將其從版本庫種刪除。
2.而紅色的D,再第二欄目,表示工作空間上的文件已經(jīng)被刪除,但是刪除操作還沒有提交到暫存區(qū)。
只有提交到了暫存區(qū)才有機(jī)會,將其在git種刪除。
顏色的紅綠只是為了讓你能迅速的分辨出暫存區(qū)和工作區(qū)的文件狀態(tài)的修改狀態(tài)。
這是第一篇GIT簡要入門的文章,如果有疑惑的地方,可以給我留言,大家相互交流。后面的文章還會再介紹 git種的沖突解決等問題。