Git 作為現代主流的版本控制系統,其分散式架構讓每個開發者都擁有完整的專案副本和歷史紀錄,方便離線開發和團隊協作。其分支管理機制允許開發者建立多個分支,以同時進行不同功能的開發或錯誤修復,有效提升開發效率。Git 的操作速度極快,無論是提交更改、切換分支或合併程式碼,都能在短時間內完成,即使是大型專案也能保持高效能。此外,Git 的高可靠性確保了程式碼的安全性,幾乎不會發生資料遺失的情況。然而,Git 的命令繁多且部分命令的用法不一致,初學者需要花費一定時間才能熟悉。學習曲線相對陡峭也是 Git 的一個缺點,需要投入時間和精力才能掌握其核心概念和操作方法。瞭解 Git 的基本指令,例如 git initgit statusgit addgit commit 等,是入門的關鍵。透過這些指令,開發者可以初始化儲存函式庫、檢查檔案狀態、暫存變更和提交程式碼。此外,理解 Git 的分支概念和如何使用 git branchgit checkoutgit merge 等指令進行分支管理,也是高效運用 Git 的重要一環。

Git的好處與基本操作

Git的優勢

Git 作為一個現代化的版本控制系統(Version Control System, VCS),具有多種優勢,使其成為開發者首選的工具。以下是Git的一些主要好處:

分支管理促進快速開發

Git 的分支管理功能強大且靈活,允許開發者建立和使用多個分支。這種設計鼓勵開發者頻繁使用分支,從而促進了安全且快速的開發流程。無論是新功能的開發還是錯誤的修復,分支管理都能讓團隊成員同時進行不同的工作,而不會相互幹擾。

高速操作

Git 的速度非常快。無論是新增檔案、提交更改、回復到舊程式碼還是同步檔案以融契約事的編輯,這些操作都能在幾秒鐘內完成,即使在大型專案中也是如此。特別是在建立、使用和合併分支時,Git 的操作速度極快,這也是開發者喜愛 Git 的原因之一。

高可靠性

可靠性是任何 VCS 的基本要求。然而,歷史上有一些 VCS 並不完全可靠,經常會丟失或損壞檔案或編輯內容。相比之下,Git 以其高可靠性著稱。雖然 Git 是一個複雜的工具,如果不正確使用其命令可能會導致人為錯誤而丟失資料,但技術故障導致資料丟失的情況幾乎未聽說過。這使得 Git 被全球無數團隊信賴,這種信賴是完全應得的。

分散式架構

在 Git 出現之前,許多 VCS 使用的是集中式架構。這意味著要編輯一個檔案,你需要從中央伺服器取得其最新版本,進行編輯後再將檔案提交回中央伺服器,這樣其他團隊成員才能存取它。

然而,集中式架構有幾個問題:

  1. 檔案鎖定:某些集中式 VCS 在你簽出一個檔案時會鎖定它,這樣其他人就無法在你編輯時對該檔案進行更改。這會導致許多「你什麼時候完成 Foo.java 呢?」這樣的對話,使工作流程變得尷尬且不便。

  2. 網路依賴:集中式架構需要你在簽出檔案或提交編輯時保持與中央伺服器的連線。如果沒有網路連線,你就無法有效地工作。雖然這個問題現在不如以前嚴重,但在某些情況下仍然存在。

  3. 單點故障:集中式架構創造了一個單點故障。如果中央伺服器當機或損壞,所有開發者都無法工作。如果伺服器丟失資料或被物理破壞,還原資料或重建硬體可能需要數天時間。

  4. 擴充套件性問題:隨著開發團隊的增長,集中式架構可能無法有效擴充套件。快速增長的團隊可能會發現自己排隊等待存取中央伺服器而被阻塞。

分散式架構解決方案

