程序員如何輕松入門Git-讀完這一篇,獨(dú)立創(chuàng)建自己的第一個(gè)項(xiàng)目(程序員 git)
如果你想試著用一下Git的話,那么我們馬上就可以開始了。本章將會(huì)帶領(lǐng)你創(chuàng)建自己的第一個(gè)項(xiàng)目。我們會(huì)為你演示那些用于提交修改版本、查看歷史和與其他開發(fā)者交換版本的命令。
1.1 準(zhǔn)備Git環(huán)境
首先,我們需要安裝好git。你可以在Git的官網(wǎng)上找到你所需要的一切:
http://git-scm.com/download
Git是一個(gè)高可配置軟件。首先,我們可以宣布用config命令配置一下用戶名和用戶郵箱:[1]
> git config –global user.email “hans@mustermann.de”
1.2 第一個(gè)Git項(xiàng)目
在這里,我們建議你最好能為接下來的Git測(cè)試單獨(dú)開辟一個(gè)項(xiàng)目。總之應(yīng)先從一個(gè)簡(jiǎn)單的小項(xiàng)目開始。在我們這個(gè)小小的示例項(xiàng)目中,first-steps目錄下只有兩個(gè)文本文件,如圖1.1所示。
圖1.1 我們的示例項(xiàng)目
在開始擺弄這個(gè)玩具項(xiàng)目之前,我們建議你最好先做一個(gè)備份!盡管在Git中,想要造成永久性的刪除或破壞也不是件容易的事情,而且每當(dāng)你要做某些“危險(xiǎn)”動(dòng)作的時(shí)候,Git通常也會(huì)發(fā)出相應(yīng)的警告消息。但是,有備無患總是好的。
1.2.1 創(chuàng)建版本庫(kù)
現(xiàn)在,我們首先需要?jiǎng)?chuàng)建一個(gè)版本庫(kù),用于存儲(chǔ)該項(xiàng)目本身及其歷史。為此,我們需要在該項(xiàng)目目錄中使用init命令。對(duì)于一個(gè)帶版本庫(kù)的項(xiàng)目目錄,我們通常稱之為工作區(qū)。
> cd /projects/first-steps > git initInitialized empty Git repository in /projects/first-steps/.git/
init命令會(huì)在上述目錄中創(chuàng)建一個(gè)名為.git的隱藏目錄,并在其中創(chuàng)建一個(gè)版本庫(kù)。但請(qǐng)注意,該目錄在Windows資源管理器或Mac Finder中可能是不可見的。
圖1.2 本地版本庫(kù)所在的目錄
1.2.2 首次提交
接下來,我們需要將foo.txt和bar.txt這兩個(gè)文件添加到版本庫(kù)中去。在Git中,我們通常將項(xiàng)目的一個(gè)版本稱之為一次提交,但這要分兩個(gè)步驟來實(shí)現(xiàn)。第一步,我們要先用add命令來確定哪些文件應(yīng)被包含在下次提交中。第二步,再用commit命令將修改傳送到版本庫(kù)中,并賦予該提交一個(gè)散列值以便標(biāo)識(shí)這次新提交。在這里,我們的散列值為2f43cd0,但可能會(huì)有所不同,因?yàn)樵撝等Q于文件內(nèi)容。
> git add foo.txt bar.txt > git commit –message “Sample project imported.” master (root-commit) 2f43cd0] Sample project imported.2 files changed, 2 insertions( ), 0 deletions(-) create mode 100644 bar.txt create mode 100644 foo.txt
1.2.3 檢查狀態(tài)
現(xiàn)在,我們來修改一下foo.txt文件的內(nèi)容,先刪除bar.txt文件,再添加一個(gè)名為bar.html的新文件。然后,status命令就會(huì)顯示出該項(xiàng)目自上次提交以來所發(fā)生的所有修改。請(qǐng)注意,新文件bar.html在這里被標(biāo)示成了未跟蹤狀態(tài),這是因?yàn)槲覀冞€沒有用add命令將其注冊(cè)到版本庫(kù)。
> git status # On branch master # Changed but not updated: # (use “git add/rm <file>…” to update what will be committed) # (use “git checkout — <file>…” to discard changes in # working directory) # # deleted: bar.txt # modified: foo.txt # # Untracked files: # (use “git add <file>…” to include in what will be committed) # # bar.htmlno changes added to commit (use “git add” and/or “git commit -a”)
如果我們還想看到更多細(xì)節(jié)性的內(nèi)容,也可以通過diff命令來顯示其每個(gè)被修改的行。當(dāng)然。有很多人可能會(huì)覺得diff的輸出是個(gè)非常難讀的東西。幸運(yùn)的是,在這一領(lǐng)域,我們有許多工具和開發(fā)環(huán)境可用,它們可以將這一切顯示得更為清晰(見圖1.3)。
圖1.3 圖形工具(kdiff3)中的Diff報(bào)告
> git diff foo.txt diff –git a/foo.txt b/foo.txt index 1910281..090387f 100644 — a/foo.txt b/foo.txt @@ -1 1 @@ -foo No newline at end of file foo foo No newline at end of file
1.2.4 提交修改
接下來,所有的修改都必須要先被歸檔成一次新的提交。我們要對(duì)修改過的文件和新文件執(zhí)行add命令,并對(duì)要?jiǎng)h除的文件使用rm命令。
> git add foo.txt bar.html > git rm bar.txt rm ‘bar.txt’
現(xiàn)在再次調(diào)用status命令,我們會(huì)看到所有的修改已經(jīng)被納入了下一次提交中。
> git status # On branch master # Changes to be committed: # (use “git reset HEAD <file>…” to unstage) # # new file: bar.html # deleted: bar.txt # modified: foo.txt #
然后用commit命令提交這些修改。
> git commit –message “Some changes.” [master 7ac0f38] Some changes. 3 files changed, 2 insertions( ), 2 deletions(-) create mode 100644 bar.html delete mode 100644 bar.txt
1.2.5 顯示歷史
log命令可用來顯示項(xiàng)目的歷史,所有提交都會(huì)按時(shí)間順序被降序排列出來。
> git logcommit 7ac0f38f575a60940ec93c98de11966d784e9e4f Author: Rene Preissel <rp@eToSquare.de> Date: Thu Dec 2 09:52:25 2010 0100 Some changes. commit 2f43cd047baadc1b52a8367b7cad2cb63bca05b7 Author: Rene Preissel <rp@eToSquare.de> Date: Thu Dec 2 09:44:24 2010 0100 Sample project imported.
1.3 Git的協(xié)作功能
現(xiàn)在,我們已經(jīng)有了一個(gè)存放項(xiàng)目文件的工作區(qū),以及一個(gè)存放項(xiàng)目歷史的版本庫(kù)。在一個(gè)像CVS和Subversion這樣傳統(tǒng)的集中式版本系統(tǒng)中,盡管每個(gè)開發(fā)者也都有屬于他/她自己的工作區(qū),但所有人都共享了一個(gè)通用的版本庫(kù)。而在Git中,每個(gè)開發(fā)者擁有的是一個(gè)屬于他/她自己的、自帶獨(dú)立版本庫(kù)的工作區(qū),因此這已經(jīng)是一個(gè)不依賴于中央服務(wù)器的、完整的版本控制系統(tǒng)了。開發(fā)者們可以通過交換各自版本庫(kù)中的提交來實(shí)現(xiàn)項(xiàng)目合作。下面我們就來做個(gè)試驗(yàn),先創(chuàng)建一個(gè)新的工作區(qū),以便我們模擬第二位開發(fā)者的活動(dòng)。
1.3.1 克隆版本庫(kù)
我們的這位新開發(fā)者首先要有一個(gè)屬于他/她自己的版本庫(kù)副本(也稱為克隆體)。該副本中包含了所有的原始信息與整個(gè)項(xiàng)目的歷史信息。下面。我們用clone命令來創(chuàng)建一個(gè)克隆體。
> git clone /projects/first-steps /projects/first-steps-cloneCloning into first-steps-clone… done.
現(xiàn)在,該項(xiàng)目結(jié)構(gòu)如圖1.4所示。
圖1.4 樣例項(xiàng)目與它的克隆體
1.3.2 從另一版本庫(kù)中獲取修改
下面,我們來修改一下first-steps/foo.txt文件,并執(zhí)行以下操作來創(chuàng)建一次新提交。
> cd /projects/first-steps > git add foo.txt > git commit –message “A change in the original.”
現(xiàn)在,新的提交已經(jīng)被存入了我們?cè)瓉淼?strong>first-steps版本庫(kù)中,但其克隆版本庫(kù)(first-stepsclone)中依然缺失這次提交。為了讓你更好地理解這一情況,我們來看一下first-steps的日志。
> git log –oneline a662055 A change in the original. 7ac0f38 Some changes. 2f43cd0 Sample project imported.
在接下來的步驟中,我們?cè)賮硇薷目寺“姹編?kù)中的first-steps-clone/bar.html文件,并執(zhí)行以下操作。
> cd /projects/first-steps-clone > git add bar.html > git commit –message “A change in the clone.” > git log –oneline 1fcc06a A change in the clone. 7ac0f38 Some changes. 2f43cd0 Sample project imported.
現(xiàn)在,我們?cè)趦蓚€(gè)版本庫(kù)中各做了一次新的提交。接下來,我們要用pull命令將原版本庫(kù)中的新提交傳遞給它的克隆體。由于之前我們?cè)趧?chuàng)建克隆版本庫(kù)時(shí),原版本庫(kù)的路徑就已經(jīng)被存儲(chǔ)在了它的克隆體中,因此pull命令知道該從哪里去取回新的提交。
> cd /projects/first-steps-clone > git pull remote: Counting objects: 5, done. remote: Compressing objects: 100% (2/2), done. remote: Total 3 (delta 0), reused 0 (delta 0) Unpacking objects: 100% (3/3), done. From /projects/first-steps 7ac0f38..a662055 master -> origin/master Merge made by recursive. foo.txt | 2 -1 files changed, 1 insertions( ), 1 deletions(-)
如上所示,pull命令從原版本庫(kù)中取回了新的修改,將它們與克隆體中的本地修改進(jìn)行了對(duì)比,并在工作區(qū)中合并了兩邊的修改,創(chuàng)建了一次新的提交。這個(gè)過程就是所謂的合并(merge)。
請(qǐng)注意!合并過程在某些情況下可能會(huì)帶來沖突。一旦遇到了這種情況,Git中就不能進(jìn)行自動(dòng)化的版本合并了。在這種情況下,我們就必須要手動(dòng)清理一些文件,然后再確認(rèn)要提交哪些修改。
在拉回(pull)、合并(merge)的過程完成之后,我們可以用一個(gè)新的log命令來查看結(jié)果。這次是日志的圖形化版本。
> git log –graph 9e7d7b9 Merge branch ’master’ of /projects/first-steps * | | * a662055 A change in the original. * | 1fcc06a A change in the clone. |/ * 7ac0f38 Some changes. * 2f43cd0 Sample project imported.
這一次,歷史記錄不再是一條直線了。在上面的日志中,我們可以很清晰地看到并行開發(fā)的過程(即中間的兩次提交),以及之后用于合并分支的那次合并提交(即頂部的那次提交)。
2.3.3 從任意版本庫(kù)中取回修改
在沒有參數(shù)的情況下,pull命令只在克隆版本庫(kù)中能發(fā)揮作用,因?yàn)橹挥性摽寺◇w中有默認(rèn)的原版本庫(kù)的連接。當(dāng)我們執(zhí)行pull操作時(shí),也可以用參數(shù)來指定任意版本庫(kù)的路徑,以便從某一特定開發(fā)分支中提取相關(guān)修改。
現(xiàn)在,讓我們將克隆體中的修改pull到原版本庫(kù)中吧。
> cd /projects/first-steps > git pull /projects/first-steps-clone master remote: Counting objects: 8, done. remote: Compressing objects: 100% (4/4), done. remote: Total 5 (delta 0), reused 0 (delta 0) Unpacking objects: 100% (5/5), done. From /projects/first-steps-clone * branch master → FETCH_HEADUpdating a662055..9e7d7b9 Fast-forward bar.html | 2 – 1 files changed, 1 insertions( ), 1 deletions(-)
2.3.4 創(chuàng)建共享版本庫(kù)
除了可以用pull命令從其他版本庫(kù)中取回相關(guān)提交外,我們也可以用push命令將提交傳送給其他版本庫(kù)。只不過,push命令只適用于那些沒有開發(fā)者在上面開展具體工作的版本庫(kù)。最好的方法就是創(chuàng)建一個(gè)不帶工作區(qū)的版本庫(kù),我們稱之為裸版本庫(kù)(bare repository)。你可以使用clone命令的–bare選項(xiàng)來創(chuàng)建一個(gè)裸版本庫(kù)。裸版本庫(kù)通??杀挥脕沓洚?dāng)開發(fā)者們傳遞提交(使用push命令)的匯聚點(diǎn),以便其他人可以從中拉回他們所做的修改。下面我們來看一個(gè)裸版本庫(kù)(見圖1.5)。
圖1.5 裸版本庫(kù)(一個(gè)沒有工作區(qū)的版本庫(kù))
> git clone –bare /projects/first-steps /projects/first-steps-bare.gitCloning into bare repository first-steps-bare.git… done.
1.3.5 用push命令上載修改
為了演示push命令的使用,我們需要再次修改一下firststeps/foo.txt文件,并執(zhí)行以下操作來創(chuàng)建一次新的提交。
> cd /projects/first-steps > git add foo.txt > git commit –message “More changes in the original.”
接下來,我們就可以用push命令向共享版本庫(kù)傳送該提交了(見圖2.6)。該指令的參數(shù)要求與pull命令相同,我們需要指定目標(biāo)版本庫(kù)的路徑及其分支。
> git push /projects/first-steps-bare.git master Counting objects: 5, done. Delta compression using up to 2 threads. Compressing objects: 100% (2/2), done. Writing objects: 100% (3/3), 293 bytes, done. Total 3 (delta 0), reused 0 (delta 0) Unpacking objects: 100% (3/3), done. To /projects/first-steps-bare.git/ 9e7d7b9..7e7e589 master -> master
圖1.6 經(jīng)由共享版本庫(kù)來進(jìn)行版本共享
1.3.6 Pull命令:取回修改
現(xiàn)在,為了讓克隆版本庫(kù)也得到相應(yīng)的修改,我們需要在執(zhí)行pull命令時(shí)配置參數(shù)指向共享版本庫(kù)的路徑參數(shù)。
> cd /projects/first-steps-clone > git pull /projects/first-steps-bare.git master remote: Counting objects: 5, done. remote: Compressing objects: 100% (2/2), done. remote: Total 3 (delta 0), reused 0 (delta 0) Unpacking objects: 100% (3/3), done. From ../first-steps-bare * branch master -> FETCH_HEAD Updating 9e7d7b9..7e7e589 Fast-forward foo.txt | 2 – 1 files changed, 1 insertions( ), 1 deletions(-)
請(qǐng)注意!如果另一個(gè)開發(fā)者在我們之前已經(jīng)做過一次push操作,此次push命令就會(huì)被拒絕傳送提交。這時(shí)候,我們必須要先做一次pull操作,將其他人新上載的更新取回,并在本地合并。
小結(jié)
- 工作區(qū)與版本庫(kù):工作區(qū)是一個(gè)包含.git子目錄(內(nèi)含版本庫(kù))中的目錄。我們可以用init命令在當(dāng)前目錄中創(chuàng)建版本庫(kù)。
- 版本提交:一次版本提交通常定義了版本庫(kù)中所有文件的一個(gè)版本,它詳細(xì)說明了該版本是由何人在何時(shí)何地創(chuàng)建的。當(dāng)然,我們需要用add命令來確定哪些文件將被納入下一次提交,然后再用commit命令創(chuàng)建新的版本提交。
- 查看信息:通過status命令,我們可以查看哪些文件已被本地修改,以及哪些修改將被納入下次提交。另外,log命令可用來顯示提交歷史。diff命令可用來顯示兩個(gè)版本文件之間的差異。
- 克?。?/strong>對(duì)于用clone命令創(chuàng)建某一個(gè)版本庫(kù)的副本,我們稱之為該版本庫(kù)的克隆體。在一般情況下,每個(gè)開發(fā)者都會(huì)擁有整個(gè)項(xiàng)目版本庫(kù)的完整克隆體,他/她的工作區(qū)中將會(huì)包含完整的項(xiàng)目歷史。這使他們可以各自獨(dú)立開展工作,無需連接服務(wù)器。
- 推送與拉回:push與pull命令可用于在本地和遠(yuǎn)程版本庫(kù)之間共享版本提交。