Git新手入門教學 – part 1

–[WARNING]學習Git之前請先熟悉UNIX的Command Line指令–

身為一位整日埋頭苦幹實幹的工程師,可能都有遭遇過下述小智的經驗:

攻城獅日記: 20XX/9/1, 天氣晴
我今天花了5小時做了一個超強的功能!
剛寄信給客戶,他一定會很喜歡(然後多加點錢ㄎㄎ)

 

隔天…
攻城獅日記: 20XX/9/2, 大暴雨艮!
花了那麼多時間做的功能竟然不賞臉叫我改掉QQ 
他知道他放棄的是甚麼嗎!(我的摳摳QQ)

 

五天後…
攻城獅日記: 20XX/9/7, 偶陣雨
客戶老婆說她超愛這功能,說要加回來...
可以多加兩倍錢嗎,害我還要重寫一次扣Orz

 

反反覆覆…來來回回… 改回來又改回去、到底動了哪些程式碼已經分不清楚了…

此時工程師小智靈機一動──不然我把每次的修改都存成一個檔案呢?一再再的修改,從此出現了各種重新命名檔案名的腦力激盪:

Project_1、Project_1_Revised、Project_1_Small Revised、 Proejct_1_Final、Project_1_New Final、Project_1_Final2

(Final後還有Final、不斷增生的Final…)

況且,每次的更改都會同時影響到檔案包(Package)內多個檔案,比如為了一項功能改了Html、也得同時更改CSS、JavaScript和ReadMe等多個檔案。不然把每次修改都獨立存成一個檔案包好了!

喂這樣需要多大的硬碟儲存空間啊…。

小智冷汗直冒、正想不出解決辦法時,突然間客戶又寫了封信 (天啊噩夢般的客戶信)──安安你做太慢了、所以我另外請了一位工程師協助你。

現在除了自己的Code面臨改來改去的問題,小智和新來的小剛又出現共同協作的困難:「你跟我寫到同一個功能了!改到同一行程式碼了!」

 
G_______G

 

別擔心,今天要來教大家的神器,就是解決這一切問題的法寶:版本控制系統Git。

什麼是版本控制系統? Git?

版本控制系統 (Version Control System, VCS)是一套能記錄所有修改版本、讓使用者能隨時取回特定版本的系統。

大家都有玩過RPG嗎? 以小燕子決戰大富翁為例,當打村莊怪物打輸、被瘟疫纏上身、或純粹做錯決定時,玩家都會有志一同地:存檔、重來!遊戲狀態就會回到存檔時的那一刻。(若是人生也能這樣就好了QQ)

git01.jpg

Git就像遊戲的存檔功能,可以存取某時點的程式碼狀態

 

只要學會,無論寫錯任何程式碼、想重新找回被砍掉的功能、同時存多種不同版本 (交作業用/借人抄用)、多人協作時程式碼不會被意外蓋掉 (還能找戰犯)… VCS就是這麼好用的工具。

先來看一下版本控制系統的基本名詞:

Repository (數據庫/版本儲存庫)
用來保存歷史程式碼,和所有跑這個專案需要的東西,
包括所有原始碼、範例設計檔、文件等等。

Local (本地端)
通常是個人開發的電腦/機台。

Remote (遠端)
通常是一個共用的伺服器。

 

只要安裝版本控制系統、在專案中建立Repository後,這位遊戲存檔專家就進駐啦!

事實上,版本控制系統有好幾支不同的軟體(BitKeeper, SVN, CVS);眾多不同的版本控制系統中,最受歡迎的一支莫過於Git。

Git是由Linus Torvalds所發明。覺得很耳熟?因為Linus同時也是Linux作業系統之父。Linus至今仍在進行Linux Kernel──Linux作業系統的核心軟體──的維護工作。

Linux Kernel是一個龐大的計劃,包含了1500萬行左右的程式碼、每天有3500多行新程式碼被加進去,而每一次釋出新版本的Linux、至少有1000位以上的開發者。

天啊!不用你在心底驚呼,Linus也感到非常頭痛…1000多位開發者,每分每秒都不知道誰改到同一行、改出什麼版本!

此時勢必要有協同開發工具,讓開發以最快且最無痛的方式進行,Git於是應運而生。(Linus的神事蹟乃在十天內自幹出這個全世界最愛用的版本開發系統…)

有這麼多款的版本控制系統,為什麼我們要專挑Git來推薦呢?以下列舉必學Git的三大優點供讀者參考:

1. Git是分散式版本控制系統

事實上,VCS分成集中式版本控制系統 (Centralized Version Control System)與分散式版本控制系統 (Decentralized Version Control System)。

集中式版本系統的版本儲存庫Repository都存在中央的伺服器;由於工作的時候都是用自己本地端的電腦,所以要先從中央伺服器取得最新的版本、然後開始一直打一直打一直打Code…

