燒雞
原本是要講出題
但後來好像要來講點 Git 協作技巧
一三學術長
AaW
猜猜看我什麼時候備完這份簡報的
反正比溫室羊出完題目時間快

開始時間: 7/21 14:23
弄完出題: 7/23 11:27
弄完全部: 7/24 13:52
出題
前情提要
為什麼要出題?
三個可能的時間點
- 課堂練習
- 社賽/考幹
想噁心人
我們有在資訊社團裡面不錯的資源
aka ISCOJ
但現在上面很多爛題目就是了
比賽
- 大社賽(建電 Only)
- 上學期末
- 通常是考 C
- 水一點,因為比賽時間不多
- 最好讓人破台
- 小社賽(演算法、Python)
- 也在上學期末
- 最好都是課內題目的包裝
- 也最好有人可以破台
- 學術上機考
- 下學期末
- 分成 ABC 難度
- A 是語法,不用太多
- B 是上課題
- C 可以噁心人
可以架設比賽的地方
-
Codeforces (有一個大群一五都不給你們加現在加一下 連結)
要出啥
比賽準備的步驟
- 考前一個月,決定好比賽日期和題數
- 至少三週前,分配下去題目,每題是誰,主題是什麼,難度是什麼
- 負責人每兩三天催一次!!!
- 至少賽前五天要出完開始驗題
怎麼決定要出什麼
- 最好從課堂每個人都寫過的開始出
- 可以結合一兩個概念
- 建議從 Codeforce 或是 Atcoder 之類的找適合的題目再去改,不建議自己原創
怎麼出
出題必要東西
- 題解
- 題序
- 輸入輸出格式
- Subtask
- 測資
- generator
- ac.cpp
- pa.cpp
非必要的
必要
- Validator:
用來檢查出好的測資是不是爛的,例如超出範圍 - Checker:測資比對的方式
- Special Judge
怎麼寫 Gen.cpp
示範:用 jngen 生一顆樹
看 tree.md
#include <jngen.h>
#include <bits/stdc++.h>
using namespace std;
#define ll long long
int main(int argc, char *argv[]) {
registerGen(argc, argv);
parseArgs(argc, argv);
ll n = rnd.next(2, std::atoi(argv[1]));
vector<ll> arr(n);
ll maxk = 0;
for (int i = 0; i < n; ++i) {
arr[i] = rnd.next(1ll, (ll)std::atoi(argv[2]));
maxk += arr[i];
}
ll k = rnd.next(1ll, maxk);
cout << n << " " << k << endl;
for (auto &i : arr) cout << i << " \n"[&i == &arr.back()];
}
執行方式
- 個人習慣:把測資用 stdio 輸出(cout) ,再從 shell 把它導到檔案中
- gen.cpp:測資生成器
- ans.cpp:關解
g++ gen.cpp -o gen
g++ ans.cpp -o ans
for i in {01..20}; do
./gen > $i.in
./ans < $i.in > $i.out
done
其他出題方式?
- 使用 TPS
(我原本要講這個但來不及搞懂,可以問蔡孟恆) - 使用 Polygon
來看看 Polygon 使用指北
怎麼放上去
ISCOJ
- 按 Edit 可以直接編輯題序文字、Judge 規則等等
- 要編輯測資需要按 Manage Testdate
- 現在上傳測資支援 zip 檔案上傳了,他規則蠻清楚的,應該很簡單
CMS

所有的 Task
建立新的題目(Task)
題目設定
注意事項:測資上傳時,請補齊名稱長度
例如用 01.in 而不是 1.in
這樣順序才是對的,不然他會按照字典序排
More GIT
一些週一沒提到的細節
禮拜一只講了 git 指令的用法,但並沒有教你們在專案開發時,遇到一些常見的情境,要怎麼處理
- 要怎麼好好開 Branch
- 要怎麼用 Github
- 要怎麼解 Conflict
- 要怎麼選擇 Merge、Rebase 或是 Squash
- 要怎麼解決 origin 和本地端的衝突
Tip:如果可以用圖形化介面來更容易上手,就可以不要用 Cli
GITHUB BASIC
對 GitHUB 有更多認識
要講的東西
- New Repo
- Special Repo
- Issue
- Pull Request
- Github Pages
- 更多分析
懶得做簡報,直接帶畫面
Commit
Commit 可以比想像中複雜
commit 的本質
- 一筆 Commit 是一個快照
- 一個 Branch和HEAD都是指標
- 但是透過 Cherry-Pick、 Rebase 和 Merge 時,git 會先計算出 diff,然後套用在新的 commit
- 也就是說,基於不同的節點,可能會有不同的結果
- https://github.blog/open-source/git/commits-are-snapshots-not-diffs/
要怎麼 Commit
- 一筆 Commit 應該要完成一件事情,且只能完成一件事情
- 如果一次處理了很多事情,應該要拆分之後再 Commit
- Commit Message 要好好寫到可以看出 Why 和 What
- HOW?
- 可參考 Angular 規範,或是 Google 怎麼寫 Commit
- 分成 Header 、Body 和 Footer
- Header 應該要用祈始句,並且可以包含 Type
- 注意長度限制

