開發筆記

🌱 Git 基礎教學:從零開始學版本控制

還在手動複製貼上管理檔案版本嗎?今天就來跟大家介紹一下 Git 這個強大的版本控制工具,從核心概念到常用指令,讓輕鬆駕馭程式碼的時光機。

The Walking Fish 步行魚 頭像

· 5582 字 / 閱讀時間: 13 分鐘 · 294

文章 🌱 Git 基礎教學:從零開始學版本控制 的特色圖片

想學 Git 嗎?本篇 Git 基礎教學將帶您從零開始,一步步認識這個強大的版本控制工具。無論是程式新手還是有經驗的開發者,掌握 Git 都能讓我們在軟體開發、專案管理與團隊協作上更加得心應手。今天這篇文章將涵蓋 Git 的核心概念、安裝流程、常用指令,以及衝突解決的教學。

而之所以寫這篇文章,是因為最近已經開始上班了,但公司內部目前的軟體開發流程,還沒有全部都轉換為使用 Git 進行版本控制與協作,也還沒有內部的規範文件。考量到未來公司也還會持續有新的實習生,所以就簡單的整理一篇常用指令教學。

🔍 Git 是什麼?為什麼我需要它?

簡單來說,Git 是一個開源的版本控制系統 (Version Control System, VCS)。想像一下,它就像是專案檔案的「時光機」。每一次對專案做了有意義的修改,Git 都能幫記錄下來。

在程式開發的旅程中,我們時常會遇到需要管理不同版本程式碼的情況。如果不使用版本管理工具的話,就可能會需要使用 project_v1.js, project_v2_final.js, project_final_20230312.js 這樣的方式來管理不同版本的檔案,來讓程式改壞時,還有挽救的機會。但這樣做既費時,又容易出錯,而 Git 則可以完美的解決這些問題,甚至可以讓我知道我們每次修改了哪份文件的哪一行!

這帶來了什麼好處呢?

  1. 追溯歷史:可以隨時回到過去的任何一個版本。不小心改壞了?沒關係,輕鬆還原。
  2. 多人協作:當多個人一起開發同一個專案時,Git 能幫助整合大家的修改,避免互相覆蓋的慘劇。根據 Stack Overflow Developer Survey,超過 90% 的開發者都在使用 Git,這足以說明其在協作上的重要性。
  3. 嘗試新功能無負擔:可以開一個「分支」(Branch),在上面大膽嘗試新功能,而不用擔心影響到主要程式碼的穩定性。

我個人認為,學習 Git 是每位開發者,無論新手或資深,都應該投入時間的技能。它不僅提升效率,更是專業開發流程的基石。

當然這邊要提一下,版控工具不只 Git 一種,只是 Git 因為其開源特性、強大功能以及廣泛的社群支持,目前已被絕大多數公司採用。而我本人也基本上都使用 Git,所以才選擇介紹它。

🛠 安裝 Git

首先,在開始學習 Git 之前,我們需要在電腦上安裝 Git。

Windows/macOS/Linux

目前主流的三種作業系統,都可以直接前往 Git 官方網站下載對應作業系統的安裝程式:
https://git-scm.com/downloads

安裝過程通常很簡單,點開來一直下一步即可。安裝完畢後,可以打開的終端機 (Terminal、命令提示字元或 Git Bash),輸入以下指令來確認是否安裝成功:

1
git --version

如果能看到 Git 的版本號,就代表安裝成功了!

⚙️ 基本設定:告訴 Git 你是誰

在第一次使用 Git 時,有兩個重要的全域設定需要完成:的使用者名稱和 Email。這些資訊會被記錄在每一次提交的變更中,讓其他人知道是誰做了這些修改。

1
2
git config --global user.name "的名字或暱稱"
git config --global user.email "的Email地址"

例如,我可能會這樣設定:

1
2
git config --global user.name "The Walking Fish"
git config --global user.email "2.jerry32262686@gmail.com"

可以透過以下指令來查看目前的 Git 設定:

1
git config --list

🧪 開始使用 Git:核心流程與常用指令

接下來,我們將透過一個簡單的流程,來了解 Git 的基本操作。

1. 初始化 Git 專案 (Repository) - git init

首先,需要告訴 Git 要開始追蹤某個專案資料夾。請在終端機中,切換到的專案資料夾路徑下,然後執行:

1
git init

執行這個指令後,Git 會在目前的資料夾中建立一個隱藏的 .git 子目錄。這個 .git 目錄非常重要,它包含了 Git 用來管理版本歷史的所有資訊和中繼資料。請不要手動修改這個資料夾裡的任何內容,除非非常清楚自己在做什麼。

1.1 複製一個儲存庫 - git clone

