前言
由于最近公司開始停止使用SVN,開始轉向GIT了,利用周末的時間學習實踐了下,分享給大家~
什麼是Git?
Git是一個開源的,免費的,分布式的版本管理系統。
我們知道CVS,SVN,也是常用的版本管理系統,他們之間最大的差別在于“分布式”。
另外,我們還需要注意GIT的一個特性:everything is local
要知道CSV,SVN,我們本地安裝的是用戶端,必須通過網絡連接配接到伺服器,才能取得代碼,如果斷網,那就OVER了。但是GIT則不同,它所有的東西都在本地存放,也就是說在斷網的情況下,可以離線運作。
GIT是大名鼎鼎的LINUX之父在2005年釋出,經過多年的沉澱,在開源社群使用率非常廣泛,可以說花一點時間學習GIT,将會給我們帶來巨大的幫助,那麼下面我們就開始吧!
來,安裝Git!
目前GIT的最新版本是2.6.3,GIT雖然有GUI,但是通過指令行的方式才是掌握GIT的最佳方式,是以我将使用LINUX GIT來進行實踐。
為了避免安裝GIT的過程中,涉及到諸多依賴,我已經在LINUX做好YUM源,并安裝了一些常用工具包,如下所示。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<code>[root@localhost ~]</code><code># yum grouplist install</code>
<code>Loading </code><code>"rhnplugin"</code> <code>plugin</code>
<code>Loading </code><code>"security"</code> <code>plugin</code>
<code>Loading </code><code>"installonlyn"</code> <code>plugin</code>
<code>This system is not registered with RHN.</code>
<code>RHN support will be disabled.</code>
<code>Setting up Group Process</code>
<code>Setting up repositories</code>
<code>Installed Groups:</code>
<code> </code><code>Office</code><code>/Productivity</code>
<code> </code><code>MySQL Database</code>
<code> </code><code>Development Libraries</code>
<code> </code><code>System Tools</code>
<code> </code><code>GNOME Desktop Environment</code>
<code> </code><code>Network Servers</code>
<code> </code><code>X Window System</code>
<code> </code><code>Printing Support</code>
<code> </code><code>Mail Server</code>
<code> </code><code>Server Configuration Tools</code>
<code> </code><code>Administration Tools</code>
<code> </code><code>Graphical Internet</code>
<code>tar</code> <code>-xvf git-2.6.3.</code><code>tar</code><code>.gz </code>
<code>.</code><code>/configure</code> <code>--prefix=</code><code>/usr/local</code>
<code>make</code>
<code>make</code> <code>install</code>
安裝完畢後,我們可以看到:
<code>[root@localhost bin]</code><code># pwd</code>
<code>/usr/local/bin</code>
<code>[root@localhost bin]</code><code># ll</code>
<code>total 23260</code>
<code>-rwxr-xr-x 116 root root 5817589 Dec 4 18:29 git</code>
<code>-rwxr-xr-x 2 root root 162431 Dec 4 18:29 git-cvsserver</code>
<code>-rwxr-xr-x 1 root root 348074 Dec 4 18:29 gitk</code>
<code>-rwxr-xr-x 116 root root 5817589 Dec 4 18:29 git-receive-pack</code>
<code>-rwxr-xr-x 2 root root 2763905 Dec 4 18:29 git-shell</code>
<code>-rwxr-xr-x 116 root root 5817589 Dec 4 18:29 git-upload-archive</code>
<code>-rwxr-xr-x 2 root root 2998205 Dec 4 18:29 git-upload-pack</code>
<code>[root@localhost /]</code><code># git --version</code>
<code>git version 2.6.3</code>
<code>[root@localhost /]</code><code>#</code>
要使用,就要先配置Git
配置GIT,很簡單!
<code>[root@localhost /]</code><code># git config --global user.name zhangfengzhe</code>
<code>[root@localhost /]</code><code># git config --global user.email [email protected]</code>
<code>[root@localhost /]</code><code># git config --global color.ui true</code>
之是以要配置USERNAME/EMAIL就是說對代碼的改動進行唯一PERSON标示
為了讓GIT有些色彩,我們對顔色進行了配置。
列出配置:
<code>[root@localhost /]</code><code># git config --list</code>
<code>user.name=zhangfengzhe</code>
<code>[email protected]</code>
<code>color.ui=</code><code>true</code>
說白了,我們利用git config指令進行的配置操作,實際上是對檔案的改動,那麼在哪裡呢?
<code>[root@localhost ~]</code><code># pwd</code>
<code>/root</code>
<code>[root@localhost ~]</code><code># cat .gitconfig </code>
<code>[user]</code>
<code>name = zhangfengzhe</code>
<code>email = [email protected]</code>
<code>[color]</code>
<code>ui = </code><code>true</code>
配置檔案就在YOUR HOME目錄下,在上面,我們看到了典型的LINUX分段式配置檔案。
是以,我們可以直接修改.gitconfig就可以完成配置。
建立Git Repo
Repo即repository的簡寫,就是倉庫,代碼庫的意思,有點MAVEN的感覺,呵呵。
先看下面的例子:
<code>[root@localhost myproject]</code><code># pwd</code>
<code>/myproject</code>
<code>[root@localhost myproject]</code><code># ll</code>
<code>total 16</code>
<code>drwxr-xr-x 2 root root 4096 Dec 4 19:41 hadoop</code>
<code>drwxr-xr-x 2 root root 4096 Dec 4 19:41 storm</code>
<code>[root@localhost myproject]</code><code># cd hadoop</code>
<code>[root@localhost hadoop]</code><code># git init </code>
<code>Initialized empty Git repository </code><code>in</code> <code>/myproject/hadoop/</code><code>.git/</code>
<code>[root@localhost hadoop]</code><code># ll -al</code>
<code>total 24</code>
<code>drwxr-xr-x 3 root root 4096 Dec 4 19:41 .</code>
<code>drwxr-xr-x 4 root root 4096 Dec 4 19:41 ..</code>
<code>drwxr-xr-x 7 root root 4096 Dec 4 19:41 .git</code>
<code>[root@localhost hadoop]</code><code>#</code>
我們經常使用的SVN,會在本地目錄下生成.svn,GIT也類似。
通過git init指令,我們可以建立一個empty repository!
事實上,對于一般的開發人員而言,我們并不需要create empty repo,我們開始需要做的其實是克隆一個repo。
比如,最近阿裡巴巴送出給apache的JSTORM項目,我們來看看如何CLONE它。
首先,我們通過https://github.com/alibaba/jstorm COPY HTTPS
<code>[root@localhost myproject]</code><code># git clone https://github.com/alibaba/jstorm.git</code>
下載下傳完成後,會出現jstorm目錄,以及在目錄中存在.git隐藏目錄。
通過git clone指令,我們可以克隆一個repo到本地。
添加以及送出
<code>[root@localhost hadoop]</code><code># pwd</code>
<code>/myproject/hadoop</code>
<code>[root@localhost hadoop]</code><code># ll -A</code>
<code>total 12</code>
<code>drwxr-xr-x 7 root root 4096 Dec 5 03:35 .git</code>
<code>-rw-r--r-- 1 root root 0 Dec 5 03:36 HelloWorld.java</code>
<code>[root@localhost hadoop]</code><code># git status</code>
<code>On branch master</code>
<code>Initial commit</code>
<code>Untracked files:</code>
<code> </code><code>(use </code><code>"git add <file>..."</code> <code>to include </code><code>in</code> <code>what will be committed)</code>
<code>HelloWorld.java</code>
<code>nothing added to commit but untracked files present (use </code><code>"git add"</code> <code>to track)</code>
首先注意到.git目錄,說明已經進行了init。
通過git status指令,我們可以檢視repo的狀态資訊。
注意到Untracked files清單,是在說明HelloWorld.java沒有注冊到Git,是以我們需要給HelloWorld.java“上戶口”。
<code>[root@localhost hadoop]</code><code># git add HelloWorld.java </code>
<code>You have new mail </code><code>in</code> <code>/var/spool/mail/root</code>
<code>Changes to be committed:</code>
<code> </code><code>(use </code><code>"git rm --cached <file>..."</code> <code>to unstage)</code>
<code>new </code><code>file</code><code>: HelloWorld.java</code>
通過git add指令,實際上是将file推送到Staged Area。
上面再次檢視狀态,提示需要送出,HelloWorld.java is new file。
下面我們來送出:
<code>[root@localhost hadoop]</code><code># git commit -m </code>
<code>"this is the first commit for me and only one file : HelloWorld.java"</code>
<code>[master (root-commit) 815b551] this is the first commit </code><code>for</code> <code>me and only one </code><code>file</code> <code>: </code>
<code> </code><code>1 </code><code>file</code> <code>changed, 0 insertions(+), 0 deletions(-)</code>
<code> </code><code>create mode 100644 HelloWorld.java</code>
注意了,同SVN一樣,GIT每次送出也會有MESSAGE資訊,直接通過-m選項進行指定。
通過git commit -m指令來進行送出到HISTORY區域。
那麼送出後的狀态,又是什麼呢?
<code>nothing to commit, working directory clean</code>
送出後,就幹淨了,你懂的!
那麼通過上面的一些實踐,其實我們可以得到一些初步的認識:
如果有FILE并沒有ADD到REPO的話,GIT會提示我們Untracked files
如果git add FILE但沒有COMMIT的話,GIT會提示我們new files
如果COMMIT後,GIT會提示我們nothing to commit, working directory clean
初步認識Git的核心原理圖
在上面的例子中,已經提及到working directory,staged area以及history。
其實這三個區域就是GIT的核心,了解他們之間的關系,是掌握GIT的基礎。
<a href="http://s3.51cto.com/wyfs02/M01/77/24/wKiom1Zjypag-E2gAAAmWV2aDWk570.png" target="_blank"></a>
我們工作在working dir區域,對檔案進行改動、添加、删除的操作,而這些改動首先應該先到達Staged area區域,然後通過送出在History區域形成最新version。
既然有3個區域,那麼自然,對于一個檔案,應該有2部分的狀态資訊:
第一,對于working dir和staged area而言,有什麼差別?
第二,對于staged area和history而言,有什麼差別?
事實上,對于git status -s會看到2列狀态标志位來表達這2部分狀态資訊。【後文會有介紹】
對于History有一個HEAD的概念:
<a href="http://s3.51cto.com/wyfs02/M01/77/23/wKioL1ZjzXmhK5QwAAAdO6jE4M8858.png" target="_blank"></a>
說起head,自然會想到頭部指針,确實是有這麼個意思。
關于HEAD,也會在指令中予以展現!
Git狀态标志位剖析
22
23
24
25
<code>[root@localhost hadoop]</code><code># ll -a</code>
<code>total 28</code>
<code>drwxr-xr-x 3 root root 4096 Dec 5 03:36 .</code>
<code>drwxr-xr-x 4 root root 4096 Dec 4 20:08 ..</code>
<code>drwxr-xr-x 8 root root 4096 Dec 5 04:01 .git</code>
<code>[root@localhost hadoop]</code><code># vi HelloWorld.java </code>
<code>Changes not staged </code><code>for</code> <code>commit:</code>
<code> </code><code>(use </code><code>"git add <file>..."</code> <code>to update what will be committed)</code>
<code> </code><code>(use </code><code>"git checkout -- <file>..."</code> <code>to discard changes </code><code>in</code> <code>working directory)</code>
<code>modified: HelloWorld.java</code>
<code>no changes added to commit (use </code><code>"git add"</code> <code>and</code><code>/or</code> <code>"git commit -a"</code><code>)</code>
<code>[root@localhost hadoop]</code><code># </code>
<code>[root@localhost hadoop]</code><code># git status -s</code>
<code> </code><code>M HelloWorld.java</code>
<code>?? love.txt</code>
<code>[root@localhost hadoop]</code><code># git add love.txt </code>
<code>A love.txt</code>
注意git status -s會簡明扼要的列出狀态資訊,就列出2個标志位!
仔細觀察上面的資訊,love.txt的A,和HelloWorld.java的M并沒有對齊,這是為什麼呢?
實際上,一個檔案的git status有2列,第一列代表staging區域資訊;第二列代表woring dir資訊。也即是說,HelloWorld.java在working dir中修改了,但沒有送出到staging area 。love.txt在staging中添加了,但是在work dir中,沒有改變。
繼續,看下面的操作,來看status的變化:
<code>[root@localhost hadoop]</code><code># git commit -m "love.txt" love.txt </code>
<code>[master f43f700] love.txt</code>
<code> </code><code>create mode 100644 love.txt</code>
<code>M HelloWorld.java</code>
如何檢視檔案差别?
其實當我們利用git status發現檔案有差異時,很顯然,我們需要檢視檔案的差别在哪裡!
看差異,看什麼和什麼的差異呢?
是working dirPK staged area ?
是staged area PK history ?
還是working dir PK history ?
git都可以滿足你!
working dir PK staged area:
<code>MM HelloWorld.java</code>
<code>[root@localhost hadoop]</code><code># git diff</code>
<code>diff</code> <code>--git a</code><code>/HelloWorld</code><code>.java b</code><code>/HelloWorld</code><code>.java</code>
<code>index 2e3abaa..5e6618e 100644</code>
<code>--- a</code><code>/HelloWorld</code><code>.java</code>
<code>+++ b</code><code>/HelloWorld</code><code>.java</code>
<code>@@ -1 +1,2 @@</code>
<code> </code><code>this is not a java </code><code>file</code><code>,haha</code>
<code>+abc</code>
staged area PK history:
<code>[root@localhost hadoop]</code><code># git diff --staged</code>
working dir PK history:
<code>[root@localhost hadoop]</code><code># git diff HEAD</code>
<code>index e69de29..5e6618e 100644</code>
<code>@@ -0,0 +1,2 @@</code>
<code>+this is not a java </code><code>file</code><code>,haha</code>
git diff/git diff --staged/git diff HEAD 檢視檔案差異。
git diff列出的資訊也是蠻多的,當然可以像git status -s那樣有簡化版本:
git diff --stat
<code>[root@localhost hadoop]</code><code># git diff --stat HEAD</code>
<code> </code><code>HelloWorld.java | 2 ++</code>
<code> </code><code>1 </code><code>file</code> <code>changed, 2 insertions(+)</code>
Git有後悔藥可以吃!
有一句老話叫 人非聖賢,孰能無過。
那麼在GIT中,如何撤銷錯誤呢?
既然是撤銷錯誤,那麼是從哪裡撤銷到哪裡呢?
在實際開發中,我們經常遇到的是這樣的情況,我們對working dir的FILE進行了修改,然後ADD到了staged area區域,突然發現有問題,想撤銷這些改動。
SO EASY,我們隻需要将history的内容還原即可。
<code>total 20</code>
<code>drwxr-xr-x 8 root root 4096 Dec 5 06:20 .git</code>
<code>-rw-r--r-- 1 root root 33 Dec 5 04:56 HelloWorld.java</code>
<code>-rw-r--r-- 1 root root 0 Dec 5 04:06 love.txt</code>
<code>[root@localhost hadoop]</code><code># git reset HelloWorld.java </code>
<code>Unstaged changes after reset:</code>
可以發現通過git reset指令将staging area還原至history版本,需要注意是的不影響WORK DIR的改動。其實,就是一個标志位的還原操作而已。
那如果我們想把已經修改的WORK DIR的檔案也恢複到原來的狀态,怎麼辦呢?
同上,我們隻需要将STAGING AREA的檔案覆寫WORK DIR即可,利用git checkout指令。
<code>[root@localhost hadoop]</code><code># more HelloWorld.java </code>
<code>this is not a java </code><code>file</code><code>,haha</code>
<code>abc</code>
<code>woyaogaini</code>
<code>[root@localhost hadoop]</code><code># git checkout HelloWorld.java </code>
注意,git提供了一個簡化指令git checkout HEAD,它等價于下面的操作:
git checkout HEAD = git reset + git checkout
<code>drwxr-xr-x 8 root root 4096 Dec 5 06:34 .git</code>
<code>-rw-r--r-- 1 root root 33 Dec 5 06:26 HelloWorld.java</code>
<code>-rw-r--r-- 1 root root 11 Dec 5 06:34 love.txt</code>
<code>[root@localhost hadoop]</code><code># cat love.txt </code>
<code>i love you</code>
<code>[root@localhost hadoop]</code><code># vi love.txt </code>
<code>do</code> <code>you love me</code>
<code> </code><code>M love.txt</code>
<code>M love.txt</code>
<code>[root@localhost hadoop]</code><code># git checkout HEAD love.txt </code>
移除及重命名檔案
<code>[root@localhost hadoop]</code><code># ll</code>
<code>-rw-r--r-- 1 root root 33 Dec 5 06:26 HelloWorld.java</code>
<code>-rw-r--r-- 1 root root 31 Dec 5 06:42 love.txt</code>
<code>[root@localhost hadoop]</code><code># touch delete.log</code>
<code>[root@localhost hadoop]</code><code># git add delete.log </code>
<code>[root@localhost hadoop]</code><code># git commit -m "commit"</code>
<code>[master 0eccf50] commit</code>
<code> </code><code>create mode 100644 delete.log</code>
<code>[root@localhost hadoop]</code><code># git rm delete.log </code>
<code>rm</code> <code>'delete.log'</code>
<code>D delete.log</code>
<code>drwxr-xr-x 8 root root 4096 Dec 5 06:53 .git</code>
<code>-rw-r--r-- 1 root root 31 Dec 5 06:42 love.txt</code>
看得出,利用git rm進行删除後,需要COMMIT!
<code>[master 415380f] commit</code>
<code> </code><code>delete mode 100644 delete.log</code>
預設情況下,git rm會把working dir以及staged area的檔案進行删除。
如果我們僅僅想從STAGING AREA中移除FILE,但是保留WORK DIR中的檔案的話,可以這樣操作:
26
27
28
29
30
31
32
33
34
35
36
37
<code>drwxr-xr-x 3 root root 4096 Dec 5 06:53 hadoop</code>
<code>[root@localhost myproject]</code><code># cd hadoop/</code>
<code>?? delete.log</code>
<code>[master 48b1bad] commit</code>
<code>-rw-r--r-- 1 root root 0 Dec 5 17:26 delete.log</code>
<code>[root@localhost hadoop]</code><code># git rm -cached delete.log </code>
<code>error: did you mean `--cached` (with two dashes ?)</code>
<code>[root@localhost hadoop]</code><code># git rm --cached delete.log </code>
git rm --cached的方式:
從最後的狀态可以看到,delete.log仍然在WORK DIR中,但是并沒有ADD到STAGING AREA區域,同時STAGING AREA區域的delete.log被删除。
重命名操作:
<code>-rw-r--r-- 1 root root 0 Dec 5 17:26 delete.log</code>
<code>drwxr-xr-x 8 root root 4096 Dec 5 17:31 .git</code>
<code>[root@localhost hadoop]</code><code># git mv delete.log delete.me</code>
<code>R delete.log -> delete.me</code>
<code>[root@localhost hadoop]</code><code># git commit -m "rename file"</code>
<code>[master 6f26a1b] rename </code><code>file</code>
<code> </code><code>rename delete.log => delete.me (100%)</code>
通過git mv即可重命名,需要COMMIT!
保留現場:暫存工作區
什麼叫暫存工作區呢?
比如在實際開發中,我們經常遇到這樣的場景:
我們在WORK DIR中進行修改,有時我們還add了一部分到staging area,此時突然發現代碼存在BUG,需要修改。當然,我們可以利用git reset+git checkout的方式迅速得到clean code,然後開始修改,但是這樣我們就丢失了在work dir中的所有改動。
git為我們提供了git stash指令,幫助我們儲存現有的工作區,于此同時work dir /staging area的代碼恢複到HISTORY水準。一旦我們改動完畢,COMMIT到HISTORY後,利用git stash pop進行還原原來的工作區!
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
<code>-rw-r--r-- 1 root root 7 Dec 5 18:07 delete.me</code>
<code>-rw-r--r-- 1 root root 0 Dec 5 17:40 t1.txt</code>
<code>[root@localhost hadoop]</code><code># vi delete.me </code>
<code>[root@localhost hadoop]</code><code># cat delete.me </code>
<code>hello , world</code>
<code> </code><code>M delete.me</code>
<code>[root@localhost hadoop]</code><code># git add delete.me </code>
<code>M delete.me</code>
<code>MM delete.me</code>
<code>[root@localhost hadoop]</code><code># git stash</code>
<code>Saved working directory and index state WIP on master: b714e98 commit</code>
<code>HEAD is now at b714e98 commit</code>
<code>-rw-r--r-- 1 root root 7 Dec 5 18:11 delete.me</code>
<code>[root@localhost hadoop]</code><code># git commit -m "bug fix"</code>
<code>[master 54cf628] bug fix</code>
<code> </code><code>1 </code><code>file</code> <code>changed, 1 insertion(+)</code>
<code>[root@localhost hadoop]</code><code># git stash pop</code>
<code>modified: delete.me</code>
<code>Dropped refs</code><code>/stash</code><code>@{0} (963a3ab0075e09ee63117b6b8b606780aca5d020)</code>
<code>-rw-r--r-- 1 root root 26 Dec 5 18:12 delete.me</code>
<code>-rw-r--r-- 1 root root 37 Dec 5 18:11 HelloWorld.java</code>
<code>hello , git</code>
總結
<a href="http://s4.51cto.com/wyfs02/M02/77/25/wKiom1Zj2NThbPFIAABNqU95x5E353.png" target="_blank"></a>
到這裡就結束了,下篇部落格将會繼續介紹GIT的一些深入知識,比如COMMIT對象、TREE-ISH表達式、建立/删除/合并分支等。
本文轉自zfz_linux_boy 51CTO部落格,原文連結:http://blog.51cto.com/zhangfengzhe/1720049,如需轉載請自行聯系原作者