Skip to content

Git:程式碼的時光機

💡 學習指南:這一章專門寫給完全沒用過 Git 的人。我們不會一上來就讓你背指令,而是先搞清楚「Git 到底在幫你解決什麼問題」,再一步步把指令和概念串起來。讀完後,你應該能獨立完成:本地提交、建立分支、推送到 GitHub。


0. 先問一個問題:你有沒有經歷過這些噩夢?

場景一:版本地獄

你寫論文或寫程式,改到一半發現改錯了,想回到三天前的版本——但你找不到了。

專案_v1.zip
專案_v2_修改版.zip
專案_v3_最終版.zip
專案_v3_最終版_真的最終版.zip
專案_v3_最終版_打死不改了.zip

每次存一個新副本,硬碟越來越亂,而且你根本記不住哪個版本改了什麼。

場景二:協作噩夢

你和隊友同時改同一個檔案:

  • 你改了第 10 行,新增了登入功能
  • 隊友改了第 10 行,修復了一個 Bug
  • 你們用電子郵件互傳程式碼,結果合併時一個人的改動被另一個人覆蓋了
  • 沒人知道最後哪段程式碼是對的

場景三:沒有「後悔藥」

你在正式環境部署了新程式碼,結果出 Bug 了,想緊急回退到上一個穩定版本——但你不知道怎麼回退,只能手忙腳亂地找備份。


Git 就是為了解決這三個問題而生的。

Git 是一個版本控制系統(Version Control System)。它的本質是:把你每一次「存檔」操作都記錄下來,形成一條完整的歷史時間線,讓你可以隨時回到任意一個歷史節點。

不誇張地說,Git 是現代軟體開發最重要的工具之一。幾乎所有的公司、所有的開源專案都在用它。


1. Git 和 GitHub 是一回事嗎?

很多初學者會混淆這兩個概念,先澄清一下:

GitGitHub
是什麼一個執行在你電腦上的版本控制工具一個存放 Git 儲存庫的網站(雲端)
在哪裡你的本地電腦網路上
能獨立使用嗎✅ 可以,只管理本地歷史❌ 需要配合 Git 使用
類比你本地的日記本存日記的雲端硬碟

簡單說:Git 是工具,GitHub 是託管服務。 就像 Word 是工具,OneDrive 是雲端硬碟一樣,兩者配合使用,但並不是同一個東西。

除了 GitHub,類似的服務還有 GitLab、Gitee(國內)等。


2. 核心概念:三個區域

這是整個 Git 最重要的設計,理解了這三個區域,你就理解了 Git 的靈魂。

Git 把你的檔案狀態分成三層:

工作區(Working Directory) 就是你的普通資料夾,你現在看到的、正在編輯的所有檔案都在這裡。你隨便改,Git 會感知到你改了什麼,但不會做任何記錄。

暫存區(Staging Area / Index) 這是一個「預備提交」的中轉站。你可以把工作區裡想要儲存的檔案「放進」暫存區,就像把快遞放進快遞盒——還沒寄出去,但已經選好了要寄什麼。

儲存庫(Repository) 這是永久存檔的歷史記錄庫,藏在 .git 資料夾裡。每次你執行 git commit,暫存區裡的內容就會被封存進儲存庫,形成一條不可竄改的歷史記錄。

👇 動手試試看:依次點擊指令按鈕,觀察檔案在三個區域之間的流轉。

~/project (main)
# You changed 3 files. This demo shows the add -> commit flow
$
📝Working treeWorking Directory
files you are editing
Changes not staged for commit:
Mlogin.jsunstaged
Mstyle.cssunstaged
Mdebug.logunstaged
git add
📦Staging areaStaging Area
files prepared for this commit
Changes to be committed:
(empty)
git commit
🗄️RepositoryRepository (.git)
versions saved permanently
Committed history (git log):
9f3e1b2init: initialize projectHEAD
💡 Click the command buttons in order and watch files move across the three areas.

為什麼要「兩步走」(add + commit)?

很多初學者會問:為什麼不能直接一鍵儲存,非要先 addcommit

因為現實開發中,你經常不想把所有改動都一起提交。

