Git 全景課 / 結業專題

結業專題

把十五個單元放進一個真實的倉庫裏用一次。兩條路線任選一條;兩條都要經過完整的 PR 審查,main 受保護,最後打一個標籤作為交付版本。
約 2–3 週三人小組(軟件線)或個人(知識線)先修:M0–M14

1兩條路線 Two tracks

軟件線知識線
做甚麼三人小組協作開發一個小工具(例如命令列記帳、課表產生器)為一門課或一個主題建立版本化知識庫
協作方式每人至少兩個 PR,並審查別人至少兩個 PR自己的改動全部走 PR;另接受至少一次外部貢獻(別人 fork 後提交的 PR)
審查要求每個 PR 至少一位組員批准才能合併每個 PR 至少一位審查者批准才能合併。審查者可以是同學、導師或同事,要加為倉庫的協作者(只有具寫入權的人,批准才算數)
正本控制main 受保護:必須 PR、至少一人批准、CI 通過、禁止強制推送和刪除main 受保護:必須 PR、至少一人批准、CI 檢查連結、禁止強制推送和刪除
最低規模合併的 PR ≥ 6(每人 ≥ 2);每人 ≥ 8 個 commit(用 squash 合併時,以各人 PR 裏的 commit 計);issue ≥ 6筆記 ≥ 15 篇;合併的 PR ≥ 6,其中 ≥ 1 個來自外部貢獻
重點單元M4–M11、M14M6、M9–M12、M14

知識線找不到審查者、只能一個人做?見第 5 節「單人變通版」。軟件線必須是三人小組。

合併方式 建議用合併 commit(merge commit)或 rebase 合併,保留每人的 commit,評核時才看得到個人貢獻和 rebase -i 的成果。如果用 squash 合併,main 上每個 PR 只剩一個 commit(M7 squash 合併),個人貢獻改以 PR 列表為準。

2建議節奏 Suggested pacing

以下是三週的建議分配,小組和自學者都適用。不設硬性日期,按自己的進度調整;重點是先把閘門裝好,再開始做內容。

階段完成甚麼完成的標誌
第 1 週
搭好正本
建倉庫、README、.gitignore;設定 main 的保護規則和 CI;拆 issue、分工;加 PR 範本有人試著直推 main,被拒絕;第一個 PR 經審查合併
第 2 週
協作
大部分 PR;製造並解決一次衝突;rebase -i;worktree;(知識線)兩個 clone 的分歧每人都有已合併的 PR 和寫過的審查;過程記錄開始累積
第 3 週
收尾與發佈
bisect/log -S、hook、救回錯誤;寫完過程記錄和反思;打 v1.0 並建立 release檢核清單全部完成;第 9 節的提交內容齊全

3軟件線檢核清單 Software track

每一項下面的灰字是「證據放在哪裏」。評核者看不到你的勾選,只看倉庫裏的證據;做完一項,就把證據寫進 README 的「過程記錄」(範本見第 7 節)。

軟件線

