天天看點

Hello , Git

前言

由于最近公司開始停止使用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 &lt;file&gt;..."</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 &lt;file&gt;..."</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 &lt;file&gt;..."</code> <code>to update what will be committed)</code>

<code>  </code><code>(use </code><code>"git checkout -- &lt;file&gt;..."</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 -&gt; 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 =&gt; 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,如需轉載請自行聯系原作者