舉個例子:你今天改了 5 個檔案:

  • login.js:完成了登入功能(想提交)
  • style.css:調整了登入頁樣式(想提交)
  • debug.log:臨時除錯輸出(不想提交)
  • experiment.js:正在測試的新功能,還沒完成(不想提交)
  • todo.txt:你的個人備忘(不想提交)

如果沒有暫存區,你要麼把這 5 個檔案全部提交(提交記錄很混亂),要麼一個都不提交。

有了暫存區,你可以精確控制:git add login.js style.css,只把這兩個檔案放進快遞盒,然後 commit,這次提交就清清楚楚地記錄「登入功能完成」。


3. 第一次使用 Git:初始化和基礎工作流程

3.1 安裝和初始化

安裝好 Git 後(macOS 自帶,Windows 去 git-scm.com 下載),開啟終端機,進入你的專案資料夾:

bash
# 在目前資料夾初始化一個 Git 儲存庫
git init

# Git 會建立一個隱藏的 .git 資料夾,所有歷史記錄存在裡面
# 輸出:Initialized empty Git repository in .../your-project/.git/

第一次使用還需要告訴 Git 你是誰(這個資訊會附在每次提交記錄上):

bash
git config --global user.name "你的名字"
git config --global user.email "你的電子郵件"

3.2 日常工作流程:三步存檔

初始化之後,日常開發 90% 的操作就是反覆執行這三步:

第一步:檢視狀態

bash
git status

這是你用得最多的指令,沒有之一。它告訴你:

  • 你在哪個分支上
  • 哪些檔案被修改了(紅色 = 未暫存)
  • 哪些檔案在暫存區裡(綠色 = 已暫存,等待提交)

第二步:把檔案放進暫存區

bash
# 新增單一檔案
git add login.js

# 新增多個檔案
git add login.js style.css

# 新增目前資料夾裡所有修改過的檔案(用 . 表示「全部」)
git add .

⚠️ 初學者常見誤區:git add . 非常方便,但會把所有修改都加進去,包括你不想提交的臨時檔案。養成精確 add 的習慣,或者用 .gitignore 排除不想追蹤的檔案(後面會講)。

第三步:提交,寫上說明

bash
git commit -m "feat: 新增使用者登入功能"

-m 後面引號裡的內容叫做 commit message(提交說明)。這是寫給未來的自己和隊友看的,要寫得有意義。

3.3 Commit Message 怎麼寫才專業?

bash
# ❌ 沒用的寫法——看了不知道做了什麼
git commit -m "update"
git commit -m "fix"
git commit -m "改了一些東西"

# ✅ 好的寫法:類型 + 冒號 + 一句話描述
git commit -m "feat: 新增使用者登入功能"
git commit -m "fix: 修復首頁在 iOS Safari 上的白畫面問題"
git commit -m "docs: 更新 README 中的部署說明"
git commit -m "refactor: 將 UserService 拆分為獨立模組"
git commit -m "style: 統一程式碼縮排為 2 空格"

常用前綴含意:

前綴含意
feat:新功能(feature)
fix:修復 Bug
docs:文件改動
style:程式碼格式調整(不影響功能)
refactor:程式碼重構(功能不變,結構最佳化)
chore:建構、工具、依賴相關
test:測試相關

養成這個習慣,幾個月後翻歷史記錄,一眼就知道每次提交做了什麼。這在團隊協作中尤其重要。

3.4 檢視歷史記錄

bash
# 詳細格式(每次提交的完整資訊)
git log

# 簡潔格式(每行一條,推薦日常使用)
git log --oneline

# 範例輸出:
# a1b2c3d (HEAD -> main) feat: 新增使用者登入功能
# 9f3e1b2 init: 專案初始化

4. 平行宇宙:分支(Branch)

分支是 Git 最強大、也是最讓初學者困惑的功能。但理解了它之後,你會發現這個設計非常優雅。

4.1 分支是什麼?用「平行宇宙」來理解

想像你在玩一個角色扮演遊戲,遊戲裡有一個關鍵選擇:

  • 選擇 A:去挑戰大 Boss(開發新功能)
  • 選擇 B:繼續穩定當前局面(主線不動)

如果你直接在主存檔上做選擇 A,萬一失敗了,整個遊戲進度就毀了。

但如果你複製一個存檔,在副本裡去挑戰 Boss:

  • 打贏了?把副本的成果合併回主存檔
  • 打輸了?主存檔完全沒有影響,刪掉副本重來

