版本管控

Git、Repository

系統開發通常是一種PDCA(Plan-Do-Check-Action)的過程,這也就意味者原始碼會因為需求、重構、協同開發等因素而有許多的版本,因此如何管理這些版本, 方便開發者追蹤原始碼,了解系統軟體的歷史變化,就需要版本控制系統來幫忙了。

一般來說若開發者修正了 3 個程式臭蟲,就得有 3 個版本被寫入儲存庫,而不要全部修正才儲存一個版本,傳統使用集中式版本控制系統若沒辦法連到網路,就必須同時修畢 3 個臭蟲後才能提交,而無法追蹤這3 次修改的變化,而分散式版本控制系統 (Distributed Version Control System) 除了遠端共享的儲存庫以外,在每一個開發者本機端還設計了一個本地端儲存庫(Repository),開發者不須透過網路便能提交新版本的程式碼到本機端儲存庫,等到需要將本機端儲存庫上資料同步到遠端儲存庫時,才需要使用網路。

目前最流行的分散式版本控制系統是 git ,是 Linus Torvalds 在 2005 年為了管理 Linux 核心所創造,git 可以有效處理龐大的專案,而且專案合併與分支也十分有效率。

在Windows下安裝

本教學預設在 Windows 平台操作,請先安裝 VS codeGit for Windows

VS code 需要調整的安裝選項請參考下圖

Git 需要調整的安裝選項請參考下圖

版本儲存庫(Repository)

Git 的版本儲存庫分為本地端儲存庫(Local repository)與遠端儲存庫(Remote repository)。

  • 本地端儲存庫:為了方便個人進行版本控制之用,會在自己的專案目錄下配置的儲存庫。

  • 遠端儲存庫 :為了讓多人使用而建立的儲存庫 ,需透過網路來上傳自己的修改內容或取得他人的修改內容,需有專用的伺服器。

本地端指令基本操作(Basic git in local)

在此簡單的介紹如何在本地端透過指令 git 來進行基本的版本管控

Git 全域設定

因為 Git 每次提交都要紀錄提交者的名稱與 Email 資訊,以下指令可針對所有專案做一次全域設定。

git config --global user.name "peterju"
git config --global user.email "peterju.tw@gmail.com"

可使用命令 git config --list 檢視目前設定,設定檔的位置在使用者家目錄下的 .gitconfig

其他全域設定 (*補充)

若要直接編輯上述設定檔,可下達命令

git config --global -e

Git 預設的編輯器是 vim,若安裝時沒有更改預設編輯器為 vscode,仍可以下述指令調整:

git config --global core.editor "code"

當然你要先安裝 vscode ,並將路徑加入環境變數 path 中才能生效

對於常用的 Git 指令與參數可以用別名的方式簡化輸入,例如

git config --global alias.st status
# git st
git config --global alias.ll "log --oneline --graph"
# git ll

建立專案的本地端儲存庫(Local repository)

cd 專案目錄(working directory)
git init

git init 初始化會建立一個隱藏目錄 .git 用來作為本地端儲存庫,其中的名為 index 的索引檔案,作為記錄專案所有檔案的狀態之用。

Git 專案設定

若要每個專案各自設定不同的作者,則請在專案目錄下執行下列指令,未設定會抓全域的設定

# 以下指令需在 git init 之後才能執行
git config --local user.name "peterju"
git config --local user.email "peterju.tw@gmail.com"

與專案有關非全域的設定檔位置在 .git/config

查詢檔案的狀態

查詢當前目錄的版控檔案狀態,例如有檔案被變更、刪除、新增或其他改變。

git status
  • untracked files:從未被提交過的新檔案,git 只會追蹤(track)被提交(commit)過的檔案修改狀態

  • staged files:移入暫存區準備要提交的檔案

  • unmodified files:已提交到版本庫的檔案,代表工作區的檔案與版本庫的檔案一致

  • modified files: 已提交的檔案被被修改或刪除過,但尚未暫存的檔案

將異動的檔案放入暫存區域(staging area)

可使用 git add 或 git stage 指令

git stage index.php package.json
git stage *.php                     # 也可以用萬用字元一次暫存相同類型檔案
git stage .                         # 也可以加入當前目錄所有的檔案

git add 指令是將指定檔案加入暫存區,而非加入版本控制,因此後續的 Git 版本建議以 git stage 取代 git add。

通知 Git 忽略檔案的異動

某些在專案目錄下的檔案,例如執行檔、資料庫帳密設定檔...等,我們並不想被 Git 監控管理,這些檔案在使用 git status 的時候都一直會出現在 Untracked files 中,此時可建立 .gitignore 的檔案,將檔案或資料夾名稱含相對路徑放進去,這樣 Git 就知道這些檔案或資料夾的變動是不需要被管理的。.gitignore 本身也是一個檔案,建立後處於 untracked,也請於確認後加入版本管控。