做完之後,再把做好的版本推回去中央伺服器。就好比是一個圖書館,你要改一本書,必須先從圖書館借出來,然後回到家自己改,改完了,再放回圖書館。

集中式版本控制系統最大的問題,就是所有的變更與提交,均須在聯網的情況下才能達成。遇到網速慢的話,可能提交一個10M的文件就需要5分鐘…看那上傳速度會讓人活生生地憋死。

相反地,分散式版本控制系統根本沒有「中央」,每個人的電腦上都有一個完整的Repository,這樣工作的時候就不需要聯網了,因為版本庫就在你自己的電腦上。

既然每個人電腦上都有一個完整的Repository,那多人該如何協作呢?

比方說你在自己電腦上改了文件A,你的同事也在他的電腦上改了文件A,這時之間只需把各自的修改推送給對方,就可以互相看到對方的修改了。

分散式版本控制系統的安全性要高很多,因為每個人電腦裡都有完整的版本庫,某一個人的電腦壞掉了不要緊,隨便從其他人那裡複製一個就可以了。而集中式版本控制系統的中央電腦要是出了問題,所有人都沒辦法工作了。

2. Git的設計架構讓開發者能碰到底層、進行更彈性的開發

為了管理龐大的Linux Kernel程式碼、勢必要選擇合適的版本控制系統;Linus認為CVS和SVN這些集中式的版本控制系統速度慢又要連網,故而堅定地反對。

然而當時的分散式版本系統都要付費,不合開源精神;好在分散式版本控制系統BitKeeper雖然是一家商業公司所發行,但它還是願意開放給Linux社群免費使用。

這幸福的日子直到2005年的某一天,一個Linux Kernel 的開發者嘗試去破解BitKeeper裡面的東西、惹怒了 BitKeeper,導致後來免費使用權被收回去。

你可能會以為Linus會跑去道歉,一切又相安無事;然而大神就是大神,BitKeeper跟大多數人沒想到的事情是、Linus會乾脆自己在十天內搞出一個 Git

由於Linus一開始在開發Git的時候,不是以「我要開發出版本控制系統」、而是「我要讓其他人更好的開發出版本控制系統」的角度出發。

所以他設計了一套以命令字元(Commands)組成的工具集、讓其他人得以藉由這套工具去開發出版本控制系統。

這套底層命令叫做Plumbing,就像幫浦在地底下打好了輸送水的基礎,使用者可自行在上面建立高層命令Porcelain,也就是在水管上蓋任何你想蓋的馬桶、浴缸等的應用。

Git在Linus的想像中是作為內部核心、讓開發更快更容易,如同他當初寫出了Linux Kernel後再釋出開源碼、讓其他開發者得更加完善化。

也因此Git不是一個很友善的使用者介面,因為它本來就不是要直接作為版本控制系統來使用的。

 

Git的原意是作為底下的水管(Pumbling),讓開發者能自行開發上層應用(Porcelain)。

結果Linus不小心連高層的Porcelain命令都寫完、自幹出了一整套版本控制系統。

…。

但這仍無影響原先Plumbing/Porcelain的設計架構,使用者現在還是可以碰到所有的底層命令Plumbing,並利用它進行更多進階的開發命令。

 

3. GitHub是全世界最受歡迎的程式碼管理/分享網站

要說什麼是工程師的臉書,非GitHub莫屬。Github.com提供Git的代管服務,允許使用者在網站上存取程式碼、專案和Git版本管理。

除此之外,GitHub也提供了一些社交協作功能,包括能追蹤其他使用者或組織的專案動態、對程式碼的更動和Bug提出評論;如果你喜歡這個專案,還可以對它按顆星星表達愛意。

優拓資訊的承澤就是台灣JS界累積最多星星數的人。

GitHub也提供了圖表功能,顯示軟體開發者的活躍程度──Contributions的數量很大程度地表現了一位工程師的履歷,面試時Github帳號丟出來、便可以大概知道該人的開發經驗和程度。

截至2015年為止GitHub已經有超過九百萬人數註冊,是目前世界上最大的程式碼存放網站和開源社群。

想放上自己的專案和其他人共同協作討論?看看別人都在做甚麼厲害的專案?想研究強者的程式嗎?想增添自己的履歷?

別猶豫,趕快學會Git/GitHub,加入這個大家庭吧!

 

 

・ 安裝並設定Git

請先下載並安裝Git、並建立一個GitHub帳號。安裝完Git後請打開命令介面,輸入git –version確認是否安裝成功。

$ git --version
git version 2.9.0.

 

安裝越新的版本、安全性越高,不過基礎指令在任何版本都可以運行。

一開始要告訴Git使用者資訊,請打開Git介面,輸入git config –global user.namegit config –global user.email的使用者名稱和Email (就是你的GitHub使用者名稱和信箱)。