Git 的分散式架構解決了上述所有問題。當每個開發者都擁有專案檔案的完整副本時,這些問題就消失了。每個開發者在本地電腦上擁有整個專案的副本,包括所有檔案、編輯歷史、標籤、提交訊息和其他後設資料。這使得他們可以在不連線到中央伺服器的情況下進行工作。

  1. 無需鎖定:由於每個開發者都擁有本地副本的所有檔案,因此沒有需要鎖定正在編輯的檔案的概念;任何人都可以隨時編輯他們本地副本中的任何檔案。

  2. 無需網路連線:你不需要聯絡中央伺服器來簽出檔案或提交編輯。你可以在本地工作多久都可以。當然,最終你需要與同事同步更改(並檢視他們的更改),但這可以根據團隊需求頻繁或不頻繁地進行。

  3. 無單點故障:由於每個開發者機器上都有一份完整的專案檔案副本,因此不再存在單點故障。如果用於同步變更的中央伺服器當機了,你可以指定任何開發者機器作為臨時中央伺服器來重建原始伺服器。

  4. 良好的擴充套件性:由於使用分散式 VCS 的開發者比使用集中式 VCS 的開發者遠少聯絡中央伺服器進行檢入簽出操作,因此分散式 VCS 比其集中式對手具有更好的擴充套件性。大多數使用 Git 的團隊在新增新成員時經歷不到任何與 VCS 相關的擴充套件問題。

基本 Git 指令實踐

以下是一些常見的 Git 指令及其用法:

# 初始化新儲存函式庫
git init

內容解密:

git init 是初始化新儲存函式庫(repository)的一個命令。當你在一個目錄中執行 git init 時,Git 會建立一個名為 .git 的隱藏目錄來存放所有與版本控制相關的資料。

# 檢視當前狀態
git status

內容解密:

git status 是用來檢視當前儲存函式庫狀態的一個命令。它會顯示哪些檔案被修改過但還沒有被暫存(staged),哪些檔案已經被暫存並準備好提交(commit)。

# 暫存更改
git add <file>

內容解密:

git add <file> 是將特設定檔案新增到暫存區的一個命令。「暫存區」是指將要提交到儲存函式庫的一組更改。「」表示你要暫存哪一個檔名稱。例如:git add README.md 表示將 README.md 暫存區域。

# 提交更改
git commit -m "commit message"

內容解密:

git commit -m "commit message" 是提交暫存區中的更改到儲存函式庫的一個命令。「-m」選項允許你在命令列直接新增提交訊息。「commit message」是描述此次提交內容的一段簡短文字。

Git 的缺點

儘管 Git 有許多優點,但它也有一些缺點:

  1. 命令不一致:Git 的某些命令可能顯得不一致、混亂且反直覺。例如: git branch 命令有三種不同方式來修改其行為:

    • git branch:列出所有可用分支。
    • git branch foo:建立一個名為 foo 的新分支。
    • git branch --delete foo:刪除名為 foo 的分支。

    舉例來說:如果使用 --list 作為替代方式:即 git branch --list 是可行且能正確執行但是因為太長了而少有人使用它。

  2. 學習曲線陡峭:對於新手來說學習 Git 需要一定時間和精力去理解其命令和工作流程。

總結來說,Git 是一個功能強大且靈活的版本控制系統,具有高速操作高可靠性以及分散式架構等優點,能夠促進安全且快速的開發流程。然而,Git 的學習曲線較陡,新手可能需要一些時間去理解其各種命令和功能。

Git 版本控制的基本操作與實務應用

在現代軟體開發中,Git 是最常見且強大的版本控制系統。然而,Git 的功能多且複雜,對於初學者來說可能會感到困惑。其實,掌握少數幾個常用指令就能完成大部分的 Git 操作。接下來,玄貓將帶您深入瞭解 Git 的基本概念和操作方法,並提供實際案例來幫助您更好地理解。

版本控制系統的優勢

首先,瞭解為什麼需要使用版本控制系統(VCS)是非常重要的。VCS 能夠記錄檔案的變更歷史,讓你能夠回溯到任何時點的檔案狀態,這對於團隊協作和錯誤追蹤至關重要。