除了使用 git init 來初始話一個儲存庫外,我們時常也會遇到專案已經在進行中的情況,這時我們一開始的動作,就會變成要先使用 git clone 將遠端的倉庫複製下來

或者是想要的話,也可以一開始就直接在 GitHub、GitLab 上,直接先創建好儲存庫,將其複製下來。

複製的指令如下:

1
git clone <遠端網址>

例如:

1
git clone https://github.com/ADT109119/gemini-api-key-manager.git

執行這個指令後,就可以看到目前專案上的檔案已經被我們複製下來,同時資料夾內也一樣會有一個隱藏的 .git 子目錄。

2. 理解 Git 的三個主要區域

在實際操作前,了解 Git 管理檔案的三個主要區域對於理解後續指令至關重要:

  • 工作目錄 (Working Directory):就是在檔案總管中看到的,實際存放專案檔案的地方。在這裡進行新增、修改、刪除檔案等操作。
  • 暫存區 (Staging Area / Index):這是一個中介區域。當對工作目錄中的檔案做了修改後,可以選擇性地將這些修改「加入」到暫存區,代表這些修改是「準備好要提交」的。我認為這是 Git 設計得非常巧妙的一點,它讓能精確控制每次提交要包含哪些內容。
  • 本地倉庫 (Local Repository):當將暫存區的內容「提交」(Commit) 後,這些變更以及一個快照就會被永久儲存在本地倉庫的 .git 目錄中,形成一個新的版本。

Git 的基本工作流程可以簡化為:在工作目錄修改檔案 -> 使用 git add 將變更加入暫存區 -> 使用 git commit 將暫存區內容提交到本地倉庫

3. 查看專案狀態 - git status

在進行任何操作之前或之後,git status 是最好的朋友。它會告訴目前工作目錄和暫存區的狀態:目前在哪個分支?哪些檔案被修改了?哪些檔案是新建立的還沒被追蹤?哪些檔案已經在暫存區等待提交?

1
git status

養成時常使用 git status 的習慣,能幫助清楚了解專案的當前狀態。

4. 將檔案變更加入暫存區 - git add

假設我們在專案中建立了一個新檔案,例如 hello.txt

1
這是一個文字文件

此時執行 git status,Git 會提示有一個未被追蹤的檔案 hello.txt。要讓 Git 開始追蹤這個檔案,並將它目前的內容加入到暫存區,我們使用 git add

1
git add hello.txt

如果修改了多個檔案,想一次將所有已追蹤檔案的變更和所有新檔案都加入暫存區,可以使用:

1
git add .

. 代表目前目錄下的所有內容。

4.1. 取消暫存:從暫存區移除檔案 - git reset HEAD (或 git restore --staged)

有時候,我們可能會不小心把不想提交的檔案加入了暫存區 (Staging Area),或者想把一個大的變更拆分成多次提交。這時,就需要將檔案從暫存區中「取消暫存」(Unstage)。

若要將特定檔案從暫存區移除 (但保留工作目錄中的修改),可以使用:

1
git reset HEAD <檔案名稱>

例如,假設我們不小心將一個暫存檔 temp.log 加入了暫存區:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
# (假設之前已經 add 了 hello.txt)
# echo "some temporary logs" > temp.log
# git add temp.log
# git status
#   # 此時會看到 hello.txt 和 temp.log 都在暫存區 (changes to be committed)

# 現在想把 temp.log 從暫存區移除
git reset HEAD temp.log
git status
#   # 此時會看到 temp.log 變回 "Changes not staged for commit" 或 "Untracked files"
#   # 而 hello.txt 依然在暫存區

執行 git reset HEAD temp.log 後,temp.log 就不在暫存區了,但它在工作目錄中的修改仍然存在。

另一個較新的指令是 git restore --staged <檔案名稱>,它也能達到相同的效果,並且在某些情境下其語義可能更為清晰,只是要注意一下這個指令 Git 的版本需要在 2.23 以上。

我個人比較習慣使用 git reset HEAD <檔案名稱> 來取消暫存,因為他比較好打w,但了解 git restore --staged <檔案名稱> 也是一個不錯的選擇,特別是當想更明確區分「取消暫存」和「還原工作目錄檔案」時 (git restore <檔案名稱>)。

5. 提交修改到本地倉庫 - git commit

當將所有想要記錄的變更都加入到暫存區後,就可以執行「提交」(Commit) 動作了。每一次提交都應該是一個有意義的變更單元,並且附帶一條清晰的提交訊息,說明這次提交做了什麼。

1
git commit -m "新增 hello.txt 檔案並加入初始內容"

-m 參數後面的文字就是提交訊息。撰寫好的提交訊息非常重要,它能讓和的團隊成員在未來回顧歷史時,快速了解每個版本做了什麼。

6. 查看提交紀錄 - git log