完成一項勾一項。勾選只存在你這部電腦的瀏覽器裏,不會交給評核者。

    4知識線檢核清單 Knowledge track

    同樣,灰字是證據的位置。

    知識線

    完成一項勾一項。勾選只存在你這部電腦的瀏覽器裏,不會交給評核者。

      為甚麼是兩個 clone,不是兩個 worktree 同一個倉庫的所有 worktree 共用一個 .git:一條分支同一時間只能在一個 worktree 簽出(第二個會被拒絕:'main' is already used by worktree),而且一邊 commit,另一邊立即看得到,不需要也無從 pull。要練習兩地分歧,得用兩個獨立的 clone(兩部電腦,或同一部電腦上的兩個資料夾),經遠端交換(M9)。

      5單人變通版 Solo variant

      知識線的自學者如果真的找不到任何審查者,可以用這個變通版:自己分飾「作者」和「協作者」兩個角色。先搞清楚 GitHub 容許甚麼:

      因此單人版的分工是:git 身份(commit 上的作者名)可以有兩個,GitHub 帳號只有一個。做法:

      # 1. 主 clone:你自己(作者身份)
      git clone https://github.com/你的帳號/notes.git notes
      # 2. 第二個 clone:扮演協作者,只在這個倉庫設定另一個 git 身份
      git clone https://github.com/你的帳號/notes.git notes-collab
      cd notes-collab
      git config user.name  "協作者(單人模擬)"
      git config user.email "collab@example.com"   # 任何不屬於你帳號的電郵都可以
      # 3. 協作者在自己的分支改動,推上去,開 PR
      git switch -c collab/補充第三章
      #    …改動、commit…
      git push -u origin collab/補充第三章
      gh pr create --fill
      # 4. 回到作者身份審查這個 PR:只能留 Comment 類審查
      gh pr review 7 --comment --body "第 12 行的出處請補上頁碼"

      單人版的檢核清單照第 4 節做,以下幾項改變:

      原要求單人版改為
      保護規則:至少一人批准必要審查人數設為 0(否則你永遠不能合併),其餘規則不變:必須 PR、CI 必過、禁止強制推送和刪除、繞過名單清空
      每個 PR 經審查者批准每個 PR 合併前都有一則 Comment 類審查,寫出具體意見;至少一次因審查而再修改後才合併
      外部貢獻(fork 後提交 PR)由第二個 clone 的協作者身份開 PR,至少 2 個;其中一個要被你要求修改
      兩部電腦的分歧不變,就用主 clone 和協作者 clone

      評核時,單人版的「PR 與審查」一項看 Comment 審查的質素;README 要寫明採用單人變通版。單人版沒有真正的他人審查,總等級最高為 B。

      6證據:評核者看甚麼 Evidence

      評核者只看倉庫,不看你的電腦。有些事倉庫本身就記錄了(commit、PR、審查、標籤);有些只發生在你的終端機裏(bisect、worktree、log -S),必須把原始輸出貼進 README 的「過程記錄」一節,或放進 evidence/ 資料夾再從 README 連過去。

      項目倉庫已有的記錄要你補上的證據
      保護規則只有部分(見下)規則設定的輸出或截圖
      CIPR 頁的檢查結果;.github/workflows/最好附一個被 CI 擋下、修正後才通過的 PR 編號
      PR 與審查PR 列表、審查意見—
      合併衝突PR 時間線PR 編號,一句話說明衝突在哪裏、怎樣解決
      rebase -iPR 時間線上的「force-pushed」記錄PR 編號,整理前後的 git log --oneline
      worktree無git worktree list 的輸出
      bisect無git bisect log 的輸出,以及找到的 commit
      log -S無完整命令和輸出
      hook如果把腳本放進倉庫(hooks/)就有被 hook 擋下時的訊息
      救回錯誤revert 會留下 commit;restore 不會反思一節寫清楚經過和命令

      保護規則的設定,有讀取權的人看不全,所以要你提供:

      軟件線另外看個人貢獻。評核者會執行這幾句,你也可以先自己跑一次,確認每人都有份:

      git shortlog -sn main          # 每人在 main 上的 commit 數(squash 合併時改看下一句)
      gh pr list --state merged --limit 100 --json number,author,reviews \
        --jq '.[] | "#\(.number)\t\(.author.login)\t審查:\([.reviews[].author.login] | unique | join(", "))"'

      第二句逐個 PR 列出作者和審查者。每位組員都要有自己開的 PR,也要在別人的 PR 上出現為審查者。README 列出組員與 GitHub 帳號的對照(只寫帳號名,不寫學號、電話等個人資料)。

      7範本 Templates

      以下三份可以直接複製進倉庫再修改。

      PR 範本:.github/pull_request_template.md

      放在這個路徑,開 PR 時 GitHub 會自動填入說明欄。

      ## 改了甚麼
      (一兩句。關聯的 issue:Closes #)
      
      ## 為甚麼
      
      ## 怎樣驗證
      - [ ] 本機跑過測試/預覽過
      - [ ] CI 通過
      
      ## AI 使用
      (沒有使用,或寫明哪些部分由哪個工具產生;commit 已加 Co-authored-by trailer)

      README 的「過程記錄」與「反思」

      ## 組員(軟件線)/ 協作方式(知識線)
      - 組員 A:@帳號 組員 B:@帳號 組員 C:@帳號
      -(單人變通版:寫明採用單人變通版,協作者身份的 git 名稱是 ____)
      
      ## 過程記錄
      ### 保護規則
      設定方式:ruleset/classic;輸出見 evidence/protection.json
      
      ### CI
      被 CI 擋下的 PR:#__,原因:____
      
      ### 合併衝突
      PR #__:衝突在 ____,在分支上用 merge/rebase 解決,選擇的版本是 ____
      
      ### rebase -i(軟件線)
      PR #__:整理前
      (貼 git log --oneline)
      整理後
      (貼 git log --oneline)
      
      ### worktree(軟件線)/兩個 clone 的分歧(知識線)
      (貼 git worktree list 輸出)/(貼 push 被拒的訊息,和解決分歧的 commit)
      
      ### bisect(軟件線)/log -S(知識線)
      (貼 git bisect log 輸出,或 log -S 的命令與輸出)
      找到的 commit:____(來自 PR #__)
      
      ### hook(軟件線)
      腳本:hooks/pre-commit;啟用:git config core.hooksPath hooks
      被擋下時的訊息:(貼上)
      
      ## 反思
      - 一次出錯:發生了甚麼,怎樣發現
      - 怎樣救回來:用了哪個命令(reflog/revert/restore/bisect…),為甚麼選它
      - 下次怎樣避免
      - AI 使用:哪些改動由 AI 產生,你怎樣檢查它(或寫「沒有使用 AI」)

      知識線的連結檢查 CI:.github/workflows/links.yml

      檢查兩種內部連結:Obsidian 的 [[筆記名]],以及 Markdown 的 [文字](相對路徑.md)。找到斷鏈就列出並失敗。設定保護規則時,把 links 這個檢查設為「必須通過」。

      name: links
      on:
        pull_request:
        push:
          branches: [main]
      jobs:
        links:
          runs-on: ubuntu-latest
          steps:
            - uses: actions/checkout@v4
            - name: 檢查內部連結
              shell: python
              run: |
                import pathlib, re, sys
                from urllib.parse import unquote
                notes = {p.stem for p in pathlib.Path('.').rglob('*.md')}
                bad = []
                for p in pathlib.Path('.').rglob('*.md'):
                    text = p.read_text(encoding='utf-8')
                    for name in re.findall(r'\[\[([^\]|#]+)', text):
                        if name.strip().split('/')[-1] not in notes:
                            bad.append(f'{p}: [[{name}]]')
                    for target in re.findall(r'\]\(([^)#\s]+\.md)\)', text):
                        if not (p.parent / unquote(target)).exists():
                            bad.append(f'{p}: ({target})')
                print('\n'.join(bad) or '沒有斷鏈')
                sys.exit(1 if bad else 0)

      它只認筆記檔名(不分資料夾),也不檢查外部網址和圖片;夠用就好,需要時再擴充。軟件線的 CI 則跑你自己的測試,格式參考 M11 CI 一節。

      8使用 AI 的規定 Using AI

      可以讓 AI agent 參與,但照 M13 的做法(一個 agent 一張書桌、追溯):

      沒有照做的後果:漏標,「過程記錄與反思」一項最高只能得 D;評核者可以要求你解釋任何一個 commit 的內容,解釋不了自己提交的改動,整份專題不合格。

      9提交 Submission

      專題經課程平台提交,不另交報告。提交時準備好以下內容:

      1. 倉庫網址。公開倉庫最簡單;私有倉庫要把評核者的 GitHub 帳號加為協作者(帳號在提交入口公布)。倉庫裏不可以有密鑰。
      2. release 頁網址,例如 …/releases/tag/v1.0。
      3. README:組員對照、「過程記錄」和「反思」兩節(範本見第 7 節)。
      4. evidence/protection.json 或保護規則的截圖(見第 6 節)。
      5. 路線:軟件線、知識線,或知識線單人變通版。
      提交入口尚未開放 課程平台的提交功能稍後開放,屆時會在這裏更新。在此之前,先把以上內容放在倉庫裏準備好。

      10評分 Grading rubric

      採用等級制。六個準則各評 A、B、C、D 或不合格,按權重合成總等級。

      準則(權重)ABCD
      正本控制
      15%
      規則齊全、繞過名單是空的;CI 真的擋下過一次問題;release 說明清楚 規則和 CI 齊全,證據完整;release 有說明 規則齊全,但 CI 形同虛設(例如永遠通過)或證據要追問才清楚 規則缺一項(例如沒有禁止刪除),但 main 沒有被直推
      PR 與審查
      25%
      每個 PR 說明目的和驗證方法;審查意見具體(指出位置、提出做法),作者回應並修改;至少一次「要求修改」後才批准 大部分 PR 有說明和有內容的審查意見 有審查,但多數只是「LGTM」式批准;說明簡短 剛好達到最低數量,審查幾乎沒有內容
      歷史品質
      15%
      每個 commit 一件事,說明具體;分支命名一致;rebase -i 前後對比清楚 大致一件事一個 commit,偶有混雜 不少說明籠統(「update」「fix」) 歷史難讀,但 main 沒有被改寫
      工具運用
      20%
      清單上的工具項目全部完成,證據是原始輸出,並說明每一步為何這樣做 全部完成,證據齊全 缺一項,或有一項證據不完整 缺兩項
      倉庫衛生
      10%
      .gitignore 合適;沒有大型二進位檔;陌生人看 README 就能上手 同上,README 略簡 有少量不應入庫的檔案 曾放入大型檔案,已移除並在反思裏說明
      過程記錄與反思
      15%
      過程記錄逐項可核實;反思具體到命令和原因,並提出改進 記錄齊全,反思具體 記錄齊全,反思泛泛 記錄有缺漏,評核者要自行翻查;或 AI 使用漏標

      「工具運用」的項目:軟件線是合併衝突、rebase -i、worktree、bisect、hook;知識線是大型檔案處理、兩個 clone 的分歧、log -S、救回刪錯的筆記。缺三項或以上,該準則不合格。

      不合格情況(需重交)

      個人貢獻(軟件線)

      組員的個人等級原則上等於小組等級,但:

      證據就是第 6 節的 git shortlog -sn 和 PR 列表。分工時就讓每人負責自己的 issue,不要由一個人代為提交。

      完成之後 What next