$ git config --global user.name "Lynn19931205"
$ git config --global user.email "j890355b@gmail.com"

 

加上 –global 表示是全域的設定。你可以使用 git config –list 這個指令來看你的 Git 設定內容:

$ git config --list
    user.name=Lynn19931205
    user.email=j890355b@gmail.com

 

 

・開始使用Git

在開始學習Git的時候,初學者首先須認知Git中的檔案有三種狀態:已修改已暫存已提交,三者分別位於Working Directory (本地端)Staging Area (暫存端)Repository (版本庫)

我們在玩遊戲時若沒有存檔、就算關掉遊戲也不會回到歷史狀態;因此我們需要藉由每一次的指令「commit」,也就是遊戲的「存檔」,讓Git知道要在什麼時間點儲存一個版本。

但Git貼心的地方是、為了讓你有反悔的機會,不要commit一個新版本後、才發現沒檢查好錯誤的地方就儲存這個版本,因此又多設了一個「暫存區」稱為「Staging Area」。

可以先將已修改的檔案先用加到「Staging Area」,等確認好檔案一切無誤後、再存入版本庫中。此時就是我們每次的遊戲存檔啦!

因此Git的提交流程即是──

  1. 修改完但還沒提交的檔案為Modified (玩到一半但還沒存檔的遊戲)
  2. 藉由git add指令進入Staging Area暫存起來 (暫存區,但還沒真的確定要存)
  3. 最後藉由git commit指令確定將這個檔案版本存入Repository (版本庫,成功存成一個遊戲版本了)。

瞭解完Git檔案狀態後,就讓我們來實際操作一次Git指令吧!

 

● 將現有專案Clone下來

如果現有的專案和Repository已經存在GitHub上,可以上GitHub網站、按下專案內部的綠色按鈕、將網址複製下來,回去打開Git的命令介面輸入git clone加上複製的網址:

$ git clone

 

這邊我輸入一個我有興趣的開源的專案:

$ git clone https://github.com/sarcadass/granim⋯⋯ New_Project

 

打開桌面(或任何你複製檔案下來的地方),就可以看到這個叫做New_Project的資料夾出現了!

 

● 建立自己的專案

首先打開Git的命令介面,在桌面創建一個叫做project_1的資料夾,並在裡面創建一個名稱為hello.txt的文件。

$ cd ~/desktop$ mkdir project_1
$ cd ~/desktop/project_1/
$ touch hello.txt

 

建立完我們第一個專案後,打上git init指令、讓Git遊戲存檔專家進駐吧!

$ git init
Initialized empty Git repository in c:/Users/Lynn/desktop/project_1/.git/
Lynn@LYNNCHEN ~/desktop/project_1 (master)

 

成功了!我們也會同時看到project_1後面出現master,是什麼意思呢。

一般而言的開發方向有一條主要的開發方向,被稱為「master」,中間可以隨時依功能開發需求、開一條獨立的分支「branch」,等branch方向的功能做到一定程度之後、再「merge」回主幹道上。

由於我們還沒有開起任何branch,Git設定我們目前在的主幹道就是master

這時候輸入git status指令,意思是查看目前的Git狀態:

$ git status
On branch master

Initial commit
Untracked files:
  (use "git add ..." to include in what will be committed)

        hello.txt

nothing added to commit but untracked files present (use "git add" to track)

 

1. 進入暫存區(add)、提交版本(commit)

看到Git大神告訴你:「hello.txt」這個檔案還沒有被追蹤嗎?別擔心,只有當我們對這個檔案進行修改addcommit三個動作後,這個檔案才正式進入被追蹤版本的狀態。

所以我們打開hello.txt這個檔案,隨便打上一些指令並存檔:

git03.jpg

回到Git命令介面,輸入git add 主檔名.副檔名將修改推到Staging Area。如果只打git add不輸入檔案名稱,就會將整個資料夾的檔案都一起推送上去到Staging Area。

最後用commit將Staging Area內的東西推到Repository成為正式的版本時,方式是輸入git commit -m並在一行之內寫上這次的修改動了哪些東西 (記得加上引號 ’ ’ )、方便其他協作者瞭解:

$ git add 
hello.txt

$ git commit -m 'Add a line'
[master (root-commit) 0dc97a8] Add a line.
 1 file changed, 0 insertions(+), 0 deletions(-)
 create mode 100644 hello.txt

 

由於git commit -m僅能輸入一行評論;如果想要比較詳細的評論時,可改為輸入git commit -e就能打開編輯器、撰寫超過一行的評論。

此時我們就成功添加了一個版本了!再用git status看看目前檔案的狀態:

$ git status
On branch masternothing to commit, working directory clean

 

現在的工作區很乾淨噢,沒有新的修改、Staging Area裡面也沒有東西。