為何 Git 如此強大?

Git 的強大之處在於其分散式架構,每個團隊成員都擁有完整的專案副本及其歷史記錄。這意味著你可以在沒有網路連線的情況下進行開發,並且在需要時與其他人同步資料。

初次接觸 Git 的困惑

許多人在初次接觸 Git 時會感到不安,因為它包含了大量的指令和選項。然而,你只需掌握一些常用指令就能夠高效地使用 Git。

程式碼範例:

# 建立一個新目錄
$ mkdir hats-for-cats

# 進入新目錄
$ cd hats-for-cats

# 檢查當前狀態(不是 Git 目錄)
$ git status
fatal: not a git repository (or any of the parent directories): .git

# 初始化一個新的 Git 目錄
$ git init

# 檢查當前狀態(已經是 Git 目錄)
$ git status
On branch main
Your branch is up to date with 'origin/main'.
nothing to commit, working tree clean

內容解密:

  1. mkdir hats-for-cats:這個指令在當前目錄下建立一個名為 hats-for-cats 的新目錄。
  2. cd hats-for-cats:這個指令切換到剛剛建立的 hats-for-cats 目錄中。
  3. git status:這個指令用來檢查當前目錄是否已經被初始化為 Git 儲存函式庫。由於當前還不是 Git 儲存函式庫,所以會顯示錯誤訊息。
  4. git init:這個指令將當前目錄初始化為一個新的 Git 儲存函式庫。
  5. 第二次 git status:再次檢查當前狀態,顯示已經在主分支上,並且工作區是乾淨的。

建立和克隆儲存函式庫

有兩種主要方法可以建立一個新的 Git 儲存函式庫:

  1. 將現有目錄轉換為 Git 儲存函式庫。
  2. 從遠端複製一個現有的儲存函式庫。

建立本地儲存函式庫

# 建立一個新目錄並進入該目錄
$ mkdir hats-for-cats
$ cd hats-for-cats

# 初始化 Git 儲存函式庫
$ git init

克隆遠端儲存函式庫

# 克隆遠端儲存函式庫到本地
$ git clone <repository-url>

新增檔案到儲存函式庫

要讓 Git 跟蹤你的檔案,你需要將檔案新增到儲存函式庫中。這是一個兩步驟的過程:

  1. 暫存:將檔案放入暫存區域。
  2. 提交:將暫存區域中的檔案提交到儲存函式庫。
# 建立一個新檔案 todo.txt
$ touch todo.txt

# 暫存 todo.txt 檔案
$ git add todo.txt

# 檢查當前狀態(todo.txt 已被暫存)
$ git status
On branch main
Your branch is up to date with 'origin/main'.
Changes to be committed:
(use "git restore --staged <file>..." to unstage)
new file: todo.txt

# 提交暫存區域中的檔案到儲存函式庫
$ git commit -m "Add todo list"

內容解密:

  1. touch todo.txt:這個指令建立一個名為 todo.txt 的空白檔案。
  2. git add todo.txt:這個指令將 todo.txt 檔案新增到暫存區域中。
  3. 第二次 git status:再次檢查當前狀態,顯示 todo.txt 已被暫存。
  4. git commit -m "Add todo list":這個指令將暫存區域中的所有檔案提交到儲存函式庫中,並附上提交訊息。

拍照式快照機制

Git 的版本控制機制類別似於拍照式快照機制,每次提交都會生成一個檔案狀態的快照。這意味著你可以完全控制何時拍照以及拍照時包含哪些檔案。

暫存和提交的靈活性

不論是建立新檔案、編輯現有檔案、重新命名或刪除檔案,都可以透過暫存和提交來進行版本控制。這使得你可以靈活地管理各種型別的變更。

此圖示解說:

  1. A:表示各種對檔案進行操作(建立、編輯、重新命名或刪除)。
  2. B:表示將變更後的檔案新增到暫存區域。
  3. C:表示將暫存區域中的所有變更提交到儲存函式庫中。
  4. D:表示生成一次新的快照。
  5. E:表示更新歷史記錄以反映最新的快照。