.gitignore 的設定規則

  • 空白列或者以#開頭的列會被忽略

  • 可使用標準的Glob pattern

  • 可以 / 結尾,代表是目錄

  • 可使用 ! 符號將特徵反過來使用

  • 需放在與 .git 相同層級目錄下

  • 設定的檔案或資料夾必須處於 untracked

PHP專案中常用的 .gitignore 參考

### PHP ###
/vendor/*
### Windows Folder ###
Thumbs.db
Desktop.ini
### VisualStudioCode ###
.vscode/*
!.vscode/settings.json
!.vscode/tasks.json
!.vscode/launch.json
!.vscode/extensions.json
.history

將放入暫存區(staging area)的檔案取移出(*進階)

若反悔檔案 package.json 不應進暫存區 (請注意該檔從未提交過,是 untracked file),以下指令可將之移出

git rm --cached package.json # 僅移除暫存區的檔案
git rm package.json          # 工作區與暫存區同時移除,請小心使用

實務上要將檔案從stage移除,還要考慮該檔案是否提交過(是否已存在於 repository) ,詳情請參考:如何將檔案從stage移除 ?

將暫存區的檔案提交(commit)至本地儲存庫

commit 只會提交已在暫存區的檔案,參數 -m 代表 message,若沒有加上 -m 參數,Git會呼叫文字編輯器出來,一定要使用者輸入本次提交的訊息

git commit -m "首次提交"

針對已提交過的檔案變動後(modified fills),可以不經 stage 一次提交,指令如下 git commit -am "提交後修改"

觀察 git 的 commit 紀錄

git log 可以看到過去所有 commit 的紀錄,每個紀錄都有專屬的 commit 代碼

git log
git log -3                      # 只看最近 3 筆紀錄
git log --oneline --graph       # 單行顯示commit歷程與修改內容
git log --oneline --name-only   # 同上、並顯示檔案名稱
git log --oneline --name-status # 同上、並顯示檔案名稱與加入狀態

針對最後一次提交點進行標籤(tag)

每次提交之後,Git 都會產生一個亂數編號代表該提交點,但我們可以針對比較重要的提交點賦予一個標示標籤(annotated tag),方便未來可以用標籤指定此提交點,通常標籤功能會拿來記錄版本編號。

git tag -a v1.4 -m "release version 1.4"
git tag -am "release version 1.5" v1.5    # 也可以合併標籤與訊息參數
git tag

刪除標籤

刪除指定的標籤。

git tag  -d v1.5

指定過去的提交點附加標籤

也可以對過去的提交貼標籤,但要指定 commit 編號。

git log
git tag  -am "release version 1.3" v1.3 401de95

比較檔案異動後改變了甚麼

首先要提醒的是,git diff 比對異動只針對加入 Git 版控的檔案,untracked 的檔案是無法比對的。

git diff 可以比較最後一次提交區(HEAD)、暫存區(index, cached, staged)、工作區(working)的所有檔案異動狀態,若要只針對某檔案則可加上檔名。

# 以下做二次 commit
echo done1>> index.php
git stage .
git commit -m "today first commit"
echo done2>> index.php
git stage .
git commit -m "today second commit"

# 修改檔案後暫存,之後再修改一次
echo StagedTree>> index.php
git stage .
echo WorkingTree>> index.php

# 觀察以下 diff 的指令差別
git diff index.php            # 比較暫存區與工作區
git diff --staged index.php   # 比較最後一次提交與暫存區
# git diff --cached index.php   # 同上
git diff HEAD index.php       # 比較最後一次提交與工作區
git diff HEAD^^ HEAD          # 比較倒數第二個提交與最後一次提交

預設 git diff 會比較工作區與暫存區的差異,但暫存區沒有檔案則會改為比對工作區與提交區的差異。

檔案異動後反悔,想取回前次的版本(*進階)

這個議題比較複雜,特別是 git reset 指令若搞不清楚,千萬別用,建議一定要用指令測試各種情境下的狀況。

1. checkout 預設會取回暫存區的 index.php ,若尚未加入暫存區,則會取回最後一次 commit 的 index.php

git checkout index.php

2. 若工作區、暫存區與最後一次 commit 的 index.php 內容都不一樣,此時想取回最後一次 commit 的檔案至工作區,同時清除暫存區的 index.php

echo test1>> index.php
git stage .
echo test2>> index.php
git status
git checkout HEAD index.php

3. 一次捨棄所有暫存區的檔案,工作區不動

git reset 

git reset 有三種模式 --hard :工作區、暫存區、儲存庫皆還原至reset目標點 --mixed : 此為預設值,暫存區、儲存庫 還原至reset目標點,工作區不變 --soft : 僅儲存庫還原至reset目標點,工作區、暫存區皆不變

4. 一次捨棄所有修改,暫存區、工作區全部還原

git reset --hard

參考網頁

Last updated