Git 分支就是這個「副本存檔」機制。

在 Git 裡,main(或 master)分支是你的「主存檔」,永遠保持穩定可用。當你要開發新功能時,你從 main 建立一個新分支,在那裡開發、測試,完成後再合併回 main。

4.2 分支的視覺化演示

👇 動手試試看:依次點擊指令按鈕,觀察下方分支圖如何分叉、延伸、最終合併。重點關注 HEAD 標籤的位置變化——它始終指向「你目前在哪裡」。

~/project (main)
# main already has 2 commits. Follow the steps to see branching
$
main branchHEAD your current position
9f3e1b2initHEADc4d8a31homemain
💡 Click the command buttons in order and watch the branch graph change

4.3 分支操作詳解

建立並切換到新分支:

bash
# 方式一:先建立,再切換(兩步)
git branch feature-login      # 建立分支
git checkout feature-login    # 切換過去

# 方式二:一步到位(推薦)
git checkout -b feature-login

# 輸出:Switched to a new branch 'feature-login'

建立分支後,你的命令列提示符會顯示目前分支名,比如:

user@mac ~/project (feature-login) $

檢視所有分支:

bash
git branch

# 輸出(* 表示目前所在分支):
# * feature-login
#   main

在分支上正常開發:

bash
# 在 feature-login 分支上,改程式碼、add、commit,和平時完全一樣
git add login.js
git commit -m "feat: 新增登入表單 HTML 結構"

git add login.js api.js
git commit -m "feat: 完成登入介面對接"

這些提交只在 feature-login 分支上,main 分支完全不知道你做了什麼。

切回主分支,合併:

bash
# 切回 main
git checkout main

# 把 feature-login 的所有改動合併進來
git merge feature-login

# 合併完成後,可以刪掉這個分支(可選)
git branch -d feature-login

4.4 什麼時候該開分支?

場景建議理由
開發一個新功能✅ 開分支功能完成前不影響主線,隨時可以放棄
修復線上緊急 Bug✅ 從 main 開 hotfix-xxx 分支修完直接合併上線,不帶入未完成的功能
和隊友平行開發✅ 各自開分支互不干擾,完成後統一透過 Pull Request 合併
只改一個錯字❌ 直接在 main 改風險極低,沒必要額外開分支

4.5 團隊常用的分支策略

在實際專案中,團隊通常會約定好分支的命名和用途:

分支名用途特點
main / master正式環境的穩定程式碼只有測試通過的程式碼才能進來,不能直接推送
dev / develop日常整合分支所有功能分支先合併到這裡,測試通過再上 main
feature/xxx具體功能開發feature/user-login,完成後合併到 dev
hotfix/xxx緊急修復從 main 建立,修完直接合併回 main 和 dev

5. 與隊友協作:遠端儲存庫

到目前為止,你學的都是本地的 Git 操作——所有歷史記錄都存在你自己的電腦上。要和隊共享程式碼,你需要一個遠端儲存庫,也就是 GitHub、GitLab 這樣的雲端儲存。

5.1 遠端儲存庫的運作原理

可以把遠端儲存庫理解為團隊共用的「公共存檔」

  • 每個人在本地寫程式碼、commit
  • 寫完後 push(上傳)到遠端儲存庫
  • 隊友 pull(下載)遠端儲存庫的最新內容到自己本地
  • 這樣大家的程式碼就保持同步了

👇 動手試試看:依次點擊指令,體驗從關聯遠端儲存庫、推送到拉取隊友更新的完整流程。

~/project (main)
# Local repository has 2 commits and no remote yet
$
💻Local repository~/project
9f3e1b2init: initialize project
c4d8a31feat: homepage layout
push →
← pull
☁️Remote repositorygithub.com/you/project
(empty)
💡 Click the command buttons below in order

5.2 第一次推送專案到 GitHub

第一步:在 GitHub 上建立一個新儲存庫(點擊右上角 + → New repository),不要勾選初始化選項。

第二步:回到本地終端機,關聯遠端儲存庫:

bash
# 把本地儲存庫和 GitHub 上的儲存庫關聯起來
# "origin" 是遠端儲存庫的別名,是約定俗成的名字(也可以改,但沒必要)
git remote add origin https://github.com/你的使用者名稱/儲存庫名.git