暫存與提交流程解說

此圖示解說:

  1. A:表示正在同時編輯兩個檔案 foo.pybar.py
  2. B:決定是否僅需要對 foo.py 生成快照。
  3. C:如果僅需要對 foo.py 生成快照,則將其新增到暫存區域並提交。
  4. D:如果需要對 bar.py 生成快照,則繼續編輯 bar.py
  5. E:決定是否準備好對 bar.py 生成快照。
  6. F:如果準備好對 bar.py 生成快照,則將其新增到暫存在區域並提交。可選地還可以將 foo.py 再次新增到暫存在區域以進行更新。
  7. G:如果還未準備好對 bar.py 生成快照,則等待進一步編輯。

透過這些基本操作和概念,您已經具備了使用 Git 的基本能力。希望這些實務經驗和技術選型考量能夠幫助您在實際開發中更好地應用 Git。

基本 Git 指令練習

現已經將變更提交到 Git 儲存函式庫,玄貓來詳細說明一些關於提交訊息的重要事項。每個提交都應包含一個人類可讀的訊息,這是提交者必須提供的部分。最簡單的方式是使用 --message 選項來為 git commit 命令新增訊息:

$ git commit --message "add list of tasks to do"

再次執行 git status 命令,你會發現沒有任何檔案等待提交,這意味著你的待辦事項清單已安全地儲存在儲存函式庫中,未來對它的所有編輯都會被 Git 跟蹤。恭喜,你剛剛完成了你第一次的 Git 使用!

現在,讓我們來談談 Git 在每次提交中包含的內容。你已經知道每次提交包含所包含檔案的內容和描述提交目地的訊息,但還有一些其他資訊也是每次提交的一部分。以下是完整的列表:

  • 任何對檔案內容(或檔案名稱或檔案系統中的位置)所做的更改。
  • 一個由 Secure Hash Algorithm(SHA)生成的 40 個十六進位制數字字串,這是根據提交檔案內容生成此字串的程式碼。提交的 SHA 唯一標識該提交;可以把它看作是該提交的一個唯一名稱。SHA 不是順序生成的:每個 SHA 都與前一個提交的 SHA 不同。
  • 提交者的名字和電子郵件。
  • 提交者做出該提交時的時間戳。
  • 一條人類可讀的訊息,描述為什麼做出該提交。
  • 指向上一個(父)提交在分支上的指標。玄貓將在下一節中介紹分支的概念並更詳細地討論這個指標。

git log 命令會顯示當前分支上所有提交的這些資訊(玄貓稍後會更詳細地解釋分支)。假設你已經做了兩次提交:一次是建立一個空的待辦事項清單,另一次是向清單中新增一項。執行 git log 則會看到類別似以下輸出:

$ git log
commit 7d4c98438ade780531e1baa283b3239c21943171 (HEAD -> master)
Author: George Spelvin <george.spelvin@example.com>
Date: Tue Jan 4 09:57:37 2022 -0800
    add first item to list

commit 63ea581d1bc693dac159c146fa10d1cbfa4e6366
Author: George Spelvin <george.spelvin@example.com>
Date: Sun Jan 2 12:31:27 2022 -0800
    add list of tasks to do

這個輸出包括了你兩次提交的資訊,最新的一次在最上面。如果你在自己的電腦上執行這些命令,你會看到每次提交不同的詳細資訊,但輸出格式將是相同的。

在這個例子中,所有的提交資訊都是由你自己做出的。但這只是因為你是這個分支上唯一新增過提交的人。如果其他人也對同一個分支做出了提交,你也會看到他們提交資訊在這個輸出中。

排除檔案不加入儲存函式庫

現在,你可能會覺得把專案中的所有檔案都暫存和提交是明智之舉。但在某些情況下,有一些型別的檔案我們通常不希望儲存在 Git 或任何其他版本控制系統(VCS)中。這些型別包括但不限於:

  • 從其他檔案生成的檔案
  • 恩大型檔案
  • 包含機密資訊的檔案

