iT邦幫忙

2018 iT 邦幫忙鐵人賽
DAY 2
0
Modern Web

Node JS-Back end見聞錄系列 第 2

Node.js-Backend見聞錄(01):關於Git(一)-什麼是版本控制系統?

Node.js-Backend見聞錄(01):關於Git(一)-什麼是版本控制系統?


前言

讀者可能會猶豫為什麼主講後端的文章會需要談到git,這是因為筆者認為在我們專案成形後,git具有協助我們做到版本控制及異地備份的功用。若要跟其他團隊中的工程師夥伴(不管前端、後端或APP端...等。)進行協同合作,也是非學不可的利器之一。

什麼是版本控制系統

在一個系統的開發過程我們可能會經歷些設計階段。

如:雛型->上線前->測試->正式上線->維護->功能延伸…等。

在每個設計階段中可能會有不一樣的版本,而版本控制系統能夠提供給我們在進行專案時,能夠紀錄一個或一組檔案在某段時間內的變更。若是之後專案中的資料有損毀或是遺失等不可預期的問題發生,都能藉由該特性隨時隨地恢復專案在能正常運作的版本(在有正常使用版本控制系統的情況下)。

以git來說它是屬於版本控制系統中的「分散式版本控制系統」,其特性是將整個專案儲存為儲存庫(repository),也就是每當有做版本上的變動時等同於在備份整個專案。

「儲存庫」是git用來儲存整個專案的物件資料庫。通常我們可以使它來將整個專案進行備份、異地開發甚至是用此特性來與他人協作開發。

相較於「分散式版本控制系統」,另外還有「本地端版本控制系統」、「集中式版本控制系統」而這些系統的特性可參考關於版本控制這篇。

對於git除了上述所提到的儲存庫(repository)外,下述還會介紹工作目錄(working directory)、暫存區(index/stage)還有提交(commit)。

工作目錄(working directory)

在我們專案中有可能會有許多的不一樣的版本,而「工作目錄」則是眾多版本中所取出的其中一個版本。而這些資料則是從「儲存庫」中取出,提供開發者進行開發或使用。

暫存區(index/stage)

為一個單純的檔案,在「工作目錄」中若檔案有所更動,其檔案的更動狀態都會暫存在「暫存區」,以供後續「提交」階段所使用。

提交(commit)

「提交」在git中就是一個節點,而這些節點就是開發者在未來想要回朔版本或追蹤版本的參考。也可以想像成每次在「提交」時,也就等同在進行一次儲存版本的動作。


一些基本的git指令

在該部分,我們將會透過下圖中的各種不同情況來進行介紹,並針對各個情境來講述要用什麼樣的git指令。

A: 將一般目錄變成git working folder

$ git init

B: 將working folder的異動狀態登錄到stage

若是要將特定檔案(如readme.md)可使用git add <filename>來進行。

$ git add readme.md

但若是不針對特定檔案,想要一次將全部有異動的狀態登錄到stage則可以使用git add .的方式進行。

$ git add .

C: 將stage的狀態放到local repository

在輸入git commit -m之後,我們可以在後面為該次commit增加異動說明,好用來提醒自己或是協同開發的夥伴們得知,每個commit之間做了什麼樣的變動。

$ git commit -m "first commit"
[master (root-commit) 6b56ee9] first commit
 1 file changed, 1 insertion(+)
 create mode 100644 readme.md

D: 將local repository的狀態放到remote repository

若是要將整個local repositry放到remote repository則可以使用git push這個指令,此時就會將我們整個專案放到遠端的儲存庫,也等同於做了一次專案備份的動作。

$ git push -u origin master
Counting objects: 3, done.
Writing objects: 100% (3/3), 214 bytes | 0 bytes/s, done.
Total 3 (delta 0), reused 0 (delta 0)
To https://github.com/PenguinRun/TeaTime.git
 * [new branch]      master -> master

E: 將remote repository的狀態取回到local repository

若是要將repository更新到在remote repository的最新版本,則可以使用git pull來進行。但若已經是在最新狀態則會出現Already up-to-date的回應。

$ git pull
Already up-to-date.