# 確認關聯成功
git remote -v
# 輸出:
# origin  https://github.com/你的使用者名稱/儲存庫名.git (fetch)
# origin  https://github.com/你的使用者名稱/儲存庫名.git (push)

第三步:推送本地內容到遠端:

bash
# 第一次推送,-u 的意思是「以後 git push 時,預設推到 origin 的 main 分支」
git push -u origin main

# 之後每次推送只需要:
git push

5.3 日常協作的指令

推送(你改了東西,要讓隊友看到):

bash
git push

拉取(隊友改了東西,你要同步):

bash
git pull

git pull 實際上是兩個指令的組合:

  1. git fetch:先去遠端儲存庫下載最新的提交記錄
  2. git merge:把下載回來的內容合併到你目前的分支

第一次從 GitHub 獲取別人的專案:

bash
# 把整個遠端儲存庫複製到本地(只需要做一次)
git clone https://github.com/某人/某專案.git

# clone 會自動建立與遠端的關聯,之後直接 push/pull 就行

5.4 push 和 pull 的方向

你的電腦(本地儲存庫)  ←→  GitHub(遠端儲存庫)

git push:  本地 → 遠端   (你改了東西,上傳給隊友)
git pull:  遠端 → 本地   (隊友改了東西,下載到你這裡)
git clone: 遠端 → 本地   (第一次完整複製整個儲存庫)

最佳實踐:每天開始工作前先 git pull,拿到最新程式碼;下班或完成一個功能後 git push,及時備份並讓隊友看到你的進展。


6. 進階:處理衝突

衝突是協作中不可避免的,但也沒那麼可怕。

6.1 衝突是怎麼發生的?

當你和隊友同時修改了同一個檔案的同一行,在合併時 Git 不知道該用誰的版本,就會產生衝突。

舉個例子:

  • 你在 login.js 第 5 行寫了:const timeout = 3000
  • 隊友同時在同一行寫了:const timeout = 5000
  • 當你 git pullgit merge 時,Git 發現了這個矛盾,就會「暫停」並告訴你:我不知道該用哪個,你來決定。

6.2 衝突檔案長什麼樣?

Git 會在衝突的地方插入特殊標記:

javascript
function login() {
  const url = '/api/login'

 <<<<<<< HEAD
  const timeout = 3000   // 你的版本
 =======
  const timeout = 5000   // 隊友的版本
 >>>>>>> feature/update-timeout

  return fetch(url, { timeout })
}
  • <<<<<<< HEAD======= 之間:是你目前分支的內容
  • =======>>>>>>> xxx 之間:是合併過來的內容

6.3 如何解決衝突?

第一步:開啟衝突檔案,找到所有 <<<<<<< 標記(通常 VS Code 等編輯器會自動高亮)

第二步:決定保留哪段程式碼,然後手動編輯檔案,刪掉所有標記符號(<<<<<<<=======>>>>>>>)。

比如決定用 5000(隊友的版本):

javascript
function login() {
  const url = '/api/login'
  const timeout = 5000   // 採用隊友的修改
  return fetch(url, { timeout })
}

第三步:重新提交

bash
# 標記衝突已解決
git add login.js

# 完成合併提交(Git 會自動產生合併提交資訊)
git commit

6.4 減少衝突的好習慣

  • 勤 pull:開始工作前同步最新程式碼,減少「你落後太多」的情況
  • 小步提交:不要寫了一週程式碼才一次性提交,頻繁小提交更容易發現和解決衝突
  • 分支隔離:不同功能用不同分支,減少對同一行程式碼的競爭
  • 溝通:要改公共檔案(比如 config.js)前,跟隊友打個招呼

7. 常用指令速查

Save this table and check it whenever a Git command slips your mind:

Usage frequency
543210git status — Very frequentgit add <file> — Before each commitgit add . — Very frequentgit commit -m "..." — Very frequentgit push — Very frequentgit pull — Very frequentgit log --oneline — Frequentgit checkout -b <branch> — Frequentgit checkout <branch> — Frequentgit clone <url> — Frequentgit branch — Mediumgit merge <branch> — Mediumgit stash — Mediumgit stash pop — Mediumgit reset HEAD~1 — Mediumgit diff — Mediumgit branch -d <branch> — Lowgit init — Once at project startgit remote add origin <url> — At project setupgit statusCheck working tree and staging area statusgit add <file>Put a specific file into the staging areagit add .Stage all changed filesgit commit -m "..."Commit staged content with a messagegit pushPush commits to the remote repositorygit pullPull the latest remote changesgit log --onelineView a compact commit historygit checkout -b <branch>Create and switch to a new branchgit checkout <branch>Switch to an existing branchgit clone <url>Clone a remote repository locallygit branchList local branchesgit merge <branch>Merge a branch into the current branchgit stashTemporarily save uncommitted changesgit stash popRestore the previous stashgit reset HEAD~1Undo the latest commit while keeping changesgit diffInspect detailed file differencesgit branch -d <branch>Delete a merged branchgit initInitialize a Git repository in the current directorygit remote add origin <url>Connect a remote repository once
Command (scroll horizontally)

8. 實戰:加入一個團隊專案的完整流程

這是你加入新團隊或新專案時的標準操作流程,可以直接照抄:

bash
# ① 第一天:把專案 clone 到本地(只做一次)
git clone https://github.com/team/project.git
cd project

# ② 每天開始工作:先拉取最新程式碼,確保你的程式碼是最新的
git pull origin main

# ③ 建立自己的功能分支(不要直接在 main 上改)
git checkout -b feature/user-profile

# ④ 正常開發...寫程式碼...

# ⑤ 完成一個小功能點後,立即提交(不要攢著)
git add src/UserProfile.vue
git commit -m "feat: 完成使用者頭像上傳功能"

git add src/UserProfile.vue src/api/user.js
git commit -m "feat: 完成使用者資料編輯介面"

# ⑥ 把自己的分支推送到遠端,讓隊友能看到
git push origin feature/user-profile

# ⑦ 在 GitHub 上建立 Pull Request(PR),請求合併到 main
# (這步在 GitHub 網頁上操作)

# ⑧ 等隊友 Code Review,按回饋修改,繼續 commit + push

# ⑨ PR 合併後,回到 main,更新本地,刪掉功能分支
git checkout main
git pull
git branch -d feature/user-profile

9. .gitignore:哪些檔案不應該被追蹤?

有些檔案你不想提交到 Git 儲存庫裡,比如:

  • node_modules/:依賴套件,體積巨大,可以用 npm install 重新產生
  • .env:環境變數檔案,裡面可能有資料庫密碼、API Key,絕對不能上傳到公開儲存庫
  • *.log:日誌檔案
  • .DS_Store:macOS 自動產生的隱藏檔案
  • dist/build/:編譯產物,可以重新建構

在專案根目錄建立一個 .gitignore 檔案,寫上不想追蹤的檔案規則:

gitignore
# 依賴套件
node_modules/

# 環境變數(重要!密碼不能提交)
.env
.env.local

# 建構產物
dist/
build/

# 系統檔案
.DS_Store
Thumbs.db

# 日誌
*.log

GitHub 上有各種語言和框架的 .gitignore 範本:github.com/github/gitignore


名詞速查表

名詞英文解釋
儲存庫Repository (Repo)存放專案所有版本歷史的資料庫,在 .git 資料夾裡
提交Commit一次完整的版本記錄,像遊戲存檔點,附有說明和時間戳
分支Branch獨立的開發線,像平行時間線,互不影響
合併Merge把一個分支的改動整合到另一個分支
衝突Conflict同一行程式碼被多人修改,Git 不知道該用哪個,需要手動解決
暫存Stage / Index把修改放入「準備提交」清單的操作
遠端Remote雲端的儲存庫副本(GitHub / GitLab / Gitee)
複製Clone把整個遠端儲存庫完整複製到本地
推送Push把本地提交上傳到遠端儲存庫
拉取Pull把遠端最新內容下載並合併到本地
HEADHEAD目前所在分支/提交的指標,表示「你現在在哪裡」
originorigin遠端儲存庫的預設別名(約定俗成的名字)
stashStash臨時儲存還沒 commit 的改動,切換任務時用
PR / MRPull Request / Merge Request請求把你的分支合併進主分支,通常需要隊友 review