第一種情況包括像是從原始碼編譯而來的可執行檔案,或是從 Markdown 源文字生成的 PDF 檔案。因為我們總可以從其來源重新生成這些檔案,所以沒有必要將它們儲存在 Git 中。此外,把它們放進 Git 中還會引入漂移問題:原始檔和生成檔案之間可能不再同步。例如,原始檔可能包含上週的一些程式碼,而編譯好的可執行檔案可能包含上個月的一些程式碼。這樣可能會導致各種意想不到的問題。

大型檔案則會帶來另一種問題。如果你增加了一個 5 GB 的 ISO 檔案或一個 10 GB 的資料集,那麼任何要複製你專案儲存函式庫到他們本地機器的人都將不得不下載那個檔案。玄貓之前曾說過 Git 是快速的,但它無法解決擁擠或緩慢網路連線帶來的問題。由於複製儲存函式庫對於 Git 使用者來說是一個非常常見操作,因此在儲存函式庫中包含巨大檔案是我們通常想避免的一件事情。通常更有意義的是把這些檔案放到 Git 外面去儲存——例如放到分享磁碟區或其他可存取到資料儲存系統中去。在你已經把大型檔案新增進去後再把它移除並不能解決問題——因為 Git 保留了每個已經加入到儲存函式庫中的檔案完整編輯歷史記錄,那個大型檔案將永遠留存在你儲存函式庫裡面,而每次有人複製 repo 到他們本地電腦時都會讓大家感到困擾。

最後就是金鑰、SSH 私鑰或者密碼等機密資訊通常不應該儲存在 Git 中了——就像玄貓之前所說明過的一樣:任何你曾經提交到儲存函式庫中的機密資料都將永遠存在於那裡面——除非你 IT 和 Git 的管理員特別小心地限制許可權才行。

排除特定型別檔案不進入版本控制

排除自動生成檔案

自動生成檔案如編譯後得到之可執行檔案、PDF 或 HTML 補充說明檔案等皆屬於自動生成檔案範疇內。 由於我們可以從原始檔重新生成它們所以完全沒有必要將它們納入版本控制系統內。 此外若把它們納入版本控制系統之中可能導致漂移問題:例如原始碼與編譯後之二進位程式碼可能不符合。 因此玄貓建議永遠不要把自動生成之程式碼納入版本控制系統之內。

排除巨大型別檔案

巨大型別如 ISO 檔、資料集等皆屬於巨大型別範疇內。 如果把巨大型別檔案納入版本控制系統中時因為要複製整份儲存函式庫至另一台電腦時需同時下載整份巨大型別檔案。 因此玄貓建議不應該將巨大型別檔案納入版本控制系統之內。

排除包含敏感資料之檔案

敏感資料如佈署金鑰、SSH 私鑰或者密碼皆屬於敏感資料範疇內。 由於我們提供給所有人的是相同版本號之版本控制系統若將包含敏感資料之程式碼放入版本控制系統當中的話代表所有人皆能取得該敏感資料。 因此玄貓建議不應該將包含敏感資料之程式碼納入版本控制系統之內。

不應被納入版本控制系統之程式碼範例(以 Python 語言為例)

# 自動生成檔案範例
import os
import subprocess

def compile_code(source_file, output_file):
    """Compile the given source file into an executable."""
    subprocess.run(['gcc', source_file, '-o', output_file])

def generate_pdf(markdown_file, pdf_file):
    """Convert a Markdown file to a PDF file."""
    subprocess.run(['pandoc', markdown_file, '-o', pdf_file])

# 巨大型別檔案範例
def download_large_file(url, output_path):
    """Download a large file from the given URL."""
    import requests
    response = requests.get(url, stream=True)
    with open(output_path, 'wb') as f:
        for chunk in response.iter_content(chunk_size=8192):
            f.write(chunk)