F: 將local repository的狀態退回到stage階段(是C的逆向)

假設在commit輸入版本資訊時有誤,想要重新修改commit資訊就可以使用該指令。
該階段我們會透過git log來觀看commit的版本歷程

$ git log
commit 6867a82c9d599d1f1e9384e3ef0bac5207afb474
Author: penguinrun <xxx@gmail.com>
Date:   Sun Mar 26 15:00:19 2017 +0800
git test
commit 6b56ee9a804e1f464ae13e40a8cdd7fd750e14b6
Author: penguinrun <xxx@gmail.com>
Date:   Sun Mar 26 14:56:52 2017 +0800
first commit

若說要把資訊為”git test”的commit修改成”black tea”,這邊有兩種方式可以實現。

方式一

可以先使用git reset --hard來記錄我們要修正的commit的SHA值。之後在藉由git reset --soft HEAD@{1}指令來實行。

$ git reset --hard d676f2c539c3adc2156b7ee8f76f26252de40e28
HEAD is now at d676f2c git test
$ git reset --soft HEAD@{1}

之後我們在透過git log來觀看,就會發現到只剩下一支commit,而這支commit也就是我們最初新建時的那支。

$ git log
commit 6b56ee9a804e1f464ae13e40a8cdd7fd750e14b6
Author: penguinrun <xxx@gmail.com>
Date:   Sun Mar 26 14:56:52 2017 +0800
first commit

那讀者應該會有個疑問是那剛剛修改的那支呢?

別擔心!只要在輸入git commit -m “black tea”的指令後就會又出現了。這是因為git的狀態已經回覆到在我們還沒輸入commit資訊前,所以它並不會顯示在git log中。而之後的版本狀態我們在透過git log來觀看就可以看到原本消失的那一個又出現了,而且commit的版本資訊也改為”black tea”。

$ git commit -m "black tea"
[master 0d39456] black tea
 1 file changed, 1 insertion(+)
 create mode 100644 black_tea.js
$ git log
commit 0d394564a3f78bb5bc92f6f0ab8efd8c61fa669d
Author: penguinrun <xxx@gmail.com>
Date:   Sun Mar 26 16:36:58 2017 +0800
black tea
commit 6b56ee9a804e1f464ae13e40a8cdd7fd750e14b6
Author: penguinrun <xxx@gmail.com>
Date:   Sun Mar 26 14:56:52 2017 +0800
first commit

方式二

直接使用git reset --soft HEAD^。這個指令中的”^”會直接針對我們在最後一支版本前的那一支直接進行更改,之後在輸入git commit -m “black tea”就可以了。

$ git reset --soft HEAD^
$ git status
On branch master
Your branch is behind 'origin/master' by 1 commit, and can be fast-forwarded.
  (use "git pull" to update your local branch)
Changes to be committed:
  (use "git reset HEAD <file>..." to unstage)
new file:   black_tea.js
$ git commit -m "black tea"
[master f7e6827] black tea
 1 file changed, 1 insertion(+)
 create mode 100644 black_tea.js
$ git log
commit f7e68273c4f9898e1686323f88232203db777ca4
Author: penguinrun <xxx@gmail.com>
Date:   Sun Mar 26 17:19:25 2017 +0800
black tea
commit 6b56ee9a804e1f464ae13e40a8cdd7fd750e14b6
Author: penguinrun <xxx@gmail.com>
Date:   Sun Mar 26 14:56:52 2017 +0800
first commit

G: 將stage的狀態退回到working folder階段(是B的逆向)

將在stage狀態的git 恢復到還沒執行git add指令之前。
這時候我們先重新git pull來將存放在remote repository的資料取回來可以發現到black_tea.js又回來了。

$ git pull
Updating 6b56ee9..6867a82
Fast-forward
 black_tea.js | 1 +
 1 file changed, 1 insertion(+)
 create mode 100644 black_tea.js

並重新使用git log來觀看目前commit的情形