FLOW
要怎麼好好開 Branch

- Flow 指的是工作流
- 指的是要怎麼開 Branch、每個 Commit 要在哪裡
- Merge 順序要是什麼
- 重點:確保專案開分支是有跡可循的,讓共編更加順暢
- 常見的有兩種:
- Git Flow
- Github Flow
github flow
-
一個輕量且直觀的工作流程
-
master:
主分支始終保持可佈署,並且代表程式碼的最新穩定版本。 -
feature:
每當需要新增功能或修復缺陷時,開發人員就為該改動建立一個功能分支。這些分支會與主分支隔離,直到準備好進行審查。 -
Pull Request:
功能分支上的工作完成後,開發人員發起拉取請求,以便開始程式碼審查與討論。 -
Continuous Deployment:
一旦拉取請求獲得批准,變更就會合併到主分支,並自動部署到生產環境。

Git flow
- 一個非常複雜的 Workflow
-
主分支(master)
主分支僅用於正式發佈,始終保持可佈署。所有經過測試且打好版本標籤(tag)的釋出,都由此分支管理。 -
開發分支(develop)
所有完成 code review 並準備整合的新功能,先合併到 develop 分支。這是下一個釋出的集成分支,僅用於開發期間的自動化測試與驗證。 -
功能分支(feature/*)
每個新功能或需求對應一個 feature 分支,從 develop 分出,開發完成後再合併回 develop。這些分支避免直接影響主線,直到功能成熟。 -
預備發佈分支(release/*)
當 develop 上的功能達到下一個版本標準,就從 develop 切出一個 release 分支。在此分支上只做測試相關修正和文檔更新,完成後同時合併到 master(打 tag)與 develop。 -
緊急修復分支(hotfix/*)
線上若出現重大問題,直接從 master 切出 hotfix 分支修復。修好後,合併到 master(並打 tag)以及 develop,確保修補也在下一個版本中包含。

git flow 優缺點
- 優點:
- 非常具有結構性
- 確保 Main 穩定
- 缺點:
- 特定情況下,會有太多 merge
- Develop Branch 雞肋:只有有測試環境需求的比較需要
- 昨天看到的優化版 Git Flow:
工作流其實反而不是重點
- 其實更常造成問題的不在工作流本身,而是在執行的撤不撤底、管理人有沒有好好花時間解衝突、以及有沒有好好的把東西分工
- 多寫 Unit Test、整合 CI/CD 測試工具,可以避免很多問題
- 要根據團隊特性去決定你們的分工和工作流程
- 善用 Github 的權限設定,比如禁止 force push 之類的
Conflict
其實沒有那麼難解?


Conflict 格式
<<<<<<< Current Change
你在的 Branch 的 Commit
=======
Merge 進來的 Branch 的 Commit
>>>>>>>
直接進行編輯,留下最後的版本就好
編輯完之後呢?
先 git add
再 git merge --continue 或是 git rebase --continue
Merge
Rebase v.s. Merge v.s. squash
都是 Merge 差在哪

Rebase 特色:可以創造線性 History、可以避免線圖太多分岔過於混亂
缺點: Rebase 完會需要 force push,蓋掉一些 commit ,並且讓其他人的亂掉
應該 Rebase 的時機
以下為個人看法,參考就好,沒有標準做法
- 當一個東西我希望他依賴於新的東西的時候
- 線圖是一種展示 Commit 依賴性的工具
- 當兩個 Commit / branch 有前後依賴性:用 rebase
- 兩個獨立:用 merge
- Rebase 最好不要用在很大的 Merge 上,最好只處理一兩個 Commit(Force Push 才不會影響太多)
- 最常用 Rebase 的時機點:pull (等等講)
應該 Squash 的時機
None
Why?
- 因為前面有說過,一個 Commit 應該只包含一件事情,這樣 Commit Message 才有意義
- Commit 是用來追朔和 Blame 程式的
- 如果 Squash merge 了,將會很難回朔出 Bug 在哪裏
Push & Pull
解決 origin 和本地的衝突
細講 PULL
- Origin/a 和 a 要視為不同的 Branch
- Pull 其實是 Fetch 和 Merge 的合成
- Pull 的時候的兩種情況
branchA
origin/branchA
branchA
origin/branchA
branchA
origin/branchA
branchA
origin/branchA
直接 git pull
情況 A:
直接 git pull
情況 B:
直接 git pull --rebase
branchA
origin/branchA
branchA
origin/branchA
PUSH
實際上會經過一次 git merge
如果遠端有 Commit,一定要先 Pull,而且最好是用 rebase Pull
如果在本地進行大規模改寫紀錄,例如 Rebase 之類的,要 force push
盡量不要 force push!
參考資料
同場加碼
怎麼極限備課
我不會
反正講的東西比簡報重要
簡報亂做就好👍
2025 建北電資聯合幹訓 - 燒雞
By Aaron Wu
2025 建北電資聯合幹訓 - 燒雞
出題的簡報顯然要跟實際出題一樣燒雞
- 78