Git & Github
首先,Git 是一款分散式版本控制系統,由 Linux 之父 Linus Torvalds 於 2005 年開發,主要用於追蹤程式碼的歷史紀錄(commit),讓開發者可以隨時回溯、還原或合併不同版本的程式碼。
而 Github 是用來儲存與管理專案的遠端平台,開發者可以在上面建立公開或私有儲存庫,並且有版本控制、團隊協作等功能。在這裡你可以看到許多知名的開源專案,如 Linux Kernel、Vue、Gemini Cli,開發者不僅能參考這些專案的標準,還能參與開源專案,甚至有開發者因為對 Vue 專案有不少的貢獻,使得 Even You 發推文推薦開發者,進而拿到不錯的 offer。
Installation
Workflow
在一個個人專案的開發過程中,你需要先在 Github 上面建立一個儲存庫,接著把它下載(clone)到本地進行開發。由於開發過程會有好幾個階段或版本,所以會先建立分支(branch)來開發各種 feature 或是修 bug,同時不影響主分支(main)的功能。當開發完成一部分(例如寫好網站的前端),就會把當前分支合併到主分支上。此外,在分支或功能的開發過程中,可能會拆分成好幾個小任務,當完成一個小任務時就會建立一個紀錄點(commit),如果之後開發不順,可以退回到這個紀錄點,重新開始。
- 想要新增一個功能(大任務) -> 建立開發分支 -> 完成一部分功能(小任務) -> 建立紀錄點(commit) -> … -> 建立完成功能 -> 合併分支
Basic Command
這邊有個視覺化 git 操作的專案,並且有提供網頁版可以操作。
- 初始化 git 的設定,設定 github 的個人資訊,讓平台能識別身分。在終端機(terminal)或 Git Bash 輸入以下指令
git config --global user.name "<GITHUB_USERNAME>" # 設定全域 usernamegit config --global user.email "<GITHUB_EMAIL>" # 設定全域 email-
點擊主頁左上角的綠色按鈕(New),填好必要資訊後建立儲存庫
-
把儲存庫下載到本地,以方便在電腦上開發
git clone <REPO_URL> # https://github.com/SoWiEee/AstroBlog.git- 預設是在主分支(main),我們可以建立另一個分支進行開發,也能查看已建立的分支
git branch <BRANCH_NAME> # 建立 dev 分支 (1)git checkout dev # 切換到 dev 分支 (2)git branch # 查看所有分支
git checkout -b dev # 建立 dev 分支並切換過去 (1)(2)git switch自從 Git 2.23 版本就新增了
git switch指令,主要用於切換分支。雖然目前仍可以使用git checkout來做,但這能讓指令有明確的分工並增加易用性。以下簡單介紹這個指令的用法:git switch -c dev # git checkout -b dev
- 新增完程式碼後,可以比對修改前後的差異,沒問題後就把檔案加入暫存區,讓 git 知道你有東西想要 commit
git diff # 查看修改後和 git 紀錄的差異git add <CHANGED_FILE> # 把修改過的檔案加入暫存區- 新增紀錄點,並上傳到 github 上的 dev branch
git commit -m "<MESSAGE>" # 建立紀錄點及變動訊息git push origin dev # 把該變動上傳到遠端的 dev 分支- 嘗試合併到主分支,但是更多時候其他開發者已經在 main 上面更新了,所以要確認是否能在 updated main 套上自己的 commit
git checkout main # 先把本地的主分支回溯到未更新的狀態git pull origin main # 再加上遠端別人的更新
git checkout dev # 切換到 dev 分支git rebase main # 先換成 updated main,再套上自己的 commitrebase conflict可能 rebase 的時候會有衝突,此時需要選擇保留哪幾行程式碼。
- 把最終版本上傳到遠端分支
git push -f origin dev # 把變動上傳到遠端的 dev 分支- (開發者)發出 Pull Request 告知專案擁有者,請求合併到主分支,通常會做 Squash and Merge,這能保持主分支的 commit history 更加整潔(把開發分支的所有改變合併成一個 commit),最後把開發分支刪掉。附帶一提,若不小心刪除分支,在指令下方會有 deleted branch 最新的 commit SHA1,我們可以使用
git branch -b <BRANCH> <COMMIT_SHA1>來復原誤刪的分支。
git checkout main # 切換到 main 分支git branch -D dev # 把 dev 分支刪掉git pull origin main # 把最後更新的 main 套用到本地- 繼續建立開發分支開發新功能
Restore Command
在回復檔案的狀態前,必須根據文件當前的狀態來決定怎麼做,才能使用正確的指令復原。我們可以使用 git status 來查看文件的狀態。
如果只修改程式碼,此時狀態為:Changes not staged for commit。想要把程式復原到修改前的狀態,可以使用 git restore <CHANGED_FILE> 回復到 git 紀錄的版本狀態。
在使用 git add 之後,狀態會變成 Changes to be committed。想要把這個修改從 git 紀錄(staging)移除,並保留本地文件的內容,可以使用 git reset <CHANGED_FILE> 或 git restore --staged <CHANGED_FILE>。如果這時候又想把當前文件回溯到未修改的狀態,要使用 git checkout HEAD <CHANGED_FILE>,這樣就可以同時撤銷 commit 和文件的修改。
git checkout除了
git checkout <BRANCH>這種格式,還能使用git checkout <COMMIT_SHA1>,這是用來查看不同的 commit 版本,其中 commit 的 SHA-1 編號可以使用git log查詢。此外,HEAD預設是會跟著最前面的 commit 走,指向最近的 commit,在使用上述指令切換 commit 時,就是變動HEAD指向的地方。git checkout <COMMIT_SHA1> # 切換到 COMMIT_SHA1 對應的 commit 上面git checkout <BRANCH> # 切換到 BRANCH 的最新狀態
在使用 git commit 之後,修改會被 git 記錄下來。若想要撤銷該 commit,要使用 git reset --soft HEAD~1,因為目前 HEAD 指向該 commit,而 HEAD~1 表示前一個 commit。若沒給 --soft 這個 option,則會撤銷 commit 和 staging 的紀錄,也就是回到 git add, commit 之前的狀態。此外還有一種等價的操作 git revert HEAD,這是再加一個用來抵銷當前 commit 的 commit,但更常用的情境是抵銷前面某個 commit 的修改,如 git revert HEAD~1。
在使用 git push 之後,修改會上傳到 github。此時若為公有分支,就要顧及其他協作者的感受,只增不減,要使用 git revert HEAD 再 git push 就可以。反之如果是個人分支,則使用 git reset --hard HEAD~1 再 git push -f,這能讓自己的 commit 更加簡潔。
懶人包
- 取消對程式碼的修改:
git restore <CHANGED_FILE>- 取消
git add操作:git reset <CHANGED_FILE>- 取消 1. 2. 的操作:
git checkout HEAD <CHANGED_FILE>- 取消
git commit操作:git reset --soft HEAD~1- 取消 2. 4. 的操作:
git reset HEAD~1- 取消 1. 2. 4. 的操作:
git reset --hard HEAD~1- 抵銷 1. 2. 4. 的操作:
git revert HEAD- (公有)取消
git push的操作:git revert HEAD->git push- (個人)取消
git push的操作:git reset --hard HEAD~1->git push -f
Commit Message Rule
如何撰寫 commit message 也是一門學問,除了要記錄工作內容,還要讓其他合作者或未來的你很容易了解這段在做甚麼。
Basic Rule
一個好的 commit message 為了能讓開發者瞭解這個提交版本,應該要包含 What & Why & How:
- 做了什麼事情 (what)
- 為什麼要做這件事情 (why)
- 用什麼方法做到的 (how)
在團隊中,要有一定的 commit log 風格及內容,我們可透過遵守現有的慣例來實現。 一個 Commit Message 主要由 Header + Body + Footer 組成:
<type> [optional scope]: <subject>
[optional body]
[optional footer]Header
type代表 commit 的類別,如 feat, fix, docs, style, refactor, test, chorescope代表 commit 影響的範圍,如資料庫、控制層、服務層,視專案不同改變subject代表 commit 的簡短描述,通常不超過 50 個字元,且結尾不用句號
懶人包盡量讓 commit 單一化,一次只更動一個主題!
type
- build:會影響建構系統或外部依賴的變動,如 gulp, broccoli, npm
- feat:新增或修改功能(feature)
- fix:修補漏洞或邏輯錯誤(bug fix)
- docs:文件相關(documentation)
- style:格式,不影響程式碼運行的變動,如 white-space, formatting, missing semi colons
- refactor:重構,不是新增功能,也非修補 bug 的程式碼變動
- perf:改善效能
- test:增加測試
- chore:意同 maintain,不影響程式碼運行、建構程序或輔助工具的變動,例如修改 config、Grunt Task 任務管理工具
- revert:撤銷回覆先前的 commit,如 revert:type(scope)
Body (optional)
- 對本次 commit 的詳細描述,解釋 What & Why & How
- 可以分成多行,每一行不超過 72 個字元
- 說明程式碼變動的項目與原因,還有與先前行為的對比
Footer (optional)
- 填寫任務編號,如 issue #1246
Merge Branch
當有兩個以上的分支想要將內容合併,就會使用到 git rebase 及 git merge。這兩個指令的差異在合併的對象,由於牽扯到版本移動的問題,所以需要先區分清楚它們的運作方式。
git rebase 的功能是將某個分支的提交歷史,重新建立在另一個分支的後面,彷彿把你的工作放到別人更新後的基礎上重做一遍。例如:目前你在 dev 分支上開發功能,但在此期間 main 分支已經被其他協作者更新。這時候可以使用:
git checkout devgit rebase main這樣會讓 dev 分支的 commit 重新套用在最新的 main 之後,產生線性歷史(commit),讓 commit log 看起來更加整齊。
warning若多人正在使用同一個分支進行開發,切勿對該分支進行 rebase 操作,這會導致 commit ID 改變,引發衝突!
git merge 則是保留兩個分支的開發紀錄,並產生一個新的 merge commit 作為交會點。
git checkout devgit merge dev這樣會將 dev 的修改合併進 main,並保留兩邊的歷史。
Advance Feature
Fork
開發者可以使用 fork 來複製一個公開儲存庫到自己的帳號,並進行修改或開發。想要為開源專案貢獻程式碼時,不需要擁有者授權就可以 Fork 一份進來修改,之後再發 PR 給專案維護者。
- 點選某個儲存庫的 Fork 按鈕(右上角)
- github 會建立一個副本到你的儲存庫
- git clone 這個儲存庫
- 修改並 push 到自己的儲存庫
- 發送 Pull Request,請求原作者合併
Issue
Issue 是 Github 提供的問題追蹤機制,可以讓使用者
- 回報 bug
- 提出新功能的建議
- 討論設計思路
使用 Issue 可以建立一套清楚的任務管理流程,也有很多 Repo 搭配 labels 來標記每個 issue 的類型(如 bug, enhancement, question 等),並使用 milestone 管理進度。
Action
Github Actions 是一種 CI/CD 工具,可以在 push 或 pull request 發生時自動執行特定流程。
- Push 時自動跑測試
- Pull Request 發生時跑 lint 或部署
- 自動部署到 Github Pages、Vercel、Cloudflare 等平台
Usage
在 .github/workflows/ 下建立 .yml 檔案,例如:
name: Build and Test
on: [push, pull_request]
jobs: build: runs-on: ubuntu-latest
steps: - uses: actions/checkout@v2 - name: Set up Node.js uses: actions/setup-node@v3 with: node-version: '18' - run: npm install - run: npm run test由於這部分我很少接觸,像是這個 blog 就是用 cloudflare page + action 維護的,可以參考這個檔案。想了解更多玩法可以參考官方文件或是這篇文章。