想看看我們都做了哪些提交嗎?git log 指令可以幫列出從最近到最遠的提交歷史。

1
git log

會看到每個提交的 SHA-1 校驗和 (一個唯一的長字串 ID)、作者、提交日期和提交訊息。如果覺得預設的 git log 資訊太多,可以試試這個:

1
git log --oneline --graph

--oneline 會讓每個提交只顯示一行,--graph 則會用 ASCII 圖形顯示分支的歷史,非常實用。

7. 再次修改與提交

版本控制的精髓就在於不斷地修改和記錄。讓我們修改一下 hello.txt 的內容:

1
2
這是一個文字文件
現在裡面有第二行

然後重複 addcommit 的流程:

1
2
3
4
git status  # 檢查狀態,會看到 hello.txt 已被修改
git add hello.txt
git commit -m "更新 hello.txt,加入第二行"
git log --oneline # 視情況查看紀錄

🧭 Git 分支 (Branch):平行開發的利器

分支是 Git 中一個非常強大的概念。它允許從主要的開發線 (通常是 mainmaster 分支) 分岔出去,在一個獨立的環境中進行工作,而不會影響到主線。這對於開發新功能、修復錯誤或進行實驗性修改非常有用。

而在實作上,通常也會將專案的主分支 main 設定為保護分支,禁止直接改動,僅允許在新功能開發完成後,通過 Pull Request,由主管審核後,合併到 main 中,或是逐層向上合併 (之後有機會在單獨寫一篇文章介紹)。

查看分支 - git branch

1
git branch

這個指令會列出本地所有的分支,並且在目前所在的分支前標上 * 號。

建立新分支 - git branch <branch-name>

假設我們要開發一個新功能,可以建立一個名為 new-feature 的分支:

1
git branch new-feature

切換分支 - git checkout <branch-name>

建立分支後,我們還需要切換到該分支上才能開始工作:

1
git checkout new-feature

現在,在這個 new-feature 分支上所做的任何提交,都只會影響這個分支,不會動到 main 分支。

較新版本的 Git (2.23+) 提供了 git switch 指令,專門用於分支切換,語義上比 git checkout 更清晰:

1
git switch new-feature

也可以使用一個組合指令來建立並立即切換到新分支:

1
git checkout -b another-feature # 建立 another-feature 分支並切換過去

合併分支 - git merge <branch-name>

當在 new-feature 分支上的工作完成並測試無誤後,通常會想將這些變更合併回 main 分支。

首先,切換回目標分支 (例如 main):

1
git checkout main

然後,執行合併指令:

1
git merge new-feature

Git 會嘗試將 new-feature 分支上的所有變更合併到目前的 main 分支。有時,如果團隊內沒有溝通好,不小心兩個分支修改了同一個檔案的相同部分,可能會發生「合併衝突」(Merge Conflict)。這時候 Git 會提示,需要手動解決這些衝突,然後再完成提交。

Git 合併衝突 (Merge Conflict) 解決方法教學

這邊為了演示一下效果,所以我手動製造了一個衝突,先在 main 分支提交一個版本,再透過 checkout 指令回到上一版進行分支並修改同一行提交。

接下來回到 main 分支,嘗試將 main 與 dev 合併,就可以看到發生衝突的訊息了

1
git merge dev

解決衝突流程

  1. 發現合併衝突訊息
  2. 使用 git status 指令查看衝突檔案
  3. 打開有錯誤的檔案(Git 會將衝突的檔案標記起來)
  4. 修改檔案,留下最後需要的程式碼
  5. 重新 addcommit 一個版本

接下來我們可以透過 git status 指令來得知是哪些檔案衝突

當我們打開該檔案後,就可以決定我們要保留哪個版本了(直接將不需要的版本,以及上下的標記刪除即可)!

如果是使用 VS Code 編輯器,它內建的小工具,還可以幫助我們快速選擇要保留的版本。

最後重新 addcommit 一個版本就可以了

以上解決衝突的操作方法,我是參考這篇文章,Chun.y.c 大大寫得很詳細,大家可以去看看!

🌐 與遠端倉庫同步 (例如 GitHub、GitLab)

到目前為止,我們所有的操作都是在「本地倉庫」進行的。但通常,我們會將專案託管到一個「遠端倉庫」上,例如 GitHub, GitLab 或 Bitbucket。這樣做的主要目的是:

  • 備份:程式碼多一份保障。
  • 協作:團隊成員可以共享和同步程式碼。

1. 連接本地倉庫到遠端倉庫 - git remote add

假設已經在 GitHub 上建立了一個空的倉庫,其 URL 是 https://github.com/的帳號/的專案名稱.git

可以使用以下指令將的本地倉庫與這個遠端倉庫連接起來,通常我們會將這個遠端倉庫命名為 origin