# 包含敏感資料之程式碼範例
def connect_to_database(host, user, password, dbname):
    """Connect to a database using the provided credentials."""
    import psycopg2
    conn = psycopg2.connect(
        host=host,
        user=user,
        password=password,
        dbname=dbname
    )
    return conn

內容解密:

以上程式碼示範如何處理三種型別被排除之程式碼:

  • 第一段程式碼 compile_codegenerate_pdf 函式示範如何編譯程式碼及轉換為 PDF 的過程。 由於編譯後及轉換後產物皆屬於自動產生之二進位及 PDF 檔所以玄貓建議不應該將此型別程式碼放置於版本控制系統當中。
  • 第二段程式碼 download_large_file 函式示範如何下載巨大型別檔。 由於下載巨大型別檔過程需要耗費許多時間且佔用許多磁碟空間所以玄貓建議不應該將此型別程式碼放置於版本控制系統當中。
  • 第三段程式碼 connect_to_database 函式示範如何連線至資料函式庫且使用金鑰及密碼進行登入。 由於金鑰及密碼皆屬於敏感資料所以玄貓建議不應該將此型別程式碼放置於版本控制系統當中。

排除特定型別檔案實務與圖示

以下圖示「此圖示」展示瞭如何將特定型別檔案排除不加入版本控制系統:

@startuml
skinparam backgroundColor #FEFEFE
skinparam componentStyle rectangle

title Git 版本控制優勢與基本操作

package "Git 版本控制" {
    package "工作區域" {
        component [工作目錄
Working Directory] as work
        component [暫存區
Staging Area] as stage
        component [本地倉庫
Local Repository] as local
        component [遠端倉庫
Remote Repository] as remote
    }

    package "基本操作" {
        component [git add] as add
        component [git commit] as commit
        component [git push] as push
        component [git pull] as pull
    }

    package "分支管理" {
        component [git branch] as branch
        component [git merge] as merge
        component [git rebase] as rebase
    }
}

work --> add : 加入暫存
add --> stage : 暫存變更
stage --> commit : 提交變更
commit --> local : 版本記錄
local --> push : 推送遠端
remote --> pull : 拉取更新
branch --> merge : 合併分支

note right of stage
  git status 查看狀態
  git diff 比較差異
end note

@enduml

「此圖示」展示了以下幾點:

  • 原始碼(Source Code):必須放置在版本控制系統當中以便追蹤變更紀錄及協作開發。
  • 生成檔(Generated Files):不應該放置於版本控制系統當中因為可以從原始碼重新產生出來且避免漂移問題發生。
  • 大型別及敏感資料(Large Files & Sensitive Data):不應該放置於版本控制系統當中以避免增加傳輸時間及無意間洩漏敏感資料風險。
未來趨勢與展望

未來隨著技術日益演進、開源社群人口越來越多且國際化程度越高化後玄貓認為以下幾點趨勢值得留意:

  1. 對於資安方面需要更嚴格措施:隨著國際化程度越高化所帶來的一系列安全問題如 Snyk Open Source Vulnerability Database 超過三百萬條漏洞紀錄實證技術工作者面臨前所未有挑戰!因此玄貓建議每家公司必須設立專門資安小組以定期進行安全風險評估及測試、並且採用 AI 自動化技術進行弱點探測與修補!
  2. 對於開源社群方面需要更多技術工作者參與:隨著國際化程度越高化所帶來的一系列互動需求如雙語介面、多平台支援等實證開源社群需要更多技術工作者參與其中!因此玄貓建議每家公司應設立專門獎勵制度以激勵更多技術工作者積極參與開源社群活動並且分享他們最新技術心得!
  3. 對於雙語介面方面需要更多支援:隨著國際化程度越高化所帶來的一系列需求如雙語介面、多平台支援等實證技術工作者必須具備良好雙語溝通能力!因此玄貓建議每家公司應設立專門培訓計畫以提供雙語溝通能力培訓課程給所有員工參與學習!