$ git log
commit 6867a82c9d599d1f1e9384e3ef0bac5207afb474
Author: penguinrun <xxx@gmail.com>
Date:   Sun Mar 26 15:00:19 2017 +0800
black tea
commit 6b56ee9a804e1f464ae13e40a8cdd7fd750e14b6
Author: penguinrun <xxx@gmail.com>
Date:   Sun Mar 26 14:56:52 2017 +0800
first commit

這時,我們先藉由F的流程先將git的狀態挪至stage的狀態,並透過git status來觀看結果:

$ git reset --soft HEAD^
$ git status
On branch master
Your branch is behind 'origin/master' by 1 commit, and can be fast-forwarded.
  (use "git pull" to update your local branch)
Changes to be committed:
  (use "git reset HEAD <file>..." to unstage)
new file:   black_tea.js

接著在透過git reset HEAD的指令來將black_tea.js還原至還沒進行git add之前,並透過git status來觀看結果:

$ git reset HEAD black_tea.js
$ git status
On branch master
Your branch is behind 'origin/master' by 1 commit, and can be fast-forwarded.
  (use "git pull" to update your local branch)
Untracked files:
  (use "git add <file>..." to include in what will be committed)
black_tea.js
nothing added to commit but untracked files present (use "git add" to track)

H: 將local repository的狀態一口氣退回到working folder階段

這時候我們先重新git pull來將存放在remote repository的資料取回來,並重新使用git log來觀看目前commit的情形:

$ git log
commit 6867a82c9d599d1f1e9384e3ef0bac5207afb474
Author: penguinrun <xxx@gmail.com>
Date:   Sun Mar 26 15:00:19 2017 +0800
black tea
commit 6b56ee9a804e1f464ae13e40a8cdd7fd750e14b6
Author: penguinrun <xxx@gmail.com>
Date:   Sun Mar 26 14:56:52 2017 +0800
first commit

這時,我們在嘗試使用看看git reset會有什麼樣的效果並藉由git log來觀看版本歷程看有什麼樣的變化:

$ git reset 6b56ee9a804e1f464ae13e40a8cdd7fd750e14b6
$ git log
commit 6b56ee9a804e1f464ae13e40a8cdd7fd750e14b6
Author: penguinrun <xxx@gmail.com>
Date:   Sun Mar 26 14:56:52 2017 +0800
first commit

就會發現到原先的“black tea”已經不見了,這時我們可以透過git status來觀看,就能發現到已經恢復到在我們進行git add之前的狀態。

$ git status
On branch master
Your branch is behind 'origin/master' by 1 commit, and can be fast-forwarded.
  (use "git pull" to update your local branch)
Untracked files:
  (use "git add <file>..." to include in what will be committed)
black_tea.js
nothing added to commit but untracked files present (use "git add" to track)

I: 從remote repository取回建立成新的working folder

取出remote repository的資料,我們可以透過git clone來實現,而這時就可以看到整個專案的檔案。

$ git clone https://github.com/PenguinRun/TeaTime.git
Cloning into 'TeaTime'...
remote: Counting objects: 6, done.
remote: Compressing objects: 100% (3/3), done.
remote: Total 6 (delta 0), reused 6 (delta 0), pack-reused 0
Unpacking objects: 100% (6/6), done.
Checking connectivity... done.

J: 將git working folder變回一般目錄

如果要將專案脫離git恢復成一般目錄則輸入rm -r .git就可以。這時我們嘗試用git status來測試,可以發現到它會告知現在已經沒有git的repository,也就是已經恢復成一般目錄了。

$ rm -r .git
$ git status
fatal: Not a git repository (or any parent up to mount point)
Stopping at filesystem boundary (GIT_DISCOVERY_ACROSS_FILESYSTEM not set).

小結

基本的git運用介紹就到此結束。在這篇中運用到的指令其實都比較跟個人開發有關,而在下一篇中將會針對「branch」來介紹git的相關指令。

參考資料

Pro Git

Revert to a commit by a SHA hash in Git?


上一篇
Node JS-Backend見聞錄(00):關於本系列文章
下一篇
Node.js-Backend見聞錄(02):關於Git(二)-branch
系列文
Node JS-Back end見聞錄31

尚未有邦友留言

立即登入留言