此時還可以利用另外一個功能git log查看所有的commit紀錄:

$ git log
commit 0dc97a8057e1f30139729ce2316f5be966a65ef2
Author: Lynn19931205 
Date:   Wed Sep 7 18:32:32 2016 +0800

    Add a line.

 

可以看到Git顯示──我們目前為止已經有一個commit了噢!

 

※ 如果add之後反悔、想將檔案移出的暫存區呢?

Oops! 我add錯檔案了! 如何將暫存區的檔案移出暫存區呢? 利用git rm –cached功能吧!然後打開git status檢視一下:

$ git rm --cached hello.txt
rm hello.txt
$ git status
On branch masterChanges to be committed:
  (use "git reset HEAD ..." to unstage)
        deleted:    hello.txt

Untracked files:
  (use "git add ..." to include in what will be committed)
        hello.txt

 

等等!git status的顯示是怎麼回事? 為什麼hello.txt又變成untracked狀態了?

如果已經被加入repository過的檔案,絕對不行用git rm –cached,因為這行指令是把檔案從Repository中幹掉:

  1. 若檔案不在Repository內 : git rm –cached幫我們從stage刪除,且檔案本來就是untracked,執行完還是untracked,還在預期之內。
  2. 若檔案已經在repository內 : git rm –cached會幫我們從repository刪除,並且從stage刪除,因為已經從repository刪除檔案,檔案會從tracked變成untracked

因此要移除Staing Area中的檔案,我們必須根據檔案狀態採用不同的指令:

  1. 若該檔案不在repository內 : git rm –cached 檔案名稱
  2. 若檔案已經在repository內 : git reset HEAD 檔案名稱

哇,我們學會了:

git clone   
clone下來別人的專案到local端

git init  
創建一個新的Repository (打開任何一個專案資料夾打上git init)

add add 主檔名.副檔名   
將檔案提交入Staging Areagit add 整個資料夾提交入Staging Area 

git commit -m '一行解說文字'  
將檔案提交入Repository git commit -e 能用編輯器撰寫多行解說文字

git rm --cached 檔案名稱 
將不在Repository的檔案移出Staging Area

git reset HEAD 
將已經在Repository的檔案移出Staging Area

git status
查看目前檔案的狀態

git log
查看目前所有commit的歷史紀錄

 

 

2. 查看目前狀態的常用指令 (diff, log, show)

如果我們打開hello.txt、輸入新的一行文字:

再從Git命令列鍵入git diff就可以看到修改過後的紀錄。

$ git diff
diff --git a/hello.txt b/hello.txt
index e69de29..1029def 100644
--- a/hello.txt
+++ b/hello.txt
@@ -0,0 +1,2 @@
+print ('hello world')^M
+print ('I wanna fly')
\ No newline at end of file

 

對了,在我們加了好幾條commit後,我要怎麼知道每一項commit確切到底改了什麼呢?(光看那一行的解說根本看不懂啊)

讓我們來玩玩看git show來查看某項特定commit的修改內容:

$ git log
commit 8a85094b22edf4184e7228c3f849807dd7eed2c9
Author: Lynn19931205 
Date:   Wed Sep 7 19:07:21 2016 +0800

    Add new line I wanna fly

commit 0dc97a8057e1f30139729ce2316f5be966a65ef2
Author: Lynn19931205 
Date:   Wed Sep 7 18:32:32 2016 +0800

    Add a line.

 

然後我們把上面第一欄的commit代碼複製起來 (複製六碼或以上)後輸入在git show後面,就會顯示該次詳細的修改內容:

$ git show 8a8509
commit 8a85094b22edf4184e7228c3f849807dd7eed2c9
Author: Lynn19931205 
Date:   Wed Sep 7 19:07:21 2016 +0800

    Add new line I wanna fly
diff --git a/hello.txt b/hello.txtindex e69de29..1029def 100644--- a/hello.txt+++ b/hello.txt@@ -0,0 +1,2 @@+print ('hello world')^M+print ('I wanna fly')\ No newline at end of file

 

事實上,在git diff後面輸入commit代碼,也可以比較兩次commit間修改的差異噢:

$ git diff 8a8509 0dc97a
diff --git a/hello.txt b/hello.txt
index 1029def..e69de29 100644
--- a/hello.txt
+++ b/hello.txt
@@ -1,2 +0,0 @@
-print ('hello world')
-print ('I wanna fly')
\ No newline at end of file

 

耶,我們學會了:

git log 
列出所有commit的歷史紀錄

git diff 
比較文件或commit間修改的差異

git show 
詳細列出該次commit的修改內容

git status 
列出目前的檔案狀態

 

辛苦了!但別在這邊就放棄,接下去還有哦~~我們會教大家如何把local端的程式碼推上雲端,請參考part 2介紹

本系列完整集數