燒雞

原本是要講出題

但後來好像要來講點 Git 協作技巧

一三學術長

AaW

猜猜看我什麼時候備完這份簡報的

反正比溫室羊出完題目時間快

開始時間: 7/21 14:23

弄完出題: 7/23 11:27

弄完全部: 7/24 13:52

出題

前情提要

為什麼要出題?

三個可能的時間點

  • 課堂練習
  • 社賽/考幹
  • 想噁心人

我們有在資訊社團裡面不錯的資源

aka ISCOJ

但現在上面很多爛題目就是了

比賽

  • 大社賽(建電 Only)
    • 上學期末
    • 通常是考 C
    • 水一點,因為比賽時間不多
    • 最好讓人破台
  • 小社賽(演算法、Python)
    • 也在上學期末
    • 最好都是課內題目的包裝
    • 也最好有人可以破台
  • 學術上機考
    • 下學期末
    • 分成 ABC 難度
    • A 是語法,不用太多
    • B 是上課題
    • C 可以噁心人

可以架設比賽的地方

要出啥

比賽準備的步驟

  • 考前一個月,決定好比賽日期和題數
  • 至少三週前,分配下去題目,每題是誰,主題是什麼,難度是什麼
  • 負責人每兩三天催一次!!!
  • 至少賽前五天要出完開始驗題

怎麼決定要出什麼

怎麼出

出題必要東西

  • 題解
  • 題序
  • 輸入輸出格式
  • Subtask
  • 測資
    • generator
    • ac.cpp
    • pa.cpp

非必要的

必要

  • Validator:
    用來檢查出好的測資是不是爛的,例如超出範圍
  • Checker:測資比對的方式
  • Special Judge

怎麼寫 Gen.cpp

  • jngen | 教學
    • 很棒的東西,可以生出圖、樹等你自己寫來生會很痛苦的東西
    • 要翻進去他 gihub 的 doc/ 資料夾
  • testlib.h
    • 另外一個常見的生成工具

示範:用 jngen 生一顆樹

#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

  • 這東西複雜很多,細節請去看傳承文檔官方說明
  • CMS 的核心概念:比賽內和比賽外是獨立的
    • 什麼意思?
    • 題目要先建立完成之後再勾選進入比賽當中
    • 使用者要先建立完成之後再勾選進入比賽當中

所有的 Task

建立新的題目(Task)

有人幫我寫好了

互動題

題目設定

一般的基本題目可以參考 

B10. 烏鴉倫不要再插旗了 (Flag)

的設定方法

注意事項:測資上傳時,請補齊名稱長度

例如用 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

  • 一筆 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!

參考資料

同場加碼

怎麼極限備課

我不會

反正講的東西比簡報重要

簡報亂做就好👍

Made with Slides.com