1
git remote add origin https://github.com/的帳號/的專案名稱.git

2. 將本地內容推送到遠端倉庫 - git push

連接好之後,就可以將本地的提交推送到遠端倉庫了。第一次推送時,通常會這樣指定:

1
git push -u origin main

-u 參數會設定本地的 main 分支去追蹤遠端的 origin/main 分支,這樣以後在 main 分支上執行 git pushgit pull 時,就不需要再指定 origin main 了。

3. 從遠端倉庫拉取更新 - git pull

如果的團隊成員在遠端倉庫上推送了新的提交,可以使用 git pull 來獲取這些更新並合併到的本地分支:

1
git pull origin main # 或者如果設定了上游分支,可以直接 git pull

🎯 常用指令速查表

指令 功能說明
git init 初始化一個新的 Git 本地倉庫
git clone <URL> 從遠端倉庫複製一份完整的專案到本地
git config --global 設定全域使用者名稱與 Email
git status 查看工作目錄與暫存區的狀態
git add <檔案> 將檔案的變更加入暫存區
git add . 將目前目錄所有變更加入暫存區
git reset HEAD <檔案> 將檔案從暫存區移除,但保留工作目錄的修改
git restore --staged <檔案> (同上) 將檔案從暫存區移除
git commit -m "訊息" 將暫存區的內容提交到本地倉庫,並附上訊息
git log 查看提交歷史紀錄
git log --oneline 以精簡方式查看提交歷史
git branch 列出所有本地分支 / 建立新分支 (若加名稱)
git checkout <分支> 切換到指定分支
git checkout -b <分支> 建立新分支並立即切換過去
git merge <分支> 將指定分支的變更合併到目前分支
git remote add <名稱> <URL> 新增一個遠端倉庫的連接
git push <遠端> <分支> 將本地分支的提交推送到遠端倉庫
git pull <遠端> <分支> 從遠端倉庫拉取更新並合併到本地分支

🎉 結語

Git 是一個功能極其強大的工具,本文所介紹的僅是其冰山一角。然而,掌握了這些基礎指令和核心概念,就已經具備了有效管理個人專案版本,以及參與團隊協作的基本能力。

當然,要熟悉 Git 的最好方式還是 多動手練習。為的每一個小專案都使用 Git,即使是個人專案,版本控制有時也可以讓我們方便在出錯時,能夠立即修改、挽救。當越熟悉 Git,就越能體會到它的優雅與強大。

如果覺得敲指令使用 Git 有些困難,其實我們還是有一些其他選擇。市面上有許多優秀的 Git GUI (圖形化介面) 工具,如 GitHub Desktop、SourceTree、GitKraken,甚至 VS Code 也內建了 source control 功能。這些工具可以讓我們透過點擊按鈕來完成大部分 Git 操作,或許能讓新手更快上手,減少挫敗感。

如果這篇文章中有哪裡寫的不夠詳細,也希望各位大佬能不吝指教!

分享這篇文章

暱稱
郵箱
網址
0/500
  • OωO
  • |´・ω・)ノ
  • ヾ(≧∇≦*)ゝ
  • (☆ω☆)
  • (╯‵□′)╯︵┴─┴
  •  ̄﹃ ̄
  • (/ω\)
  • ∠( ᐛ 」∠)_
  • (๑•̀ㅁ•́ฅ)
  • →_→
  • ୧(๑•̀⌄•́๑)૭
  • ٩(ˊᗜˋ*)و
  • (ノ°ο°)ノ
  • (´இ皿இ`)
  • ⌇●﹏●⌇
  • (ฅ´ω`ฅ)
  • (╯°A°)╯︵○○○
  • φ( ̄∇ ̄o)
  • ヾ(´・ ・`。)ノ"
  • ( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
  • (ó﹏ò。)
  • Σ(っ °Д °;)っ
  • ( ,,´・ω・)ノ"(´っω・`。)
  • ╮(╯▽╰)╭
  • o(*////▽////*)q
  • >﹏<
  • ( ๑´•ω•) "(ㆆᴗㆆ)
  • 😂
  • 😀
  • 😅
  • 😊
  • 🙂
  • 🙃
  • 😌
  • 😍
  • 😘
  • 😜
  • 😝
  • 😏
  • 😒
  • 🙄
  • 😳
  • 😡
  • 😔
  • 😫
  • 😱
  • 😭
  • 💩
  • 👻
  • 🙌
  • 🖕
  • 👍
  • 👫
  • 👬
  • 👭
  • 🌚
  • 🌝
  • 🙈
  • 💊
  • 😶
  • 🙏
  • 🍦
  • 🍉
  • 😣
  • 颜文字
  • Emoji
  • Bilibili
1 則留言
raccoon

IMG_9485

謝謝走路魚哥的分享 :tv_偷笑: